CM4_UART使用参考
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
1.0 | 06/03/2024 |
1. 概述¶
1.1 UART¶
一般指通用异步通讯收发器,通讯特征为异步,串行。UART总线有两条数据线,TX和RX,实现全双工的发送和接收。收发双方通过各自设定的帧格式和波特率解析接收到的信号,因此在通讯过程中要求收发双方使用相同的帧格式和波特率。
1.2 SigmaStar UART¶
符合标准串口通信协议的模块。当中含有多个UART,其中会有FUART、UART的不同称呼,其区别在于FUART相比UART,多了CTS/RTS硬件流控功能。
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资源¶
共提供了1组UART与2组FUART,FUART支持DMA mode & HW CTS/RTS。
各UART/URDMA与bank address对应如下,UART与URDMA唯一绑定,例如PM_FUART和PM_URDMA绑定,PM_FUART1和PM_URDMA1绑定:
UART IP | PM_UART | PM_FUART | PM_FUART1 |
---|---|---|---|
BANK ADDR | 0x35 | 0x36 | 0x52 |
URDMA IP | NA | PM_URDMA | PM_URDMA1 |
---|---|---|---|
BANK ADDR | NA | 0x37 | 0x53 |
3.2 功能支持¶
下表提供各UART对各功能的支持情况
功能 | FIFO mode | FIFO buffer size(byte) | DMA mode | DMA buffer size(byte) | HW CTS/RTS | baudrate | protocol |
---|---|---|---|---|---|---|---|
支持情况 | ✔ | 32 | pm_fuart pm_fuart1 | 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. 使用介绍¶
5.1 RTOS config配置¶
RTOS下使用UART,需要开启CONFIG_CONSOLE_DRIVER_FUART驱动编译选项,这个默认build-in,如下:
proj/mak/common/common_option.mak 445:PP_OPT_COMMON += CONFIG_CONSOLE_DRIVER_FUART
5.2 驱动路径¶
proj/sc/driver/sysdriver/uart/drv/src/drv_uart.c proj/sc/driver/sysdriver/uart/drv/pub/drv_uart.h proj/sc/driver/sysdriver/uart/hal/iford/src/hal_uart.c proj/sc/driver/sysdriver/uart/hal/iford/inc/hal_uart.h proj/sc/driver/sysdriver/uart/hal/iford/inc/hal_uart_cfg.h
5.3 SYSDESC定义¶
<pm_uart> [reg_u32] 0x40006A00; [interrupts_u32] INT_IRQ_PM_UART; [dma_u8] 0; [tx_fifo_level_u8] 0; [rx_fifo_level_u8] 0; [gpio_u16] PAD_PM_UART2_TX; [padmode_u32] PINMUX_FOR_PM_UART0_MODE_3; [rx_pin_u8] PAD_PM_UART2_RX; [status_u8] 1; <pm_fuart> [reg_u32] 0x40006C00, 0x40006E00; [interrupts_u32] INT_IRQ_UART2MCU, INT_IRQ_UART_MERGE, INT_IRQ_UART_MERGE; [dma_u8] 0; [tx_fifo_level_u8] 0; [rx_fifo_level_u8] 0; [rx_pin_u8] 0; [status_u8] 1; <pm_fuart1> [reg_u32] 0x4000A400, 0x4000A600; [interrupts_u32] INT_IRQ_UART12MCU, INT_IRQ_UART1_MERGE, INT_IRQ_UART1_MERGE; [dma_u8] 0; [tx_fifo_level_u8] 0; [rx_fifo_level_u8] 0; [rx_pin_u8] 0; [status_u8] 1;
属性 | 说明 |
备注 |
---|---|---|
<pm_uart> |
用于和驱动匹配 | 禁止修改 |
reg_u32 | IO_address Address_size | 禁止修改 |
interrupts_u32 | 中断号 | 禁止修改 |
dma_u8 | urdma mode使能 | 1 :使能 0 :禁用 |
tx_fifo_level_u8 | 仅在urdma mode禁用情况下有效,用于控制tx中断触发的水位 | 0 : fifo empty1 : 2 characters in the fifo2 : fifo ¼ full3 : fifo ½ full |
rx_fifo_level_u8 | 仅在urdma mode禁用情况下有效,用于控制rx中断触发的水位 | 0 : 1character in the fifo1 : fifo ¼ full2 : fifo ½ full3 : fifo 2 less than full |
rx_pin_u8 | 根据所选择的padmux,将rx对应pin填充在此,在uart初始化期间或波特率修改时,用于禁用rx输入 | 必填,将rx对应pin填充在此 |
sctp_enable_u8 | 流控使能,仅fuart具有此功能 | 省略或0:禁用;1:使能 |
tolerance | 波特率允许误差范围百分比 | 如: =<3> 表示 3% |
status_u8 | 节点使能,用于是否使能该UART | "okay": 使能 "disabled": 不使能 |
5.4 引脚复用¶
即PADMUX,用于将UART信号连接到某组具体的引脚上。设定修改在proj/sc/driver/sysdriver/padmux/hal/iford/src/*-padmux.c当中,以便统一管理。第一列为引脚号定义,第二列为引脚复用的padmod编号,第三列puse为功能定义。需要注意的是,第一列的引脚号不能重复;对应的第二列的padmod编号与引脚号要匹配的上。
proj/sc/driver/sysdriver/padmux/hal/iford/src/iford-ssc029a-s01a-padmux.c pad_info_t schematic[] = { // for example // {PAD_I2C1_SCL, PINMUX_FOR_I2C1_MODE_1, MDRV_PUSE_I2C1_SCL}, // {PAD_I2C1_SDA, PINMUX_FOR_I2C1_MODE_1, MDRV_PUSE_I2C1_SDA}, // for pm_uart {PAD_PM_UART2_TX, PINMUX_FOR_PM_UART0_MODE_3, MDRV_PUSE_PM_UART_TX}, {PAD_PM_UART2_RX, PINMUX_FOR_PM_UART0_MODE_3, MDRV_PUSE_PM_UART_RX}, }; int size_of_schematic = sizeof(schematic);
5.5 测试用例介绍¶
截取部分测试用例如下:(proj/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_name = CliTokenPop(cli); cfg.rate = 115200; cfg.bit_length = 8; cfg.parity = UART_PARITY_NONE; cfg.stop = UART_STOP_1BIT; cfg.rtscts = 1; //使能流控 CamOsPrintf("start test %s...\n", uart_name); read_buf = (u8 *)CamOsMemAlloc(UART_BUF_SIZE); if (read_buf == NULL) { CamOsPrintf("alloc read buf fail.\n"); return eCLI_PARSE_ERROR; } write_buf = (u8 *)CamOsMemAlloc(UART_BUF_SIZE); if (write_buf == NULL) { CamOsPrintf("alloc write buf fail.\n"); CamOsMemRelease((void *)read_buf); return eCLI_PARSE_ERROR; } for (i = 0; i < UART_BUF_SIZE; i++) { *(write_buf + i) = i + 0x40; *(read_buf + i ) = 0; } 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 *)write_buf, UART_BUF_SIZE, 0); 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, -1); 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); CamOsMemRelease((void *)write_buf); CamOsMemRelease((void *)read_buf); return eCLI_PARSE_OK; } SS_RTOS_CLI_CMD(uart, "uart ut test", "this command requires no parameters", uart_ut_test);