2.3. UART _SPEC
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驱动使用步骤:
配置UART。配置
uart_platform_data
结构体,设置UART参数,如波特率、奇偶校验位等。UART设备打开时会根据该配置进行初始化。注册UART设备。
打开设备。通过提供UART设备名,SDK会找到步骤2注册的设备配置信息,来对UART进行初始化。
操作设备。使用
dev_write()
、dev_read()
、dev_ioctl()
等对设备进行操作。关闭设备。
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参数。buf
为NULL
或者cnt
为0
时,返回值为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长度的buf
给dev_read(hdl, buf, 10)
用于读取uart接收到的数据,而串口只收到5
Byte数据,那么dev_read()
函数将只返回5
Byte数据;而如果使能了UART_SET_RECV_ALL
,dev_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仅UART1
和UART2
支持硬件流控,该命令仅对这两个串口模块有效。
dev_ioctl(hdl, UART1_FLOW_CTL_RTS_SUSPEND, 0);
14.UART_FLOW_CTL_RTS_RESUME
功能:串口控制流恢复。串口恢复数据接收,并控制RTS脚,指示本设备准备好了,发送方可以发送数据。
参数:无作用。
注意:AC792N仅UART1
和UART2
支持硬件流控,该命令仅对这两个串口模块有效。
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_ADDR
和UART_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
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
-
enumerator UART_IRQ_EVENT_NULL
-
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
-
u8 irq