2.3. UART _SPEC

AC792N有5组UART控制器,可独立设置波特率、校验位(常0/常1/奇/偶 校验)、CTS/RTS流控(仅UART1和UART2支持),支持9Bit模式,支持智能卡模式。支持Crossbar功能,可将TX/RX脚映射到除PF、PR、PV口以外的任意IO。本文为spec_uart驱动说明,而spec_uart则是基于device这一套接口实现,因此开发者需要通过device API接口对UART进行操作。本文提供 UART 应用示例、配置介绍、串口控制流的使用介绍和注意事项说明。
注:串口打印使用另一套专门的驱动,请参考uart_log相关文档,不要与本文所述内容混淆。

示例工程:可在demo_hello工程中的apps/demo/demo_hello/include/app_config.h中打开USE_UART_TEST_DEMO宏测试。

例程路径:apps/common/example/peripheral/uart/uart_test.c

AC792N UART驱动使用步骤:

  1. 配置UART。配置uart_platform_data结构体,设置UART参数,如波特率、奇偶校验位等。UART设备打开时会根据该配置进行初始化。

  2. 注册UART设备。

  3. 打开设备。通过提供UART设备名,SDK会找到步骤2注册的设备配置信息,来对UART进行初始化。

  4. 操作设备。使用dev_write()dev_read()dev_ioctl()等对设备进行操作。

  5. 关闭设备。

2.3.1. UART参数配置

1.配置结构体

  • uart_platform_data结构体

struct uart_platform_data {
    u8  irq;                              ///< 中断号,在注册的时候已经对应设置好了
    u8  flow_ctl_enable;                  ///< 1:使能串口流控
    u8  rts_hw_enable;                    ///< 1:使能硬件RTS
    u8  rts_idle_level;                   ///< 0:RTS空闲电平为低; 1:RTS空闲电平为高
    u8  cts_idle_level;                   ///< 0:CTS空闲电平为低; 1:CTS空闲电平为高
    u8  rx_thresh;                        ///< 接收BUF触发流控的比值0-100
    u8  disable_tx_irq;                   ///< 1:不使用发送中断
    u8  disable_rx_irq;                   ///< 1:不使用中断接收
    u8  disable_ot_irq;                   ///< 1:不使用超时中断

    int tx_pin;                           ///< 发送引脚,不配置需设置-1
    int rx_pin;                           ///< 接收引脚,不配置需设置-1
    int rts_pin;                          ///< 串口控制流RTS脚,不配置需设置-1
    int cts_pin;                          ///< 串口控制流CTS脚,不配置需设置-1
    int flags;                            ///< 串口用作打印
    u32 baudrate;                         ///< 波特率设置。理论范围:CLK/3 ~ CLK/4/(0xFFFF+1) -> CLK/3 ~ CLK/262144

    u32 max_continue_recv_cnt;            ///< 连续接收最大字节,范围:0 ~ 0xFFFFFFFF */
    u32 idle_sys_clk_cnt;                 ///< 超时计数器。在指定时间里没有收到数据,则产生超时中断,范围:0 ~ 0xFFFFFFFF
    UART_CLK_SRC clk_src;                 ///< 选择时钟源。可选:STD_48M、STD_24M、EXT_CLK、LSB_CLK
    UART_PARITY parity;                   ///< 奇偶校验位。可选:UART_PARITY_DISABLE、UART_PARITY_EVEN、UART_PARITY_ODD
    GPIO_DRIVE_STRENGTH tx_pin_hd_level;  ///< tx io开强驱等级。可选GPIO_DRIVE_STRENGTH_1p8mA/6p0mA/20p0mA/24p0mA
};
  • 配置示例1:常规UART收发

UART2_PLATFORM_DATA_BEGIN(uart2_data)
    .flow_ctl_enable        = 0,         ///< 关闭流控
    .baudrate               = 115200,
    .tx_pin                 = IO_PORTA_04,
    .rx_pin                 = IO_PORTA_05,
    .tx_pin_hd_level        = GPIO_DRIVE_STRENGTH_6p0mA,
    .max_continue_recv_cnt  = 1024,
    .idle_sys_clk_cnt       = 500000,
    .clk_src                = STD_24M,
    .parity                 = UART_PARITY_DISABLE,
