SSU_UART使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 02/17/2022

    1. 概述

    本文讲述Sigmastar UART驱动使用方法,UART驱动采用标准的Linux框架,能够使用统一的接口操作UART,同时在Linux框架外添加符合Sigmastar IP功能的附加设置。当前平台总共有6组带 URDMA的FUART,其中UART0默认作为console使用。


    2. kernel代码及配置

    2.1. 设备树节点

    UART对应的别名和dtsi节点如下所示,别名可以让我们更方便访问节点。

    1. aliases {
    2.     console = &uart0;
    3.     serial0 = &uart0;
    4.     serial1 = &uart1;
    5.     serial2 = &uart2;
    6.     serial3 = &uart3;
    7.     serial4 = &uart4;
    8.     serial5 = &fuart;
    9.     serial6 = &pm_uart;
    10. };
    11.
    12. uart0: uart0@1F221000 {
    13.     compatible = "sstar,uart";
    14.     reg = <0x1F221000 0x100>, <0x1F220E00 0x100>;
    15.     interrupts= <GIC_SPI INT_IRQ_FUART_0 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_URDMA_0 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_FUART0_EMPTY IRQ_TYPE_LEVEL_HIGH>;
    16.     dma = <1>;
    17.     status = "ok";
    18.     clocks = <&CLK_fuart0>;
    19. };
    20.
    21. fuart: fuart@1F220400 {
    22.     compatible = "sstar,uart";
    23.     reg = <0x1F220400 0x100>, <0x1F220600 0x100>;
    24.     interrupts= <GIC_SPI INT_IRQ_FUART IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_URDMA IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_IRQ_FUART_EMPTY IRQ_TYPE_LEVEL_HIGH>;
    25.     dma = <1>;
    26.     sctp_enable = <0>;
    27.     rx_fifo_level = <0>;
    28.     tx_fifo_level = <0>;
    29.     digmux = <0xFF>;
    30.     tolerance = <3>;
    31.     status = "ok";
    32.     clocks = <&CLK_fuart>;
    33. };
    34.
    35. pm_uart: pm_uart@1F006A00 {
    36.     compatible = "sstar,uart";
    37.     reg = <0x1F006A00 0x100>;
    38.     interrupts= <GIC_SPI INT_IRQ_PM_UART IRQ_TYPE_LEVEL_HIGH>;
    39.     rx_pin = <PAD_PM_GPIO0>;
    40.     status = "ok";
    41.     clocks = <&CLK_pm_uart>;
    

    2.2. 节点属性含义

    DTSI节点各属性含义如下表所示,其中sctp_enable为fuart独有的属性,pm_uart不支持dma属性。

    属性值 描述 备注
    Compatible 用于匹配驱动进行驱动的注册 禁止修改
    Reg 用于指定UART(和URDMA)所在的Bank及范围 不需要修改
    Interrupts 指定UART(和URDMA)的中断及触发方式 不需要修改
    Clocks 指定UART的时钟源 不需要修改
    Sctp_enable 使能FUART的硬件流控 需要时写为1
    Dma 使能FUART的DMA功能 需要时写为1
    Tolerance 设置UART设置波特率可以允许的误差 不能设置为3
    Status 选择是否使能UART驱动 根据需要修改
    Digmux 选择rx tx pin接到哪一组digmux,每一组uart都会默认接到对应的digmux上,若无特殊需求,不需要设置该属性;0xFF代表驱动不会对该uart默认digmux做修改 根据需要修改,若无特殊需求,建议删除该属性
    rx_fifo_level 选择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 选择uart tx fifo的等级,影响为txfifo剩余多少字节触发一次中断,超时也会触发中断,当前fifo深度为32byte;
    "0" - FIFO empty;
    "1" - 2 characters in the FIFO;
    "2" - FIFO ¼ full;
    "3" - FIFO ½ full;
    根据需要修改,若无特殊需求可删除该属性,驱动默认初值为0;仅在dma属性设置为0时有效

    2.3. KERNEL CONFIG

    要将UART驱动编译进kernel中需要在命令行键入make menuconfig进入kernel配置界面,之后打开Serial / UART driver即可,默认是打开的。

    Device Drivers-->
    
        SStar SoC platform drivers-->
    
            [*] SSTAR UART driver
    

    将光标移到Serial / UART driver之后按下空格即可操作:

    *号为将UART driver直接编入kernel。

    2.4. PADMUX设置

    在xxx-padmux.dtsi中配置mode格式如下图的,"<>"中第一个值为PAD值,第二个值为要设置的mode,第三个值为PAD在该mode下对应的功能。根据tmux表中的对应关系配置xxx-padmux.dtsi配置PADMUX,UART0的padmux一般不需要配置,下面是配置FUART padmux的范例。

    1. <PAD_FUART_TX      PINMUX_FOR_FUART_MODE_1     MDRV_PUSE_FUART_TX>,
    2. <PAD_FUART_RX      PINMUX_FOR_FUART_MODE_1     MDRV_PUSE_FUART_RX>,
    3. <PAD_FUART_RTS     PINMUX_FOR_FUART_MODE_1     MDRV_PUSE_FUART_RTS>,
    4. <PAD_FUART_CTS     PINMUX_FOR_FUART_MODE_1     MDRV_PUSE_FUART_CTS>,
    

    3. 波特率

    3.1. 波特率的计算

    波特率是指数据信号对载波的调制速率,它用单位时间内载波调制状态改变的次数来表示,是UART的一个重要的指标。目前的硬件设计UATR实际输出的波特率由输入到UART的Clk source和设置的分频值共同确定。波特率(BAUD)、分频值(DIV)以及输入的CLK频率(CLK)三者的关系如下:

    DIV = CLK / (BAUD × 16)
    

    由于给到UART的CLK rate并不是连续的,根据公式得出UART可以支持的波特率(误差3%)也不是连续的。目前可以支持1M以下的常用波特率,其他的波特率误差需要计算误差是否符合标准。

    3.2. 波特率的修改

    UART的波特率可以通过stty命令修改,以将UART1的波特率修改为115200为例:

    1.  stty -F /dev/sttyS1 ospeed 115200
    

    4. 测试

    4.1. 测试方法

    测试UART功能常用方式是将UART的TX脚和RX脚短接,配置好padmux设置波特率之后使用测试程序进行自发自收测试,量取UART TX脚上的波形解析波特率看是否和设置的相同,并查看测试程序打印接收到的数据是否和发送的数据相同。

    4.2. 测试代码

    1.  #include <stdio.h>
    2.  #include <stdlib.h>
    3.  #include <unistd.h>
    4.  #include <sys/types.h>
    5.  #include <sys/stat.h>
    6.  #include <fcntl.h>
    7.  #include <errno.h>
    8.  #include <sys/time.h>
    9.  #include <time.h>
    10. #include <string.h>
    11. #include <sys/ioctl.h>
    12. #include <termios.h>
    13. #include <stdint.h>
    14. #include <stdio.h>
    15. #include <fcntl.h>
    16. #include <sys/ioctl.h>
    17. #include <linux/spi/spidev.h>
    18. #include <string.h>
    19. #include <assert.h>
    20. #include <netinet/in.h>
    21. #include <sys/types.h>
    22. #include <signal.h>
    23. #include <stdlib.h>
    24. #include <sys/time.h>
    25.
    26. #define msleep(x) usleep(x*1000)
    27.
    28. void my_printf(const char* head_l, unsigned char*date_in, int length_l)
    29. {
    30.     int k;
    31.     printf("%s ", head_l);
    32.     for ( k = 0; k < length_l; k++)
    33.     {
    34.         printf("%02x ", date_in[k]);
    35.     }
    36.     printf("\n");
    37. }
    38.
    39. int select_serial_device(int dev_id, char *name)
    40. {
    41.     switch (dev_id)
    42.         {
    43.         case 1:
    44.             strcpy(name, "/dev/ttyS1");
    45.             break;
    46.
    47.         case 2:
    48.             strcpy(name, "/dev/ttyS2");
    49.             break;
    50.
    51.         case 3:
    52.             strcpy(name, "/dev/ttyS3");
    53.             break;
    54.
    55.         case 4:
    56.             strcpy(name, "/dev/ttyS4");
    57.             break;
    58.
    59.         case 5:
    60.             strcpy(name, "/dev/ttyS5");
    61.             break;
    62.
    63.         case 6:
    64.             strcpy(name, "/dev/ttyS6");
    65.             break;
    66.
    67.         case 7:
    68.             strcpy(name, "/dev/ttyS7");
    69.             break;
    70.
    71.         case 8:
    72.             strcpy(name, "/dev/ttyS8");
    73.             break;
    74.
    75.         case 9:
    76.             strcpy(name, "/dev/ttyS9");
    77.             break;
    78.         case 10:
    79.             strcpy(name, "/dev/ttyS10");
    80.             break;
    81.
    82.         case 11:
    83.             strcpy(name, "/dev/ttyS11");
    84.             break;
    85.
    86.         default:
    87.             return -1;
    88.         }
    89.         return 0;
    90. }
    91. unsigned int choose_baud_rate(int br_id)
    92. {
    93.         unsigned int baud;
    94.         switch (br_id)
    95.         {
    96.             case 0:
    97.                 baud = B0;
    98.                 break;
    99.             case 50:
    100.                 baud = B50;
    101.                 break;
    102.             case 75:
    103.                 baud = B75;
    104.                 break;
    105.             case 110:
    106.                 baud = B110;
    107.                 break;
    108.             case 134:
    109.                 baud = B134;
    110.                 break;
    111.             case 150:
    112.                 baud = B150;
    113.                 break;
    114.             case 200:
    115.                 baud = B200;
    116.                 break;
    117.             case 300:
    118.                 baud = B300;
    119.                 break;
    120.             case 600:
    121.                 baud = B600;
    122.                 break;
    123.             case 1200:
    124.                 baud = B1200;
    125.                 break;
    126.             case 1800:
    127.                 baud = B1800;
    128.                 break;
    129.             case 2400:
    130.                 baud = B2400;
    131.                 break;
    132.             case 9600:
    133.                 baud = B9600;
    134.                 break;
    135.             case 19200:
    136.                 baud = B19200;
    137.                 break;
    138.             case 38400:
    139.                 baud = B38400;
    140.                 break;
    141.             case 57600:
    142.                 baud = B57600;
    143.                 break;
    144.             case 115200:
    145.                 baud = B115200;
    146.                 break;
    147.             case 460800:
    148.                 baud = B460800;
    149.                 break;
    150.             case 921600:
    151.                 baud = B921600;
    152.                 break;
    153.             case 1000000:
    154.                 baud = B1000000;
    155.                 break;
    156.             default:
    157.                 printf("input error: baud rate not correct");
    158.                 return -1;
    159.         }
    160.         return baud;
    161. }
    162. int init_serial_device(char *name, int baud)
    163. {
    164.     int fd;
    165.     int ret;
    166.     struct termios options;
    167.
    168.     fd = open(name, O_RDWR | O_NDELAY | O_NOCTTY);
    169.
    170.     if (fd == -1){
    171.         printf("%s: open error\n", name);
    172.         return -1;
    173.     }
    174.     ret = tcgetattr(fd, &options);
    175.     if (-1 == ret)
    176.         return -1;
    177.
    178.     options.c_cflag &= ~CSIZE;    //屏蔽其他标志
    179.     options.c_cflag |= CS8;        //将数据位修改为8bit
    180.     options.c_cflag &= ~PARENB; //无校验
    181.     options.c_cflag &= ~CSTOPB; // 设置一位停止位;
    182.
    183.     options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    184.     cfsetispeed(&options, baud);
    185.     cfsetospeed(&options, baud);
    186.     options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    187.     ret = tcsetattr(fd, TCSANOW, &options);
    188.     if (-1 == ret)
    189.         return -1;
    190.     return fd;
    191. }
    192.
    193. void process_serial_data(int fd)
    194. {
    195.     char sendbuf[64] = "123456789123456789123456789123456789";
    196.     char recvbuf[64] = "";
    197.     int ret;
    198.
    199.     while (1){
    200.         ret = write(fd, sendbuf,36); //发送数组
    201.         my_printf("send:",sendbuf,ret);
    202.         msleep(100);
    203.
    204.         while(1){
    205.             memset(recvbuf, 0, sizeof(recvbuf));
    206.             ret = read(fd, recvbuf, 255);
    207.             if (ret == 0)
    208.                 continue;
    209.             if(ret>0){
    210.                 my_printf("recv:",recvbuf,ret);
    211.                 break;
    212.             }
    213.         }
    214.         msleep(200);
    215.     }
    216. }
    217.
    218. int main(int argc, char **argv)
    219. {
    220.     int fd;
    221.     int bn;
    222.     int ret;
    223.     char name[12];
    224.     char sn;
    225.     speed_t baud;
    226.
    227.     if (argc < 2){
    228.         printf("input error:*argv=1-11\n");
    229.         exit(-1);
    230.     }
    231.     if (argc > 3){
    232.         printf("input error: less than 4 parameters\n");
    233.         exit(-1);
    234.     }
    235.
    236.     ret = sscanf(argv[1], "%d", &sn);
    237.
    238.     ret = select_serial_device(sn,name);
    239.     if (-1 == ret)
    240.         return -1;
    241.
    242.     ret = sscanf(argv[2], "%d", &bn);
    243.
    244.     baud = choose_baud_rate(bn);
    245.     if (-1 == ret)
    246.         return -1;
    247.
    248.     fd = init_serial_device(name,baud);
    249.     if (-1 == fd)
    250.         return -1;
    251.
    252.     process_serial_data(fd);
    253.
    254.     return 0;
    255. }
    

    5. FUART

    5.1. FUART的特殊性

    FUART相对于普通的UART增加了DMA以及硬件流控,所以理论上fuart是可以支持更大的波特率范围且可以在更高的波特率运行保证数据不丢失,硬件流控仅FUART支持。

    5.2. URDMA

    URDMA的功能是在UART接收到数据之后自动将数据读出放到内存的指定地址中,将我们写入到指定内存地址的数据分多次放入到UART FIFO中由UART TX发出。

    将FUART的dtsi节点中的dma 属性设置为1可以开启FUART的DMA功能。DMA功能可以降低UART驱动的CPU占用率以及支持更高的波特率不丢失数据。

    1. fuart: fuart@1F220400 {
    2.     compatible = "sstar,uart";
    3.     reg = <0x1F220400 0x100>, <0x1F220600 0x100>;
    4.     interrupts = <GIC_SPI INT_IRQ_FUART IRQ_TYPE_LEVEL_HIGH>,
    5.                  <GIC_SPI INT_IRQ_URDMA_TYPE_LEVEL_HIGH>;
    6.     clocks = <&CLK_fuart>;
    7.     sctp_enable = <0>;
    8.     dma = <1>;
    9.     tolerance = <3>;
    10.     status = "ok";
    11. }
    

    5.3. 硬件流控

    硬件流控功能也是一种防止数据丢失的方法,需要将FUART的dtsi节点中的sctp_enable属性设置为1开启。

    1. fuart: fuart@1F220400 {
    2.     compatible = "sstar,uart";
    3.     reg = <0x1F220400 0x100>, <0x1F220600 0x100>;
    4.     interrupts = <GIC_SPI INT_IRQ_FUART IRQ_TYPE_LEVEL_HIGH>,
    5.                  <GIC_SPI INT_IRQ_URDMA_TYPE_LEVEL_HIGH>;
    6.     clocks = <&CLK_fuart>;
    7.     sctp_enable = <1>;
    8.     dma = <0>;
    9.     tolerance = <3>;
    10.     status = "ok";
    11. }
    

    5.4. FUART测试

    开启fuart硬件流控的测试代码如下:

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <unistd.h>
    4. #include <sys/types.h>
    5. #include <sys/stat.h>
    6. #include <fcntl.h>
    7. #include <termios.h>
    8. #include <errno.h>
    9.
    10. int enable_rtscts(int fd)
    11. {
    12.     struct termios options;
    13.     if(tcgetattr(fd, &options) != 0)
    14.     {
    15.         perror("SetupSerial 1");
    16.         return -1;
    17.     }
    18.     options.c_cflag |= CRTSCTS;
    19.     tcflush(fd, TCIFLUSH);
    20.     if(tcsetattr(fd,TCSANOW, &opyions) != 0)
    21.     {
    22.         perror("SetupSerial 3");
    23.         return -1;
    24.     }
    25.     return 0;
    26. }
    27.
    28. int main(int argc, char **argv)
    29. {
    30.     int fd;
    31.     fd = open("/dev/ttyS2",  O_RDWR|O_NOCTTY|O_NDELAY);
    32.     if(fd <= 0)
    33.     {
    34.         printf("Can't Open Serial Port!\n");
    35.         return -1;
    36.     }
    37.     if(enable_rtscts(fd) == -1)
    38.     {
    39.         printf("Set Parity Error\n");
    40.         exit(1);
    41.     }
    42.     printf("ebable rts cts\n");
    43.     close(fd);
    44.     return 0;
    45. }