CM4_UART使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 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-1 UART timing procotol

    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. 硬件连接介绍


    图4-1 UART connect


    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 empty
    1 : 2 characters in the fifo
    2 : fifo ¼ full
    3 : fifo ½ full
    rx_fifo_level_u8 仅在urdma mode禁用情况下有效,用于控制rx中断触发的水位 0 : 1character in the fifo
    1 : fifo ¼ full
    2 : fifo ½ full
    3 : 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);