UART2_PLATFORM_DATA_END();
  • 配置示例2:开启流控的UART收发

UART1_PLATFORM_DATA_BEGIN(uart1_data)
    .flow_ctl_enable        = 1,         ///< 开启流控
    .rts_hw_enable          = 1,         ///< 使能硬件流控
    .rts_pin                = IO_PORTA_01,
    .cts_pin                = IO_PORTA_02,
    .rts_idle_level         = 0,         ///< RTS空闲时低电平
    .cts_idle_level         = 0,         ///< CTS空闲时低电平
//    .rx_thresh              = 80,        ///< 如果rts_hw_enable为0,即软件流控,需要配置该参数

    .baudrate               = 115200,
    .tx_pin                 = IO_PORTA_06,
    .rx_pin                 = IO_PORTA_07,
    .tx_pin_hd_level        = GPIO_DRIVE_STRENGTH_6p0mA,
    .max_continue_recv_cnt  = 1024,
    .idle_sys_clk_cnt       = 500000,
    .clk_src                = STD_24M,
    .parity                 = UART_PARITY_DISABLE,
UART1_PLATFORM_DATA_END();

2.注册设备

REGISTER_DEVICES(device_table) = {    };中添加注册项。

REGISTER_DEVICES(device_table) = {
        {"uart2", &uart_dev_ops, (void *)&uart2_data },
};

2.3.2. UART device操作

1.打开设备

操作:使用dev_open()打开设备。如果open设备时,传入了uart_platform_data类型的配置参数,那么UART将按照传入参数的配置进行初始化。如果传入的参数是NULL,则按照Board中注册的配置进行初始化。

参数:arg0-设备名;arg1-uart_palform_data类型变量的地址。

返回值:打开的设备的句柄。后续其他操作可通过该句柄对该设备进行操作。

注意:如果UART设备被dev_open()打开多次,那么设备只在第一次打开时执行初始化。

struct uart_palform_data uart_pfdata;                  ///< 自定义配置
void *hdl = dev_open("uart1", (u32)&uart_pfdata);      ///< 传入配置参数,则按传入的参数初始化UART
void *hdl = dev_open("uart1", NULL);                   ///< 传入NULL,则按Board中注册的配置初始化UART

2.关闭设备

操作:使用dev_close()关闭设备。

参数:UART设备open时获得的句柄。

返回值:固定为0。

注意:如果设备被dev_open()打开n次,关闭时则需要执行dev_close()n次才能关闭。

dev_close(hdl);

3.接收数据

操作:使用dev_read(hdl, buf, cnt)读取UART收到的数据。该接口将会把UART接收到的数据放入指定的buf中,放入最多cnt个数据(如果未配置UART_SET_RECV_ALL,RX只接收到n个数据,且n < cnt,那么dev_read()将只读回n个数据)。

参数:arg0-设备句柄;arg1-接收数据的buf地址;arg2-一般为buf长度。

返回值:正常为放入指定buf中的数据个数。接收数据异常时会返回异常原因代号UART_CIRCULAR_BUFFER_WRITE_OVERLAY(-1)、UART_RECV_TIMEOUT(-2)、UART_RECV_EXIT(-3),分别对应内部循环buf写满出现覆盖接收数据超时(执行超时回调后)接收终止退出

