RTOS_UART使用参考
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
1.0 | 01/29/2024 |
1. 概述¶
1.1. UART简介¶
一般指通用异步通讯收发器,通讯特征为异步,串行。UART总线有两条数据线,TX和RX,实现全双工的发送和接收。收发双方通过各自设定的帧格式和波特率解析接收到的信号,因此在通讯过程中要求收发双方使用相同的帧格式和波特率。
1.2. SigmaStar UART¶
符合标准串口通信协议的模块。当中含有多个UART,其中会有FUART、UART的不同称呼,其区别在于FUART相比UART,多了CTS/RTS硬件流控功能。一般默认使用UART0作为console port。本文讲述Sigmastar UART驱动在rtos下的使用方法。
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和一组pm_uart,除了pm_uart不支持DMA MODE,其它UART都支持DMA mode,且只有FUART支持HW CTS/RTS。
各UART/URDMA与bank address对应如下,UART与URDMA唯一绑定,例如FUART和URDMA绑定,UART0和URDMA0绑定:
UART IP | FUART | UART0 | UART1 | PM_UART |
---|---|---|---|---|
BANK ADDR | 1102 | 1108 | 1109 | 006A |
URDMA IP | URDMA | URDMA0 | URDMA1 |
---|---|---|---|
BANK ADDR | 1103 | 1107 | 110E |
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%)也不是连续的。
波特率的修改
可以在测试程序当中设定,其设定方法参考下面测试demo图。
4. 硬件连接介绍¶

