RTOS_UART使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 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-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%)也不是连续的。

    波特率的修改

    可以在测试程序当中设定,其设定方法参考下面测试demo图。

    4. 硬件连接介绍


    图4-1 UART connect


    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抓取波形分析