UART使用参考
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
1.0 | 08/26/2024 |
1. 概述¶
1.1 UART¶
一般指通用异步通讯收发器,通讯特征为异步,串行。UART总线有两条数据线,TX和RX,实现全双工的发送和接收。收发双方通过各自设定的帧格式和波特率解析接收到的信号,因此在通讯过程中要求收发双方使用相同的帧格式和波特率。
1.2 SigmaStar UART¶
符合标准串口通信协议的模块。当中含有多个UART,其中会有FUART、UART的不同称呼,其区别在于FUART相比UART,多了CTS/RTS硬件流控功能。所有UART均支持DMA mode。一般默认使用UART0作为console port。
2. 关键字¶
-
TX
数据发送功能/引脚,按照设定的帧格式和波特率发出UART信号。
-
RX
数据接收功能/引脚,接收到的信号会被UART以设定的帧格式和波特率解析,TX和RX共用同一套设定。
-
CTS
流控引脚/信号,输入信号,解释为“发送允许”。用于判断是否可以向对方发送数据,低电平有效。
-
RTS
流控引脚/信号,输出信号,解释为“发送请求”。用于指示本设备准备好可接收数据,低电平有效。
-
FIFO mode
每一帧数据都需要通过CPU传递给UART硬件发送缓存寄存器,再由UART自行从发送缓存拿走往外发送。硬件发送缓存为32字节。或者接收时CPU从UART硬件接收缓存寄存器读取,硬件接收缓存为32字节。
-
DMA mode
每一帧的数据不再需要CPU逐个下发或读取,只需要在触发通讯之前,将要发送的数据一次性写入DMA指定的存储位置当中,再触发通讯;或者从指定存储当中一次性拿走接收到的所有数据;通讯执行期间,于UART之间的数据交互由URDMA自行完成,无需CPU再参与。 DMA mode可以使得传输更加连贯,减少CPU loading,减少UART通讯中断数量,同时接收发送各提供的4096字节存储空间,能极大减少数据丢失的可能性。
-
URDMA
专门用于为UART提供数据搬运服务的模块,DMA mode时需要启用。启用后,UART不再发生中断,由URDMA发生中断;且DMA enable时,CPU不能再去访问UART寄存器,否则会导致卡死。
-
CONSOLE PORT
console是一个缓冲的概念,专为内核提供打印与busybox接收shell命令使用。PC与Console Port连接,通过PC的终端应用,显示打印信息或输入操作指令。
-
PADMUX
引脚复用,将UART PAD与实际引脚导通,让信号能通过引脚传递。
-
DIGMUX
用于导通UART TX/RX digital message与UART PAD。不同的UART PAD可以接入到不同的UART模块。但是默认作为CONSOLE PORT的PAD_PM_UART_TX与PAD_PM_UART_RX这组PAD无法切换digmux。
例如:当硬件layout固定时,假设原先使用UART1的功能,此时需要HW CTS/RTS的支持,而fuart又没有相应的PADMUX可以切到这组硬件引脚来。则可以通过切换DIGMUX,把UART1与FUART做切换,此时FUART的TX/RX与UART1的TX/RX信号连接互换了,但CTS/RTS则还是原先FUART设定的引脚,满足对HW CTS/RTS的使用。
3. 资源与功能支持¶
3.1 UART资源¶
共提供了2组UART与1组FUART,全部支持DMA mode,但只有FUART支持HW CTS/RTS。
各UART/URDMA与bank address对应如下,UART与URDMA唯一绑定,例如FUART和URDMA绑定,UART0和URDMA0绑定:
UART IP | FUART | UART0 | UART1 |
---|---|---|---|
BANK ADDR | 1102 | 1108 | 1109 |
URDMA IP | URDMA | URDMA0 | URDMA1 |
---|---|---|---|
BANK ADDR | 1103 | 1106 | 1107 |
3.2 功能支持¶
下表提供各UART对各功能的支持情况
功能 | FIFO mode | FIFO buffer size(byte) | DMA mode | DMA buffer size(byte) | HW CTS/RTS | baudrate | protocol |
---|---|---|---|---|---|---|---|
支持情况 | ✔ | 32 | ✔ | 4096 | only fuart | ✔ | ✔ |
波特率支持情况如下表:
UART |
BAUDRATE(bps) |
---|---|
ALL UART | 1200 |
ALL UART | 1800 |
ALL UART | 2400 |
ALL UART | 4800 |
ALL UART | 9600 |
ALL UART | 19200 |
ALL UART | 38400 |
ALL UART | 57600 |
ALL UART | 115200 |
ALL UART | 230400 |
ALL UART | 460800 |
ALL UART | 500000 |
ALL UART | 576000 |
ALL UART | 921600 |
ALL UART | 1000000 |
ALL UART | 1152000 |
ALL UART | 1500000 |
ALL UART | 2000000 |
ALL UART | 2500000 |
ALL UART | 3000000 |
UART通讯协议支持情况如下:
UART |
start bits | char bits | even parity | stop bits |
---|---|---|---|---|
ALL UART | 1 bit | 5 bits;6 bits; 7 bits; 8 bits | Y/N | 1 bit; 1.5 bits |
通讯时序图如下图3-1:

