UART使用参考
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
1.0 | 04/18/2023 | |
1.1 | 04/08/2025 |
1. 概述¶
UART一般指通用异步通讯收发器,通讯特征为异步,串行。UART总线有两条数据线,TX和RX,实现全双工的发送和接收。收发双方通过各自设定的帧格式和波特率解析接收到的信号,因此在通讯过程中要求收发双方使用相同的帧格式和波特率。
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资源¶
PCUPID共提供了6组标准UART、4组FUART(硬件流控的uart模块,支持CTS/RTS,为fuart、uart1、uart2、uart3,可以在dts中配置sctp_enable开启流控功能)和一组PM_UAR(不支持DMA mode)。
各UART/URDMA与bank address对应如下,UART与URDMA唯一绑定,例如FUART和URDMA绑定,UART0和URDMA0绑定:
UART IP | FUART | UART0 | UART1 | UART2 | UART3 | UART4 | UART5 | UART6 | UART7 | UART8 | PM_UART |
---|---|---|---|---|---|---|---|---|---|---|---|
BANK ADDR | 1102 | 1108 | 1109 | 110A | 110B | 110C | 110D | 1140 | 1142 | 1144 | 0035 |
URDMA IP | URDMA | URDMA0 | URDMA1 | URDMA2 | URDMA3 | URDMA4 | URDMA5 | URDMA6 | URDMA7 | URDMA8 |
---|---|---|---|---|---|---|---|---|---|---|
BANK ADDR | 1103 | 1107 | 110E | 110F | 1111 | 1112 | 1113 | 1141 | 1143 | 1145 |
3.2 功能支持¶
下表提供各UART对各功能的支持情况
功能 | FIFO mode | FIFO buffer size(byte) | DMA mode | DMA buffer size(byte) | HW CTS/RTS | baudrate | protocol |
---|---|---|---|---|---|---|---|
支持情况 | ✔ | 32 | ✔(除PM_UART) | 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 |
ALL UART | 3500000 |
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为例:
stty -F /dev/sttyS1 ospeed 115200
4. 硬件连接¶
-
标准UART是指不包含任何硬件流控制机制的UART通信方式。它仅依赖于起始位、数据位、奇偶校验位(可选)和停止位来进行数据帧的界定。只是用TX接对端RX,RX接对端TX。
-
FUART指的是在标准UART的基础上增加了硬件或软件流控制机制,以防止数据丢失并提高通信的可靠性。在标准UART基础上,增加CTX连接对端CTS、CTS连接对端RTS。
图4-1 UART connect
5. Uboot用法介绍¶
5.1. Uboot Config配置¶
在编译Uboot时需要选择的配置如下:
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
5.2 Uboot DTS配置¶
aliases { console = &uart0; serial0 = &uart0; serial1 = &uart1; serial2 = &fuart; serial3 = &uart2; }; 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"; };
UART DTS配置说明:
属性 描述备注 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.3 Uboot cmd参数说明¶
参考文件:
cmd/sstar/uart.c
这里面提供了uart的测试命令,可以在uboot命令行下操作uart,必须先init uart,才能使用putchar发送数据或getchar接收数据。
-
初始化uart:
uart init [port] [baudrate]
参数名称 描述 port uart序号,对应dts的serial*序号 baudrate 波特率 -
发送数据:
uart putchar [char]
参数名称 描述 char 发送的字符串 -
接收数据:
uart getchars size
参数名称 描述 size 获取数据的个数Bytes
5.4 Uboot cmd 使用实例¶
SigmaStar # uart init 2 115200 // 获取serial2即uart2的设备 SigmaStar # uart putchar c // 发送字符'c' SigmaStar # uart getchar // 接收字符
6. Kernel用法介绍¶
6.1. Kernel Config配置¶
要将SigmaStar UART驱动编译进kernel中需要在命令行键入make menuconfig进入kernel配置界面,之后打开Serial / UART driver即可,默认是打开的。
Device Drivers--> SStar SoC platform drivers--> [*] SSTAR UART driver
6.2 DTS定义¶
pcupid.dtsi中,fuart0与fuart节点段落如下:
aliases { console = &fuart0; serial0 = &fuart0; serial1 = &fuart1; serial2 = &fuart2; serial3 = &uart3; serial4 = &uart4; serial5 = &uart5; serial6 = &uart6; serial7 = &uart7; serial8 = &uart8; serial9 = &fuart; serial10 = &pm_uart; }; fuart0: fuart0@1F221000 { compatible = "sstar,uart"; reg = <0x00 0x1F221000 0x00 0x100>, <0x00 0x1F220E00 0x00 0x100>; interrupts= <GIC_SPI INT_IRQ_FUART_0 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_UART0_MERGE IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_UART0_MERGE IRQ_TYPE_LEVEL_HIGH>; //dma-enable; sctp_enable = <0>; rx_fifo_level = <0>; tx_fifo_level = <0>; digmux = <0>; clocks = <&CLK_fuart0>; status = "ok"; }; fuart: fuart@1F220400 { compatible = "sstar,uart"; reg = <0x00 0x1F220400 0x00 0x100>, <0x00 0x1F220600 0x00 0x100>; interrupts= <GIC_SPI INT_IRQ_FUART IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_UART_MERGE IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_UART_MERGE IRQ_TYPE_LEVEL_HIGH>; dma-enable; sctp_enable = <0>; rx_fifo_level = <0>; tx_fifo_level = <0>; digmux = <1>; clocks = <&CLK_fuart>; status = "ok"; };
在上面的段落中,aliases定义的uart别名,如serial0最终会注册为/dev/ttyS0,一一绑定,如/dev/ttyS9绑定FUART。
节点中属性的说明如下:
属性 说明备注 compatible 用于匹配驱动注册 禁止修改 reg IO_address Address_size 无需修改 interrupts UART中断号于中断类型说明 一个uart有3个中断,顺序为: 1. UART TX/RX中断 2. URDMA TX/RX中断 3. UART 移位寄存器清空的中断 dma-enable 是否使用DMA mode 注释表示不开启 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 用于交换不同uart的pad 默认不需要修改,如果uart1的pad给uart2使用,uart2的pad给uart1使用,只需要将uart1与uart2的digmux值互换 clocks 时钟节点 禁止修改 status 节点使能,用于是否使能该UART "ok": enable "disabled": disable 注意: pm_uart没有digmux,所有uart的digmux值不能重复。
6.3 PADMUX配置¶
UART/FUART在Uboot及Kernel环境下的padmux配置方法一致,只需要根据选择的引脚在对应的padmux.dtsi中,UART0的padmux一般不需要配置,下面是配置FUART padmux的范例。
<PAD_GPIOE_11 PINMUX_FOR_FUART_MODE_1 MDRV_PUSE_FUART_TX>, <PAD_GPIOE_10 PINMUX_FOR_FUART_MODE_1 MDRV_PUSE_FUART_RX>, <PAD_GPIOE_13 PINMUX_FOR_FUART_MODE_1 MDRV_PUSE_FUART_RTS>, <PAD_GPIOE_12 PINMUX_FOR_FUART_MODE_1 MDRV_PUSE_FUART_CTS>,
第一列为引脚索引号,可以在/drivers/sstar/inlcude/{chipname}/gpio.h中查到;
第二列为模式定义,在/drivers/sstar/gpio/{chipname}/hal_pinmux.c中m_hal_gpio_st_padmode_info_tbl数组里,罗列了所有引脚的复用关系,查询该引脚支持哪些复用功能可以查询该数组;
第三列为引脚及搭配模式的索引名称,可在/drivers/sstar/include/drv_puse.h里查询。
6.4 模块使用介绍¶
用户空间应用程序的使用,基本流程为:
-
打开设备节点
-
uart配置
-
读写调用
举例:串口连接主机pc,使用串口工具测试发送与接收数据。源码参考6.5.Sample code章节
/customer # echo 987654 > 1.txt /customer # ./uart_ut -D /dev/ttyS1 -B 115200 -p 1.txt
图6-4 UART ut test6.5 Sample code¶
此demo提供参考读写uart操作,可根据需求更改,可配置参数:
属性 说明 -D 用于指定设备节点;如 "-D /dev/ttyS1" -B 用于指定波特率,如"-B 9600" -C 用于开启流控控制功能 -p 用于指定发送的数据 -g 用于指定接收数据保存到文件 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <sys/time.h> #include <time.h> #include <string.h> #include <sys/ioctl.h> #include <termios.h> #include <stdint.h> #include <assert.h> #include <netinet/in.h> #include <signal.h> #include <getopt.h> #define msleep(x) usleep(x * 1000) #define TX_SIZE 64 #define RX_SIZE 256 static char * device = "/dev/ttyS1"; static int options_flag; static speed_t speed; static char * put_file = NULL; static char * get_file = NULL; typedef unsigned int u32; #define TABLE_LEN 31 static const u32 baud_bits[TABLE_LEN] = {B4000000, B3500000, B3000000, B2500000, B2000000, B1500000, B1152000, B1000000, B921600, B576000, B500000, B460800, B230400, B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1800, B1200, B600, B300, B200, B150, B134, B110, B75, B50, B0}; static const u32 baud_table[TABLE_LEN] = {4000000, 3500000, 3000000, 2500000, 2000000, 1500000, 1152000, 1000000, 921600, 576000, 500000, 460800, 230400, 115200, 57600, 38400, 19200, 9600, 4800, 2400, 1800, 1200, 600, 300, 200, 150, 134, 110, 75, 50, 0}; /* Print all baud rates */ void buad_help(void) { int i = 0; int j = 0; printf("support buad: \n"); for (i = 0; i < TABLE_LEN; i++) { j = (i + 1) % 8; if (j == 0) printf("\n"); printf(" %8d ", baud_table[i]); } printf("\n"); } /* ut supports commands */ static void print_usage(const char *prog) { printf("Usage: %s [-DBCpg]\n", prog); puts( " -D --device device to use (default /dev/ttyS1)\n" " -B --baud baud rate (bps)\n" " -C --flow flow Control\n" " -p --put_file put file \n" " -g --get_file get file\n"); exit(1); } /* Get the configured baud rate */ static int get_baud_rate(void) { int i = 0; for (i = 0; i < TABLE_LEN; i++) { if (speed == baud_table[i]) { printf("baud_table[i]: %d\n", baud_table[i]); return baud_bits[i]; } } buad_help(); return -1; } /* Open the uart node and configure uart */ int uart_open(char *name, int baud, int flag, int timeout_ms) { int fd; int ret; struct termios options; fd = open(name, flag); if (fd == -1) { printf("%s: open error\n", name); return -1; } ret = tcgetattr(fd, &options); if (-1 == ret) { return -1; } options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_cflag |= CLOCAL | CREAD; options.c_cflag |= PARENB; options.c_iflag &= ~PARODD; options.c_iflag &= ~INPCK; options.c_cflag &= ~CSTOPB; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); if (!timeout_ms) { options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 0; } else { options.c_cc[VMIN] = 0; options.c_cc[VTIME] = timeout_ms / 100; } ret = cfsetispeed(&options, baud); if (ret) { printf("cfsetispeed err: %d\n", ret); } ret = cfsetospeed(&options, baud); if (ret) { printf("cfsetispeed err:%d \n", ret); } options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); options.c_cflag |= options_flag; tcflush(fd, TCIFLUSH); ret = tcsetattr(fd, TCSANOW, &options); if (-1 == ret) { return -1; } return fd; } /* close */ static int uart_close(int fd) { return close(fd); } /* uart write */ static size_t uart_write(int fd, void *buf, size_t count) { size_t left_count = count; ssize_t real_count = 0; do { real_count = write(fd, buf, left_count); if (0 == real_count) { printf("write timeout !!!\n"); break; } if (0 > real_count) { printf("write fail !!!\n"); break; } buf += real_count; left_count -= real_count; } while (left_count); count -= left_count; return count; } /* uart read */ static size_t uart_read(int fd, void *buf, size_t count) { size_t left_count = count; ssize_t real_count = 0; do { real_count = read(fd, buf, left_count); if (0 == real_count) { break; } if (0 > real_count) { printf("read fail !!!\n"); break; } buf += real_count; left_count -= real_count; } while (left_count); count -= left_count; return count; } /* Parsing parameters */ static void parse_opts(int argc, char *argv[]) { int c; options_flag = 0; while (1) { static const struct option lopts[] = { {"device", 1, 0, 'D'}, {"baud", 1, 0, 'B'}, {"flow", 0, 0, 'C'}, {"put_file", 1, 0, 'p'}, {"get_file", 1, 0, 'g'}, {NULL, 0, 0, 0}, }; c = getopt_long(argc, argv, "D:B:Cp:g:", lopts, NULL); if (c == -1) break; switch (c) { case 'D': device = optarg; break; case 'B': speed = atoi(optarg); break; case 'C': options_flag |= CRTSCTS; break; case 'p': put_file = optarg; break; case 'g': get_file = optarg; break; default: print_usage(argv[0]); } } } int main(int argc, char **argv) { int fd = 0; int ret = 0; u32 baud = 0; long filesize = 0; FILE * stream = NULL; char * write_buffer = NULL; char * read_buffer = NULL; struct stat put_statbuf; char * ack_buf = "uart"; parse_opts(argc, argv); ret = get_baud_rate(); if (-1 == ret) { return -1; } else { baud = ret; } fd = uart_open(device, baud, O_RDWR | O_NOCTTY | O_SYNC, 3000); if (-1 == fd) { return -1; } if (put_file) { stat(put_file, &put_statbuf); filesize = put_statbuf.st_size; printf("put file size : %ld\n", filesize); write_buffer = malloc(filesize); if (!write_buffer) { printf("malloc write_buffer fail !!!\n"); goto out; } stream = fopen(put_file, "r"); if (!stream) { printf("fopen %s fail !!!\n", put_file); goto out; } fread(write_buffer, 1, filesize, stream); ret = uart_write(fd, write_buffer, filesize); if (filesize != ret) { printf("write %s fail !!!\n", put_file); goto out; } memset(write_buffer, 0, filesize); printf("Wait for UartBurnTool transfer binary to SOC\n"); uart_read(fd, write_buffer, strlen(ack_buf)); ret = strcmp(write_buffer, ack_buf); if (!ret) { printf("ack, compare PASS\n"); } else { printf("ng, compare FAIL\n"); } goto out; } if (get_file) { printf("get file test of 32KBytes ... \n"); read_buffer = malloc(0xA000); if (!read_buffer) { printf("malloc read_buffer fail !!!\n"); goto out; } stream = fopen(get_file, "w"); if (!stream) { printf("fopen %s fail !!!\n", get_file); goto out; } printf("Wait for UartBurnTool transfer binary to SOC\n"); filesize = uart_read(fd, read_buffer, 0x8000); while (0 != filesize) { fwrite(read_buffer, 1, filesize, stream); if (filesize == 0x8000) { break; } } printf("\r\n [fwrite done] \r\n"); // uart_write(tty_fd, read_buffer, 0x800); goto out; } out: if (write_buffer) { free(write_buffer); } if (read_buffer) { free(read_buffer); } if (stream) { fclose(stream); } if (fd) { uart_close(fd); } return 0; }
7. FAQ¶
Q1:URDMA-接收数据被分段
-
复现uart2 tx和rx 自环,在soc板子上执行如下命令:
stty -F /dev/ttyS2 speed 9600 -icanon -echo cat /dev/ttyS2 & echo "0123456789101112" > /dev/ttyS2
图7-1 UART rx not continuous原因分析
DMA rx的中断机制:
1、rx buff数据量达到threshold的设定,触发中断并将buff中的数据搬走;
2、rx等待数据量达到threshold的时间超时,也会触发中断将buff中的数据搬走。
分析实验,可以看到,发送的总的数据量不大,远低于预设的0x500 threshold,也就是说,是第二步触发的中断过快,导致一批数据,被分成两段发送到tty层。
解决
由于是rx timeout时间小于波特率码元传输间隔,导致数据分段。因此,不同的波特率下,应该设置不同的rx timeout时间,使得rx timeout value > 码元传输间隔
公式:
cycle_time(时钟单位) = 1/clk_mcu * 1000000000 ns urdma timeout value: = N
则:
t1 = cycle_time * 2 ^ N :rx等待超时时间 t2 = 1000000000 / request_baud * 10(bits) 传输一个码元的时间
通过调整urdma rx timeout 寄存器(设置N),只要满足t1 > t2,即可解决分段问题。
Q2: 接收数据丢失
-
UART 接收到的数据和发送给UART的数据对比会差一部分数据,分为2种情况:1、丢失的数据有规律,只丢失0x11和0x13且0x0A变成了0x0D;2、丢失的数据没有规律。第一种情况只需要在初始化串口时加上options.c_iflag &= ~(ICRNL | IXON);
-
UART RX接收数据丢失的第二种情况原因是UART发出的Received Data Available中断没有被CPU及时处理,有2种情况会导致UART中断没有得到及时的处理:
1、UART出中断的时候恰好有其他的中断正在处理,因为Linux没有中断抢占机制所以UART的中断会延时被处理;
2、UART出中断的时候恰好Linux中断被关了(spin_lock_irqsave等),这样UART的中断也会被延时处理。一般都是第二情况导致的RX接收数据丢失,只有在波特率很高时才会遇到第一种情况引起UART RX丢数据。可以通过在UART中断和Linux中断处理入口处拉选取的GPIO来确定,用LA抓UARTRX和选取的GPIO波形。正常情况的波形如图7-2,可以看到RX脚接收到一个Byte数据就会唤醒一次RX中断将FIFO中数据取走,出错的情况如图7-3,这时候RX中断间隔了16MS才被唤醒一次,我们硬件的RX FIFO长度是32Byte,在典型的115200波特率的情况下不间断接收数据只要2.78ms中断没有被处理就会因为FIFO满而丢数据,所以只要系统中存在超过2.78ms以上的关中断理论上就会引起UART接收丢数据。
图7-2 UART no data loss
图7-3 UART data loss计算方法:
8N1协议的情况下,8bit 数据位,1bit停止位,1bit起始位,无校验位,即通讯1byte数据需要占用10bit的位宽。
波特率-概念:串口通信的速率,单位bps(bits per second),即每秒钟可以传输的二进制位的个数。
比如115200,就是每秒钟可以传输115200各二进制位,也就是115200bit
在115200的波特率下,32byte的fifo深度,可等待时间的计算 :
time = (fifo深度 * 每byte占用的位宽) / 波特率 = (32 * 10) / (115200) S = 2.78ms
解决方法
如果发生丢数据的UART波特率很高,这时候处理其他中断也有可能造成挡到UART RX中断太久导致丢数据,这时候开启urdma才能保证数据不会丢失。
Q3: stty设波特率大于1M失败
-
使用stty命令设置波特率大于1M时设置失败
/ # stty -F /dev/ttyS2 1000000 stty: invalid argument '1000000'
-
说明
busybox的问题,在busybox版本1.26.x之后就可以设置大于1M的波特率。
-
解决方法
如果需要设定大于1M的波特率,可使用C PROGRAM设定。
-