u8 recv_buff[64];
dev_read(hdl, recv_buf, sizeof(recv_buf);

4.发送数据

操作:使用dev_write(hdl, buf, cnt)发送数据。该接口会将指定 buf中的cnt个数据发送出去。

参数:arg0-设备句柄;arg1-发送的数据的buf地址;arg2-发送数据数量。

返回值:发送的数据量,即传入的agr2参数。bufNULL或者cnt0时,返回值为0。

注意:如果设置了非阻塞发送,则函数会等待上一次数据发送完成后,才开始发送新数据。

u8 send_buf[] = {0x00, 0x11, 0x22, 0x33};
dev_write(hdl, send_buf, sizeof(send_buf));

2.3.3. UART IOCTL命令

ioctl函数原型:

/**
 * #brief dev_ioctl:用于对设备进行控制和参数的修改
 * @param device 设备句柄
 * @param cmd 设备控制命令
 * @param arg 控制参数
 * @return 0:正常执行完成  -EINVAL(-22):找不到该命令
 */
int dev_ioctl(void *device, int cmd, u32 arg);

串口设备的IOCTL命令:

#define UART_MAGIC                     'U'
#define UART_FLUSH                     _IO(UART_MAGIC,1)                             ///< 串口重载
#define UART_SET_SEND_BLOCK            _IOW(UART_MAGIC,2,bool)                       ///< 设置串口发送阻塞
#define UART_SET_RECV_BLOCK            _IOW(UART_MAGIC,3,bool)                       ///< 设置串口接收阻塞
#define UART_SET_RECV_ALL              _IOW(UART_MAGIC,4,bool)                       ///< 设置串口等待接收满才退出(但需先使用上一条命令配置UART为阻塞接收)
#define UART_SET_RECV_TIMEOUT          _IOW(UART_MAGIC,5,u32)                        ///< 设置串口接收超时
#define UART_SET_RECV_TIMEOUT_CB       _IOW(UART_MAGIC,6,int (*)(void))              ///< 设置串口超时之后的回调函数
#define UART_GET_RECV_CNT              _IOR(UART_MAGIC,7,u32 *)                      ///< 获取串口循环buf中已接收到未被取走的数据的累计值
#define UART_START                     _IO(UART_MAGIC,8)                             ///< 开启串口
#define UART_STOP                      _IO(UART_MAGIC,9)                             ///< 暂停串口
#define UART_SET_CIRCULAR_BUFF_ADDR    _IOW(UART_MAGIC,10,u32)                       ///< 设置串口循环buf地址
#define UART_SET_CIRCULAR_BUFF_LENTH   _IOW(UART_MAGIC,11,u32)                       ///< 设置串口循环buf长度
#define UART_SET_BAUDRATE              _IOW(UART_MAGIC,12,u32)                       ///< 串口运行过程中修改波特率
#define UART_FLOW_CTL_RTS_SUSPEND      _IO(UART_MAGIC,13)                            ///< 串口控制流挂起
#define UART_FLOW_CTL_RTS_RESUME       _IO(UART_MAGIC,14)                            ///< 串口控制流恢复
#define UART_SET_IRQ_EVENT_CB          _IOW(UART_MAGIC,15,void (*)(UART_IRQ_EVENT))  ///< 设置中断事件回调函数

1.UART_FLUSH

功能:串口重载。函数会清除UART的pending、循环buf数据,恢复初始启动状态。

参数:无作用。

dev_ioctl(hdl, UART_FLUSH, 0);

2.UART_SET_SEND_BLOCK

功能:设置串口是否以阻塞的方式发送数据。使能时,dev_write()函数中会调用os_sem_pend()阻塞线程。

参数: 0 - 关闭;1 - 使能。模块默认初始化为1。

dev_ioctl(hdl, UART_SET_SEND_BLOCK, 0);///< 关闭
dev_ioctl(hdl, UART_SET_SEND_BLOCK, 1);///< 使能

3.UART_SET_RECV_BLOCK

功能:设置串口是否以阻塞的方式接收数据。使能时,dev_read()函数中会调用os_sem_pend()阻塞线程。如果未使用UART_SET_RECV_TIMEOUT命令设置timeout时间,则默认没有超时限制,那么dev_read()会死等接收到数据后才退出。

参数: 0 - 关闭;1 - 使能。默认为0。

dev_ioctl(hdl, UART_SET_RECV_BLOCK, 0);///< 关闭
dev_ioctl(hdl, UART_SET_RECV_BLOCK, 1);///< 使能

4.UART_SET_RECV_ALL

功能:设置串口是否等待接收满才退出。比如,我们提供一个10 Byte长度的bufdev_read(hdl, buf, 10)用于读取uart接收到的数据,而串口只收到5 Byte数据,那么dev_read()函数将只返回5 Byte数据;而如果使能了UART_SET_RECV_ALLdev_read()将继续等待,直到接收到完整10 Byte数据后才返回。

参数: 0 - 关闭;1 - 使能。

注意:该命令需要先使能了UART_SET_RECV_BLOCK才有效。

dev_ioctl(hdl, UART_SET_RECV_ALL, 0);///< 关闭
dev_ioctl(hdl, UART_SET_RECV_ALL, 1);///< 使能

5.UART_SET_RECV_TIMEOUT

功能:设置串口接收超时时间。

参数:超时时间,单位ms,可设置范围0~0xFFFFFFFF。若设置为0,则没有超时限制。

注意:该命令需要配置UART_SET_RECV_BLOCK才有效,而且与UART_SET_RECV_BLOCK没有设置顺序要求。

dev_ioctl(hdl, UART_SET_RECV_TIMEOUT, 1000);

6.UART_SET_RECV_TIMEOUT_CB

功能:设置串口接收超时回调函数,dev_read()读取数据出现超时时会调用该函数。

参数:超时回调函数地址。回调函数参数应为void类型,返回值为int类型。

注意:超时回调函数在dev_read()中执行,回调函数返回值会影响dev_read()的返回值:超时回调函数返回非0值,则dev_read()返回UART_RECV_EXIT(-3),否则返回UART_RECV_TIMEOUT(-2)

int timeout_cb(void)
{
    printf("I am timeout callback\n");
    return 1;
}
dev_ioctl(hdl, UART_SET_RECV_TIMEOUT_CB, (u32)&timeout_cb);

7.UART_GET_RECV_CNT

功能:获取串口循环buf中已接收到未被取走的数据的累计值。

参数:存放计数值的地址。

u32 recv_cnt = 0;
dev_ioctl(hdl, UART_GET_RECV_CNT, (u32)&recv_cnt);
printf("UART receive data count:%u\n", recv_cnt);

8.UART_START

功能:开启串口。将执行:a.打开UART 模块RX开关。b.如果使能流控并配置了RTS,该命令还会设置RTS为idle状态。

参数:无作用。

dev_ioctl(hdl, UART_START, 0);

9.UART_STOP

功能:串口停止。该命令将等到模块空闲后执行:a.关闭UART模块TX、RX开关;b.如果使能流控并配置了RTS,该命令还会设置RTS为busy状态;c.执行flush操作;d.清除所有pnd。

参数:无作用。

dev_ioctl(hdl, UART_STOP, 0);

10.UART_SET_CIRCULAR_BUFF_ADDR

功能:设置串口循环buf地址。

参数:循环buf地址。

注意:a.循环buf地址空间必须要4字节对齐;b.驱动接收使用了dma,因此uart使用前须配置循环buf空间,否则会出现异常复位;c.该命令须与UART_SET_CIRCULAR_BUFF_LENTH命令配合使用,只配置buf地址,不配置buf长度的话,当uart模块收到数据时会报错 :RP UART0_CIRCULAR_BUFFER_WRITE_OVERLAY

u8 recv_buf[64] __attribute__((aligned(4)));
dev_ioctl(hdl, UART_SET_CIRCULAR_BUFF_ADDR, (u32)recv_buf);

11.UART_SET_CIRCULAR_BUFF_LENTH

功能:设置串口循环buf长度。

参数:循环buf长度。

注意:a.该命令须与UART_SET_CIRCULAR_BUFF_ADDR命令配合使用。

dev_ioctl(hdl, UART_SET_CIRCULAR_BUFF_LENTH, sizeof(recv_buf));

12.UART_SET_BAUDRATE

功能:修改串口波特率。dev_open("uart2", NULL)后,uart波特率会初始化为board.c文件中配置值。该命令可在dev_open()串口后修改波特率。

参数:要设置的波特率。

注意:使用该命令修改波特率时,驱动会暂时关闭串口收发功能,清pending,并清空循环buf数据,使串口恢复初始状态。建议修改波特率前将内部循环buf的数据读取完后再执行该命令。

dev_ioctl(hdl, UART_SET_BAUDRATE, 115200);

13.UART_FLOW_CTL_RTS_SUSPEND

功能:串口控制流挂起。串口关闭数据接收,并控制RTS脚,指示本设备未准备好,发送方不要发送数据。

参数:无作用。

注意:AC792N仅UART1UART2支持硬件流控,该命令仅对这两个串口模块有效。

dev_ioctl(hdl, UART1_FLOW_CTL_RTS_SUSPEND, 0);

14.UART_FLOW_CTL_RTS_RESUME

功能:串口控制流恢复。串口恢复数据接收,并控制RTS脚,指示本设备准备好了,发送方可以发送数据。

参数:无作用。

注意:AC792N仅UART1UART2支持硬件流控,该命令仅对这两个串口模块有效。

dev_ioctl(hdl, UART1_FLOW_CTL_RTS_RESUME, 0);

15.UART_SET_IRQ_EVENT_CB

功能:设置中断事件回调函数。

参数:中断事件回调函数地址。回调函数参数应为UART_IRQ_EVENT类型,返回值为void类型。

注意:该回调函数在中断中执行,回调函数程序应简单快速。

void uart_irq_cb(UART_IRQ_EVENT event)
{
     if(event == UART_IRQ_EVENT_TX)printf("I am UART_IRQ_EVENT_TX\n");
     else if(event == UART_IRQ_EVENT_RX)printf("I am UART_IRQ_EVENT_RX\n");
     else printf("I am %d\n",event);
}
dev_ioctl(hdl, UART_SET_IRQ_EVENT_CB , (u32)&uart_irq_cb);
typedef enum uart_irq_event {
     UART_IRQ_EVENT_NULL,      ///< 0.无*/
     UART_IRQ_EVENT_TX,        ///< 1.TX pnd*/
     UART_IRQ_EVENT_RX,        ///< 2.RX pnd,当RX DMA连续收到的数据超过RXCNT的设定值时*/
     UART_IRQ_EVENT_OT,        ///< 3.OverTime pnd,当RX DMA收到一包数据后经过OTCNT个CLK后未再收到新数据时*/
     UART_IRQ_EVENT_CTS,       ///< 4.CTS pnd*/
     UART_IRQ_EVENT_CHK,       ///< 5.CHK pnd*/
} UART_IRQ_EVENT;

2.3.4. 其它说明

  • 如果希望直接操作寄存器,自行实现程序逻辑,可参考例程: apps/common/example/peripheral/uart/uart_reg_operation_test.c

  • 串口智能卡模式的使用,可参考例程: apps/common/example/peripheral/uart/uart_smartcard_iso7816_test.c

  • UART接收,需要先用IOCTL的UART_SET_CIRCULAR_BUFF_ADDRUART_SET_CIRCULAR_BUFF_LENTH命令配置循环buf空间。因为SDK的UART驱动,使用DMA进行收/发数据,未设置BUF的话,系统将因dma访问地址错误而复位。

  • UART引脚没有固定IO口,可在任意IO上使用crossbar映射出来,驱动内部已实现。

  • 如果不需要用到 tx_pin 或者 rx_pin,需要将其赋值为-1。

  • UART停止位固定为1位。

  • UART的dev_write()函数内部会判断上次发送是否完成,未完成则程序会while等待。

  • 非阻塞方式dev_write()发送数据后,如果有需要对uart的配置进行修改,建议先用dev_write(hd1,NULL,0)等待数据发送完成后。

  • max_continue_recv_cnt为dma连续接收一次最大数量,(在开启硬件流控的情况下)RX接收到数据一旦等于该值,RTS脚状态将变为busy。

2.3.5. API参考

UART dev_ioctl funciton selest

UART_MAGIC
IOCTL_UART_FLUSH

串口重载

IOCTL_UART_SET_SEND_BLOCK

设置串口发送阻塞

IOCTL_UART_SET_RECV_BLOCK

设置串口接收阻塞

IOCTL_UART_SET_RECV_ALL

设置串口等待接收满才退出(需先使用上一条命令配置UART为阻塞接收)

IOCTL_UART_SET_RECV_TIMEOUT

设置串口接收超时

IOCTL_UART_SET_RECV_TIMEOUT_CB

设置串口超时之后的回调函数

IOCTL_UART_GET_RECV_CNT

获取串口循环buf中已接收到未被取走的数据的累计值

IOCTL_UART_START

开启串口

IOCTL_UART_STOP

暂停串口

IOCTL_UART_SET_CIRCULAR_BUFF_ADDR

设置串口循环buf地址

IOCTL_UART_SET_CIRCULAR_BUFF_LENTH

设置串口循环buf长度

IOCTL_UART_SET_BAUDRATE

串口运行过程中修改波特率

IOCTL_UART_FLOW_CTL_RTS_SUSPEND

串口控制流挂起

IOCTL_UART_FLOW_CTL_RTS_RESUME

串口控制流恢复

IOCTL_UART_SET_IRQ_EVENT_CB

设置中断事件回调函数

const struct device_operations uart_dev_ops
int uart_init(const struct uart_platform_data *data)
void uart_debug_suspend(void)
void uart_debug_resume(void)

UART error

enum uart_event_t

Values:

enumerator UART_CIRCULAR_BUFFER_WRITE_OVERLAY

循环buf写满

enumerator UART_RECV_TIMEOUT

接收超时

enumerator UART_RECV_EXIT

接收终止退出

UART IRQ event

enum uart_irq_event_t

Values:

enumerator UART_IRQ_EVENT_NULL

0.无

enumerator UART_IRQ_EVENT_TX

1.TX pnd

enumerator UART_IRQ_EVENT_RX

2.RX pnd,当RX DMA连续收到的数据超过RXCNT的设定值时

enumerator UART_IRQ_EVENT_OT

3.OverTime pnd,当RX DMA收到一包数据后经过OTCNT个CLK后未再收到新数据时

enumerator UART_IRQ_EVENT_CTS

4.CTS pnd

enumerator UART_IRQ_EVENT_CHK

5.CHK pnd

struct uart_platform_data

Public Members

u8 irq

中断号,在注册的时候已经对应设置好了

u8 flow_ctl_enable

1:使能串口流控

u8 rts_hw_enable

1:使能硬件RTS

u8 rts_idle_level

0:RTS空闲电平为低; 1:RTS空闲电平为高

u8 cts_idle_level

0:CTS空闲电平为低; 1:CTS空闲电平为高

u8 rx_thresh

接收BUF触发流控的比值0-100

u8 disable_tx_irq

1:不使用发送中断

u8 disable_rx_irq

1:不使用中断接收

u8 disable_ot_irq

1:不使用超时中断

u8 tx_pin_hd_level

tx pin强驱等级

int tx_pin

发送引脚,不配置需设置-1

int rx_pin

接收引脚,不配置需设置-1

int rts_pin

串口一控制流RTS脚,不配置需设置-1

int cts_pin

串口一控制流CTS脚,不配置需设置-1

int flags

串口用作打印

u32 baudrate

波特率设置。理论范围:CLK/3 ~ CLK/4/(0xFFFF+1) -> CLK/3 ~ CLK/262144

u32 max_continue_recv_cnt

连续接收最大字节,范围:0 ~ 0xFFFFFFFF

u32 idle_sys_clk_cnt

超时计数器。在指定时间里没有收到数据,则产生超时中断,范围:0 ~ 0xFFFFFFFF

uart_clk_src_t clk_src

选择时钟源。可选:STD_48M、STD_24M、EXT_CLK、LSB_CLK

uart_parity_t parity

奇偶校验位。可选:UART_PARITY_DISABLE、UART_PARITY_EVEN、UART_PARITY_ODD