3.3 注意事项¶
-
外部上拉
RX一定要接外部上拉,TX建议接外部上拉。
-
FIFO mode
使用FIFO mode,HW buffer size仅有32byte,当UART无法在buffer被填满之前,及时响应UART中断,进而从HW buffer当中的数据读走,就会出现接收数据丢失的情况。
3.4 波特率的计算¶
波特率是指数据信号对载波的调制速率,它用单位时间内载波调制状态改变的次数来表示,是UART的一个重要的指标。目前的硬件设计UART实际输出的波特率由输入到UART的Clk source和设置的分频值共同确定。波特率(BAUD)、分频值(DIV)以及输入的CLK频率(CLK)三者的关系如下:
DIV = CLK / (BAUD × 16)
由于给到UART的CLK rate并不是连续的,根据公式得出UART可以支持的波特率(误差3%)也不是连续的。
波特率的修改
可以在用户空间的应用程序当中设定。
也可以通过stty命令修改,以将UART1的波特率修改为115200为例:
1. stty -F /dev/sttyS1 ospeed 115200
4. 硬件连接介绍¶

5. Uboot使用介绍¶
5.1 Uboot DM-arch调用框架¶

5.2 config配置¶
uboot下使用UART,需要开启SigmaStar UART驱动编译选项,这个默认build-in,因为console driver也在uart driver里面。另外需要打开CONFIG_DM_SERIAL。如下:
Device Drivers ---> Serial drivers ---> (1) UART used for console [*] Enable Driver Model for serial drivers [ ] Enable RX buffer for serial input [ ] Search for serial devices after default one failed [ ] Probe all available serial devices
只要打开'Enable Driver Model for serial drivers','UART used for console'就会被自动选上。
5.3 驱动路径¶
drivers/sstar/uart/drv_uart.c drivers/sstar/uart/drv_uart.h drivers/sstar/uart/os/os_uart.h drivers/sstar/uart/ifado/hal_uart.c drivers/sstar/uart/ifado/hal_uart.h drivers/sstar/uart/ifado/hal_uart_cfg.h
5.4 DTS定义¶
uart0: uart0@1F221000 { compatible = "sstar,uart"; reg = <0x1F221000 0x100>; group = <0>; char-bits = <8>; stop-bits = <1>; parity-en = <0>; // 0-disable; 1-odd; 2-even. tolerance = <3>; status = "okay"; }; uart1: uart1@1F221200 { compatible = "sstar,uart"; reg = <0x1F221200 0x200>; group = <1>; char-bits = <8>; stop-bits = <1>; parity-en = <0>; // 0-disable; 1-odd; 2-even. tolerance = <3>; status = "okay"; };
属性 | 说明 |
备注 |
---|---|---|
compatible | 用于和驱动匹配 | 禁止修改 |
reg | IO_address Address_size | 禁止修改 |
group | UART组号 | 无需修改 |
rate | 波特率 | 若需要指定,可以在节点中添加属性,或者在使用过程中调用API动态调节 |
char-bits | 数据位 | 若没有这个属性,则默认 8 bits |
stop-bits | 停止位 | 若没有这个属性,则默认 1 bits |
parity-en | 奇偶校验 | 若没有这个属性,默认无校验 0 : no parity 1 : odd parity 2 : even parity |
tolerance | 波特率允许误差百分比 | 数字N代表 N% 若没有这个属性,则默认3% |
status | 节点使能,用于是否使能该UART | "okay": 使能 "disabled": 不使能 |
5.5 引脚复用¶
即PADMUX,用于将UART信号连接到某组具体的引脚上。设定修改在ifado-padmux.dtsi当中,以便统一管理。第一列为引脚号定义,第二列为引脚复用的padmod编号,第三列puse为功能定义。需要注意的是,第一列的引脚号不能重复,若重复则编译检测失败;对应的第二列的padmod编号与引脚号要匹配的上,如果匹配不上,也编译失败。
arch/arm/dts/ifado-padmux.dtsi padmux { compatible = "sstar,padmux"; schematic = ... <PAD_PM_GPIO3 PINMUX_FOR_UART1_MODE_6 MDRV_PUSE_FUART_RX>, <PAD_PM_GPIO7 PINMUX_FOR_FUART_2W_MODE_2 MDRV_PUSE_FUART_TX>, ... };
5.6 软件使用流程¶
DM架构,在启动流程当中,会自动完成device与driver的匹配,若匹配成功,自动执行'.of_to_plat'与'.probe'方法,完成软硬件的初始化与设定。因此在使用时,我们需要做的是:获取uclass device,获取ops方法,调用相对应的函数指针执行操作。
// uclass_id 必须为 UCLASS_SERIAL ret = uclass_get_device_by_seq(UCLASS_SERIAL, seq, &udev); ... // 获取执行方法 ops = serial_get_ops(udev); ... // 设定通信波特率 if (ops && ops->setbrg) ops->setbrg(udev, baud); ... // 发送字符 if (udev && ops && ops->putc) ops->putc(udev, c); .... // 接收字符 if (udev && ops && ops->getc) printf("%x\r\n", ops->getc(udev));
如果对协议格式有需求,可以在ifado.dtsi当中对应的uart节点当中修改属性值。
注意,在要使用对应uart时,它在dtsi文件当中的节点,一定要使能!
需要说明的是,在获取uclass device时,参数'int seq'在这里指代serial number,因为我们在dtsi文件中,定义了aliases,所有uart device与serial number对应注册进管理链表中。
// arch/arm/dts/ifado.dtsi ... aliases { console = &uart0; serial0 = &uart0; };
比如要进行uart1的操作,那uart1就填入'1',这里并没有uart1,可以自行添加serial1 = &uart1,同时在设备树文件中添加uart1的node。
5.7 uboot 测试命令¶
参考文件:
cmd/sstar/uart.c
这里面提供了uart的测试命令,可以在uboot命令行下操作uart,同时也是作为使用范例,可供使用者参考。
命令如下:
SigmaStar # uart init 1 115200 // 获取serial1即uart1的设备 SigmaStar # uart putchar c // 发送字符'c' SigmaStar # uart getchar // 接收字符
6. Kernel使用介绍¶
6.1 kernel调用框架¶

6.2 config配置¶
要将SigmaStar UART驱动编译进kernel中需要在命令行键入make menuconfig进入kernel配置界面,之后打开Serial / UART driver即可,默认是打开的。
Device Drivers--> SStar SoC platform drivers--> [*] SSTAR UART driver
将光标移到Serial / UART driver之后按下空格即可操作:
*
号为将UART driver直接编入kernel。
6.3 驱动路径¶
drivers/sstar/uart/drv/drv_uart.c drivers/sstar/uart/drv/drv_uart_ioctl.h drivers/sstar/uart/hal/ifado/hal_uart.c drivers/sstar/uart/hal/ifado/hal_uart.h
6.4 DTS定义¶
ifado.dtsi中,uart与fuart节点段落如下:
aliases { console = &uart0; serial0 = &uart0; serial1 = &uart1; serial2 = &fuart; }; ...... uart0: uart0@1F221000 { compatible = "sstar,uart"; reg = <0x1F221000 0x100>, <0x1F220C00 0x100>; interrupts= <GIC_SPI INT_IRQ_FUART_0 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_URDMA_0 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_FUART0_EMPTY IRQ_TYPE_LEVEL_HIGH>; dma = <1>; status = "ok"; clocks = <&CLK_fuart0>; }; ...... fuart: fuart@1F220400 { compatible = "sstar,uart"; reg = <0x1F220400 0x100>, <0x1F220600 0x100>; interrupts= <GIC_SPI INT_IRQ_FUART IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_URDMA IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_FUART_EMPTY IRQ_TYPE_LEVEL_HIGH>; dma = <1>; sctp_enable = <0>; rx_fifo_level = <0>; tx_fifo_level = <0>; digmux = <0xFF>; tolerance = <3>; status = "ok"; clocks = <&CLK_fuart>; };
在上面的段落中,aliases定义的uart别名,如serial0最终会注册为/dev/ttyS0,一一绑定,如/dev/ttyS2绑定FUART。
节点中属性的说明如下:
属性 | 说明 |
备注 |
---|---|---|
compatible | 用于匹配驱动注册 | 禁止修改 |
reg | IO_address Address_size | 无需修改 |
interrupts | UART中断号于中断类型说明 | 一个uart有3个中断,顺序为: 1. UART TX/RX中断 2. URDMA TX/RX中断 3. UART 移位寄存器清空的中断 |
dma | 是否使用DMA mode | 1: enable 0: disable |
sctp_enable | HW CTS/RTS enable | 1: enable 0: disable |
rx_fifo_level | 接收中断水位设定 | FIFO mode生效;水位设定如下: 0: 1 character in FIFO 1: ¼ FIFO full 2: ½ FIFO full 3: FIFO 2 less than full |
tx_fifo_level | 发送中断水位设定 | FIFO mode生效;水位设定如下: 0: FIFO empty 1: 2 characters in FIFO 2: ¼ FIFO full 3: ½ FIFO full |
digmux | digmux设定 | 默认0xFF,无需修改 |
tolerance | 波特率允许误差范围百分比 | 如: =<3> 表示 3% |
clocks | 时钟节点 | 禁止修改 |
status | 节点使能,用于是否使能该UART | "ok": enable "disabled": disable |
6.5 PADMUX设置¶
在xxx-padmux.dtsi中配置mode格式如下图的,"<>"中第一个值为PAD值,第二个值为要设置的mode,第三个值为PAD在该mode下对应的功能。根据tmux表中的对应关系配置xxx-padmux.dtsi配置PADMUX,UART0的padmux一般不需要配置,下面是配置FUART padmux的范例。
1. <PAD_FUART_TX PINMUX_FOR_FUART_MODE_1 MDRV_PUSE_FUART_TX>, 2. <PAD_FUART_RX PINMUX_FOR_FUART_MODE_1 MDRV_PUSE_FUART_RX>, 3. <PAD_FUART_RTS PINMUX_FOR_FUART_MODE_1 MDRV_PUSE_FUART_RTS>, 4. <PAD_FUART_CTS PINMUX_FOR_FUART_MODE_1 MDRV_PUSE_FUART_CTS>,
6.6 测试用例介绍¶
用户空间应用程序的使用,基本流程为:
- 打开设备节点
- uart配置
- 读写调用
测试UART功能常用方式是将UART的TX脚和RX脚短接,配置好后使用测试程序进行自发自收测试,量取UART TX脚上的波形解析波特率看是否和设置的相同,并查看测试程序打印接收到的数据是否和发送的数据相同。
截取部分测试用例如下:
int init_serial_device(char *name, int baud) { int fd; int ret; struct termios options; fd = open(name, O_RDWR | O_NDELAY | O_NOCTTY); ret = tcgetattr(fd, &options); if (-1 == ret) return -1; options.c_cflag &= ~CSIZE; //屏蔽其他标志 options.c_cflag |= CS8; //将数据位修改为8bit options.c_cflag &= ~PARENB; //无校验 options.c_cflag &= ~CSTOPB; // 设置一位停止位; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); cfsetispeed(&options, baud); cfsetospeed(&options, baud); options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); ret = tcsetattr(fd, TCSANOW, &options); if (-1 == ret) return -1; return fd; } void *ST_ReadProcess(void *pArgs) { int *fd = (int *)pArgs; int ret = 0; char szBuf[1024]; int len = 0; int i = 0; fd_set read_fds; struct timeval TimeoutVal; while(1) { FD_ZERO(&read_fds); FD_SET(*fd, &read_fds); TimeoutVal.tv_sec = 1; TimeoutVal.tv_usec = 0; ret = select(*fd + 1, &read_fds, NULL, NULL, &TimeoutVal); if(ret < 0) ...... else { if(FD_ISSET(*fd, &read_fds)) { memset(szBuf, 0, sizeof(szBuf)); len = read(*fd, szBuf, sizeof(szBuf) -1); ...... } } } return NULL; }
开启fuart硬件流控的测试代码如下:
int enable_rtscts(int fd) { struct termios options; if(tcgetattr(fd, &options) != 0) { perror("SetupSerial 1"); return -1; } options.c_cflag |= CRTSCTS; tcflush(fd, TCIFLUSH); if(tcsetattr(fd,TCSANOW, &opyions) != 0) { perror("SetupSerial 3"); return -1; } return 0; } int main(int argc, char **argv) { int fd; fd = open("/dev/ttyS2", O_RDWR|O_NOCTTY|O_NDELAY); if(fd <= 0) { printf("Can't Open Serial Port!\n"); return -1; } if(enable_rtscts(fd) == -1) { printf("Set Parity Error\n"); exit(1); } printf("ebable rts cts\n"); close(fd); return 0; }