5. RTOS用法介绍¶
UART通讯测试的时候,需要保证基本的如下步骤:
- 硬件设备连接,外部上拉电阻确认,简单测试方式就是tx和rx短接;
- CONFIG支持UART驱动,默认开启;
- SYSDESC文件配置需求属性;
- PADMUX设定;
- API调用,执行通讯。
5.1 DRIVER PATH¶
sc/driver/sysdriver/uart/drv/pub/drv_uart.h sc/driver/sysdriver/uart/drv/src/drv_uart.c sc/driver/sysdriver/uart/drv/src/drv_uart_test.c sc/driver/sysdriver/uart/drv/src/drv_uart_lite.c sc/driver/sysdriver/uart/hal/iford/src/hal_uart.c sc/driver/sysdriver/uart/hal/iford/inc/hal_uart.h sc/driver/sysdriver/uart/hal/iford/inc/hal_uart_cfg.h
5.2 CONFIG配置¶
UART驱动编译选项在RTOS中默认开启,在RTOS这边需要确认CONSOLE选项,键入make menuconfig进入RTOS配置界面,默认uart0作为console打印串口,如果要更改其它串口打印,可将其更改: #make menuconfig
System Options ---> Console Options ---> <uart0> Console uart port
5.3 SYSDESC配置¶
chipname.sys文件位于sc/driver/sysdriver/sysdesc/hal/chipname/pub
以fuart为例,节点内容如下所示:
<fuart> [reg_u32] 0x1F220400, 0x1F220600; [interrupts_u32] INT_IRQ_FUART, INT_IRQ_FUART_MERGE, INT_IRQ_FUART_MERGE; [camclk_u6] CAMCLK_fuart; [dma_u8] 0; [sctp_enable_u8] 0; [rx_fifo_level_u8] 0; [tx_fifo_level_u8] 0; [digmux_u8] 1; [status_u8] 1;
节点中属性的说明如下:
属性值 | 描述 | 备注 |
---|---|---|
Reg_u32 | 用于指定UART(和URDMA)所在的Bank及范围 | 不需要修改 |
Interrupts_u32 | 指定UART(和URDMA)的中断及触发方式 | 不需要修改 |
Camclk_u16 | 指定UART的时钟源 | 不需要修改 |
Sctp_enable_u8 | 使能FUART的硬件流控 | 需要时写为1 |
Dma_u8 | 使能FUART的DMA功能 | 需要时写为1 |
Sctp_enable | HW CTS/RTS enable | 1: enable 0: disable |
Digmux_u8 | 选择rx tx pin接到哪一组digmux,每一组uart都会默认接到对应的digmux上,若无特殊需求,不需要设置该属性;0xFF代表驱动不会对该uart默认digmux做修改 | 根据需要修改,若无特殊需求,建议删除该属性 |
rx_fifo_level_u8 | 选择uart rx fifo的等级,影响为接收到多少字节触发一次中断,超时也会触发中断,当前fifo深度为32byte; "0" - 1 character in the FIFO; "1" - FIFO ¼ full; "2" - FIFO ½ full; "3" - FIFO 2 less than full |
根据需要修改,若无特殊需求可删除该属性,驱动默认初值为0;仅在dma属性设置为0时有效 |
tx_fifo_level_u8 | 选择uart tx fifo的等级,影响为txfifo剩余多少字节触发一次中断,超时也会触发中断,当前fifo深度为32byte; "0" - FIFO empty; "1" - 2 characters in the FIFO; "2" - FIFO ¼ full; "3" - FIFO ½ full; |
根据需要修改,若无特殊需求可删除该属性,驱动默认初值为0;仅在dma属性设置为0时有效 |
Status_u8 | 选择是否使能UART驱动 | 根据需要修改 |
5.4. PADMUX设定¶
对于UART的padmux,共有三种配置方法,三种情况只会使用一种,当前默认使用sysDesc配置方法。三种如下:
方法一:CONFIG_PADMUX_SUPPORT=Y
该方法通过使能PADMUX驱动,在chipname-padmux.c
文件配置引脚复用功能,该文件位于sc/driver/sysdriver/padmux/hal/chipname/src
,只需要在对应的schematic
属性添加如下内容中设定:
在文件的结构体数组变量schematic当中,依照格式添加对应的pad id、mode、puse
typedef struct { U32 u32PadId; U32 u32Mode; U32 u32Puse; } pad_info_t schematic[] = { {PAD_FUART_TX, PINMUX_FOR_FUART_MODE_1, MDRV_PUSE_FUART_TX}, {PAD_FUART_RX, PINMUX_FOR_FUART_MODE_1, MDRV_PUSE_FUART_RX}, }
第一列为引脚索引号,可以在sc/drivers/sysdriver/gpio/hal/chipname/pub/gpio.h
中查询;
第二列为模式定义,可以在sc/drivers/sysdriver/gpio/hal/chipname/pub/padmux.h
中查询;
第三列为引脚及搭配模式的索引名称,可以在sc/drivers/sysdriver/padmux/drv/pub/drv_puse.h
中查询;
方法二:CONFIG_PADMUX_SUPPORT=N && CONFIG_GPIO_SUPPORT=Y
该方法通过SYSDESC在chipname.sys
文件配置引脚复用功能,该文件位于sc/driver/sysdriver/sysdesc/hal/chipname/pub
,可在对应的UART节点属性padmux_u8
配置padmux.h
定义的模式:
<fuart> [reg_u32] 0x1F220400, 0x1F220600; [interrupts_u32] INT_IRQ_FUART, INT_IRQ_FUART_MERGE, INT_IRQ_FUART_MERGE; [camclk_u6] CAMCLK_fuart; [dma_u8] 0; [sctp_enable_u8] 0; [rx_fifo_level_u8] 0; [tx_fifo_level_u8] 0; [digmux_u8] 1; [padmode_u32] PINMUX_FOR_FUART_MODE_1; [status_u8] 1;
方法三:CONFIG_PADMUX_SUPPORT=N && CONFIG_GPIO_SUPPORT=N
该方法通过SYSDESC在chipname.sys
文件配置引脚复用功能,该文件位于sc/driver/sysdriver/sysdesc/hal/chipname/pub
,可在对应的I2C节点属性padmux_u8
配置引脚模式:
<fuart> [reg_u32] 0x1F220400, 0x1F220600; [interrupts_u32] INT_IRQ_FUART, INT_IRQ_FUART_MERGE, INT_IRQ_FUART_MERGE; [camclk_u6] CAMCLK_fuart; [dma_u8] 0; [sctp_enable_u8] 0; [rx_fifo_level_u8] 0; [tx_fifo_level_u8] 0; [digmux_u8] 1; [padmode_u32] 1; [status_u8] 1;
各UART硬件组padmux罗列
UART BUS | register addr | padmod | PAD | PIN_NAME |
---|---|---|---|---|
FUART | bank 103cH offset 6eH bit[10:8] | 1 | PAD_FUART_TX | FUART0_TX |
PAD_FUART_RX | FUART0_RX | |||
2 | PAD_SD0_D1 | FUART0_TX | ||
PAD_SD0_D0 | FUART0_RX | |||
3 | PAD_PM_FUART_TX | FUART0_TX | ||
PAD_PM_FUART_RX | FUART0_RX | |||
4 | PAD_OUTN_RX0_CH[3] | FUART0_TX | ||
PAD_OUTP_RX0_CH[3] | FUART0_RX | |||
UART0 | bank 103cH offset 6dH bit[1:0] | 1 | PAD_PM_UART_TX | UART0_TX |
PAD_PM_UART_RX | UART0_RX | |||
2 | PAD_GPIO1 | UART0_TX | ||
PAD_GPIO0 | UART0_RX | |||
3 | PAD_PM_UART2_TX | UART0_TX | ||
PAD_PM_UART2_RX | UART0_RX | |||
UART1 | bank 103cH offset 6dH bit[5:4] | 1 | PAD_FUART_CTS | UART1_TX |
PAD_FUART_RTS | UART1_RX | |||
2 | PAD_OUTP_RX0_CH[4] | UART1_TX | ||
PAD_OUTN_RX0_CH[4] | UART1_RX | |||
3 | PAD_PM_UART2_TX | UART1_TX | ||
PAD_PM_UART2_RX | UART1_RX | PM_UART | bank 3fH offset 59H bit[10:8] | 1 | PAD_PM_UART_TX | UART0_TX |
PAD_PM_UART_RX | UART0_RX | |||
2 | PAD_PM_PWM1 | UART0_TX | ||
PAD_PM_PWM0 | UART0_TX | |||
3 | PAD_PM_UART2_TX | UART0_TX | ||
PAD_PM_UART2_RX | UART0_RX | |||
4 | PAD_PM_GPIO12 | UART0_TX | ||
PAD_PM_GPIO11 | UART0_RX |
注意
如果要开启fuart流控功能的话,需要开启sctp_enable,并按同样的方式在padmux中添加流控配置,如下所示:
pad_info_t schematic[] = { {PAD_FUART_RTS, PINMUX_FOR_FUART_MODE_1, MDRV_PUSE_FUART_RTS}, {PAD_FUART_CTS, PINMUX_FOR_FUART_MODE_1, MDRV_PUSE_FUART_CTS}, }
5.5 API及demo说明¶
头文件位于*sc/driver/sysdriver/uart/drv/pub/drv_uart.h*
5.5.1 drv_uart_open¶
-
功能
用于打开uart设备,当中会进行初始化操作
-
语法
uart_handle drv_uart_open(char *name)
-
参数
参数 设备 name uart设备名
uart0: uart0
uart1: uart1
fuart: fuart -
返回值
成功返回控制句柄,表明初始化成功;失败返回NULL。
5.5.2 drv_uart_close¶
-
功能
关闭open的uart
-
语法
void drv_uart_close(uart_handle handle)
-
参数
参数 说明 handle open成功返回的句柄 -
返回值
无
5.5.3 drv_uart_ioctrl¶
-
功能
uart通讯协议设定
-
语法
s32 drv_uart_ioctrl(uart_handle handle, u32 cmd, void *arg)
-
参数
参数 说明 handle uart句柄,open成功获得 cmd 命令,设定协议使用 UART_CMD_CONFIG arg 协议内容 arg参数结构体原型如下:
typedef struct { u8 bit_length; ///< length in number of bits u8 parity; ///< parity u8 stop; ///< stop bit u8 rtscts; u32 rate; ///< bit rate } uart_cfg;
-
返回值
值 说明 0 成功 -1 失败
5.5.4 drv_uart_write¶
-
功能
uart对获取到的handle进行数据发送
-
语法
s32 drv_uart_write(uart_handle handle, u8 *buf, u32 len, s32 timeout_ms)
-
参数
参数 说明 handle uart句柄,open成功获得 buf 发送数据缓存 len 发送字节数 timeout_ms 阻塞等待时长
= 0: 非阻塞
< 0: 阻塞直到完成
> 0: 阻塞设定时长 -
返回值
返回值 说明 0 uart未使能 -1 错误句柄 >0 完成的字节数
5.5.5 drv_uart_read¶
-
功能
uart对获取到的handle进行数据接收
-
语法
s32 drv_uart_read(uart_handle handle, u8 *buf, u32 len, s32 timeout_ms)
-
参数
参数 说明 handle uart句柄,open成功获得 buf 接收数据缓存 len 接收字节数 timeout_ms 阻塞等待时长
= 0: 非阻塞
< 0: 阻塞直到完成
> 0: 阻塞设定时长 -
返回值
返回值 说明 0 uart未使能 -1 错误句柄 >0 完成的字节数
5.5.3 RTOS uart demo¶
demo源码参考 sc/driver/sysdriver/uart/drv/src/drv_uart_test.c
static int uart_ut_test(CLI_t * cli, char * p) { uart_handle handle; uart_cfg cfg = {0}; u8 *read_buf = NULL; u8 *write_buf = NULL; u32 read_len = 0; u32 read_len_left = 0; u32 i = 0; u8 argc; char *uart_name; argc = CliTokenCount(cli); if (argc < 1) return eCLI_PARSE_INPUT_ERROR; //uart test device uart_name = CliTokenPop(cli); cfg.rate = 115200; cfg.bit_length = 8; cfg.parity = UART_PARITY_NONE; cfg.stop = UART_STOP_1BIT; CamOsPrintf("start test %s...\n", uart_name); read_buf = CamOsMemAlloc(UART_BUF_SIZE); if (read_buf == NULL) { CamOsPrintf("alloc read buf fail.\n"); return eCLI_PARSE_ERROR; } write_buf = CamOsMemAlloc(UART_BUF_SIZE); if (write_buf == NULL) { CamOsPrintf("alloc write buf fail.\n"); return eCLI_PARSE_ERROR; } for (i = 0; i < UART_BUF_SIZE; i++) { *(write_buf + i) = i + 0x40; *(read_buf + i ) = 0; } #if 1 handle = drv_uart_open(uart_name); if (!handle) CamOsPrintf("drv_uart_open fail\n"); //set attribute drv_uart_ioctrl(handle, UART_CMD_CONFIG, (void *)&cfg); //uart write drv_uart_write(handle, (u8 *)write_buf, UART_BUF_SIZE, -1); read_len_left = UART_BUF_SIZE; while (read_len < UART_BUF_SIZE) { read_len += drv_uart_read(handle, (read_buf + read_len), read_len_left, 0); read_len_left = UART_BUF_SIZE - read_len; } for (i = 0; i < UART_BUF_SIZE; i++) { if (*(write_buf + i) != *(read_buf + i)) { CamOsPrintf("data check fail index %d.\n", i); break; } } drv_uart_close(handle); #else handle = drv_uart_open(uart_name); if (!handle) CamOsPrintf("drv_uart_open fail\n"); drv_uart_ioctrl(handle, UART_CMD_CONFIG, (void *)&cfg); drv_uart_write(handle, (u8 *)debug_char, 28, -1); do { read_len = drv_uart_read(handle, read_buf, 28, -1); if (read_len >= 0) { read_buf[read_len] = '\0'; CamOsPrintf("%s", read_buf); if (read_buf[0] == 'q') break; } read_len_left += read_len; } while(1); drv_uart_close(handle); #endif return eCLI_PARSE_OK; }
6. DEBUG方法¶
当出现通讯异常时,可以参考如下方面进行问题调试,提供了几种较为常见的排查方向,另调试过程建议抓取波形方便分析。后期会持续更新debug方法
排查方向 | 常见问题 |
备注 |
---|---|---|
padmux | 1. 阻塞模式,卡在循环读上,console卡住点不动;2. 无波形,触发不了 | 参考PADMUX章节,或padmux模块说明 |
通信波特率 | uart处于busy状态,设置不了波特率 | 客户设备一直往UART的RX发送数据 |
时钟源 | 无波形 | 参考CLKGEN模块说明 |
中断 | RX数据丢失 | UART出中断的时候恰好Linux中断被关了(spin_lock_irqsave等)可通过LA抓取波形分析 |