S3C2440-裸机篇-06 | UART数据发送和接收实验(扫描模式)

烈酒焚心 提交于 2020-01-31 08:37:26

关于RS-232C串口总线通信标准请参见我的另一个系列专题文章:
【嵌入式系统通信协议②】EIA RS-232C串口总线标准

实验 —— UART数据收发实验

1. 看原理图确定UART硬件如何连接



由原理图可以看出,JZ2440开发板上将三个串口全部引出,其中UART0设置了板载的USB转串口电路,只需连接板上的USB口就可以,所以接下来我们使用UART0进行数据收发实验。

2. 看芯片手册设置引脚复用功能(GPHCON)、开启片内上拉(GPHUP)

由原理图可以看出,UART0的引脚是:

  • GPH2:TXD0
  • GPH3:RXD0

这两个引脚都是普通的GPIO口,所以需要设置引脚复用功能,作为串口UART0的引脚:

在【【嵌入式系统通信协议②】EIA RS-232C串口总线标准】一文中讲解通信协议的时候讲过,串口的两根信号线在空闲的时候需要保持高电平,所以要开启这两个引脚的片内上拉电阻:

3. 看芯片手册设置串口

3.1.设置串口数据帧格式(ULCONn)

3.2.设置串口(UCONn)

3.2.1.设置串口波特率产生器的时钟源([11:10])

之前在【【S3C2440⑤】S3C2440时钟体系】中进行实验设置了时钟PCLK=50Mhz,所以在此基础上选择PCLK作为串口UART0的波特率发生器的时钟来源:

3.2.2.设置发送/接收数据模式([3:0])

为了简单起见,不使用中断模式和DMA模式,直接采用查询模式(polling mode):

3.3.设置波特率(UBRDIVn)


波特率由UBRDIVn寄存器决定,这个寄存器的值该取多少呢?公式如下:

比如,这里PCLK = 50Mhz,想要设置波特率为115200bit/s

UBRDIVn = (int)(50000000/(115200*16)) - 1
		= (int)(50000000/1843200) - 1
		= (int)(27.13) - 1
		= 27 - 1
		= 26

3.4.数据发送/接收缓存寄存器(UTXHn和URXHn)


其中最重要的是,在使用指针访问这个寄存器的时候,不能使用int型指针,因为int型指针访问的是4个字节的数据,而此处只能访问一个字节数据,所以要使用char型指针

#define UTXH0	(*(volatile unsigned char *)(0x50000020))  //UART 0 transmission hold 
#define URXH0	(*(volatile unsigned char *)(0x50000024))  //UART 0 receive buffer    

3.5.发送/接收状态寄存器

4. 编写代码

4.1.启动文件start.s

和之前相同。

4.2.驱动文件bsp_uart_scan.c/bsp_uart_scan.h

  • bsp_uart_scan.h
#ifndef _BSP_UART_SCAN_H_
#define _BSP_UART_SCAN_H_

void uart0_init();
int putchar(int c);
int getchar(void);
int puts(const char *s);

#endif /* _BSP_UART_SCAN_H_ */
  • bsp_uart_scan.c
/**
 * @ file    bsp_uart_scan.c
 * @ breif   uart0驱动
 * @ note    查询方式
 * @ author  mculover666
 * @ date    2019/3/7
*/
# include "bsp_uart_scan.h"
# include "s3c2440.h"
/**
 * @ brief  串口0初始化
 * @ param  无
 * @ retval 无
 * @ note   115200,8N1
*/

void uart0_init()
{
    /* 初始化uart0使用的引脚 */
    //GPH2-TXD0,GPH3-RXD0
    GPHCON &= ~(3<<(2*2) | (3<<(2*3)));
    GPHCON |= (2<<(2*2)) | (2<<(2*3));

    //开启GPH2、GPH3上拉    
    GPHUP  &= ~((1<<2) | (1<<3));

    /* 设置数据格式: 8N1 */
    ULCON0 = 0x03;
    
    /* 设置串口 */
    // 使用PCLK作为串口时钟源,发送和接收均为查询模式
    UCON0 = 0x0005;

    /* 设置波特率为115200bit/s(PCLK = 50Mhz) */
    //UBRDIVn = (int)(50000000/(115200*16)) - 1 = 26
    UBRDIV0 = 26;
}

/**
 * @ brief  串口发送一个字节的数据
 * @ param  c-要发送的数据
 * @ retval 无
 * @ note   映射到串口0
*/

int putchar(int c)
{
    /* 在发送数据之前检查是否处于发送完成状态 */
    while(!(UTRSTAT0 & 0x06));
    UTXH0 = (unsigned char)c;

    return 0;
}
/**
 * @ brief  串口接收一个字节的数据
 * @ param  无
 * @ retval int
 * @ note   映射到串口0
*/
int getchar(void)
{
    while (!(UTRSTAT0 & (0x01)));
	return URXH0;
}
/**
 * @ brief  串口发送字符串
 * @ param  s
 * @ retval 无
 * @ note   映射到串口0
*/
int puts(const char *s)
{
    while(*s)
    {
        putchar(*s);
        s++;
    }
    return 0;
}

4.3.驱动测试文件main.c

/**
 * @ breif   测试uart0驱动程序:bsp_uart_scan.c
 * @ author  mculover666
 * @ date    2019/3/7
*/
# include "bsp_uart_scan.h"

int main(void)
{
    unsigned char recv_data;
    //初始化uar0:115200,8N1
    uart0_init();

    //测试发送字符串
    puts("Hello,World.I am mculover666.\r\n");

    while(1)
    {
        recv_data = getchar();
        putchar(recv_data);
    }
}

5. 编译代码

使用makefile构建编译,在之前的基础上进行修改,如下:

TARGET = uart

CFLAGS = -Wall	#输出所有warning

$(TARGET).bin:$(TARGET).elf
	arm-linux-objcopy -O binary -S $(TARGET).elf $(TARGET).bin
	
#注意:启动文件必须第一个链接
$(TARGET).elf:start.o bsp_uart_scan.o main.o
	arm-linux-ld -Ttext 0 start.o bsp_uart_scan.o main.o -o $(TARGET).elf
	
start.o:start.s
	arm-linux-gcc -c start.s $(CFLAGS) -o start.o
bsp_uart_scan.o:bsp_uart_scan.c
	arm-linux-gcc -c bsp_uart_scan.c $(CFLAGS) -o bsp_uart_scan.o
main.o:main.c
	arm-linux-gcc -c main.c $(CFLAGS) -o main.o	
	
clean:
	rm -rf *.o *.elf *.bin

download_to_nand:
	#下载到nand flash
	oflash 0 1 0 0 0 $(TARGET).bin 

6. 下载运行

这里使用SerialPort Utility软件进行测试:

7.实验总结

历经三天,终于完成了本实验,通过该实验:

  • 嵌入式系统的角度来说:掌握了RS-232C串口总线通信标准,包括其物理层和协议层,针对现在的情况主要使用其改进版,它的协议简单,在嵌入式系统中被大量使用,但是也有缺点,就是传输距离短,在15m左右;
  • S3C2440这颗芯片来说:掌握了其UART设备的使用。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!