目录
前言
一、Uart Device介绍
1 常用API介绍
1.1 uart_configure
1.2 uart_get
1.3?uart_tx
1.4?uart_tx_abort
1.5?uart_rx_enable
1.6?uart_poll_in
1.7?uart_poll_out
1.8? uart_callback_set
1.9?uart_irq_callback_set(旧版)
1.9.2 回调函数介绍
2.0?uart_irq_callback_user_data_set
2.1?uart_irq_tx_enable
2.2?uart_irq_tx_disable
2.3 uart_irq_rx_enable
2.2?uart_irq_rx_disable
2.3?uart_irq_tx_ready
2.4?uart_irq_update
2.2 参数介绍
2.3 注释
2.5?uart_irq_rx_ready
2.2 参数介绍
2.5?uart_irq_tx_ready
2.2 参数介绍
2.5 IRQ相关函数使用事项
2.6?uart_fifo_fill
2.7 uart_fifo_fill
2.8 uart_drv_cmd
2 结构体介绍
1. uart_config
1.3 位相关枚举
2. uart_event
2.1 结构体原型
2.2 主要成员介绍
3 开发注意事项
4 示例
前言
Uart实现原理与理论知识请参考这篇文章:UART工作原理详解
一、Uart Device介绍
1 常用API介绍
1.1.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
int uart_configure(const struct device* dev, const struct uart_config* cfg) | 配置uart设备 | 0成功,非0失败 |
1.1.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device* | 指向Uart设备驱动指针 | cfg | const struct uart_config* | 指向配置结构体的指针 |
1.2 uart_get
1.2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
int uart_config_get(const struct device* dev, struct uart_config* cfg) | 获取当前Uart设备的属性,如波特率之类的 | 0成功,非0失败 |
1.2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device* | 指向Uart设备驱动指针 | cfg | struct uart_config* | 指向配置结构体的指针,会将Uart设备属性写入到这个结构体的成员变量中 |
1.3?uart_tx
1.3.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
int uart_tx(const struct device* dev, const uint8_t* buf, size_t len, int32_t timeout) | 向串口写入数据 | 0成功,非0失败 |
1.3.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device* | 指向Uart设备驱动指针 | buf | const uint8_t* | 要写入的数据 | len | size_t | 写入数据的长度 | timeout | int32_t | 超时等待时长,毫秒为单位 |
1.4?uart_tx_abort
1.4.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
int uart_tx_abort(const struct device *dev) | 终止当前串口数据的发送 | 0成功,非0失败 |
1.4.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 |
1.5?uart_rx_enable
1.5.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
int uart_rx_enable(const struct device *dev, uint8_t *buf, size_t len, int32_t timeout) | 从串口接收数据 | 0成功,非0失败 |
1.5.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 | buf | uint8_t * | 指向接收缓冲区的指针 | len | size_t | 接收缓冲区的大小 | timeout | int32_t | 超时时间 |
1.6?uart_poll_in
1.6.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
int uart_poll_in(const struct device *dev, unsigned char *p_char) | 在轮询的模式下接收数据的输入 | 0成功,非0失败 |
1.6.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 | p_char | unsigned char * | 指向unsigned char类型的指针 |
1.6.3 注释
轮询模式是指每隔一段时间就去读一下缓冲区是否有数据,这个函数每次只能读入一个字节的字符
此函数需要配合循环使用,这个函数的作用就是去检查UART LSR寄存器中的DR位,这个位表示是否有新的数据到来,如果为1则有数据到了,则从里面读入一个字节数据出来,需要注意它不是阻塞函数,需要用户使用while循环配合使用
1.7?uart_poll_out
1.7.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
void uart_poll_out(const struct device *dev, unsigned char out_char) | 轮询的模式下向串口输出一个字符 | 无返回值 |
1.7.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 | out_char | unsigned char | 输出的字符 |
1.7.3 注释
此函数会向数据缓存器里写一个字节,然后查UART LSR寄存器中的TEMT和THRE两个位是否为0,只有这两个位为0时则代表数据发送寄存器里已经有数据了以及已经做好了发送准备可以进行发送了,然后开始发送,它也不是阻塞函数,需要用户配合while循环来使用,目的就是为了达到轮询的效果
1.8? uart_callback_set
1.8.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
static inline int uart_callback_set(const struct device *dev, uart_callback_t callback, void *user_data) | 设置事件处理回调函数 | 0成功,非0失败 |
1.8.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 | callback | uart_callback_t | 回调函数,原型:void uart_call (const struct device *dev, struct uart_event* evt,void* user_data) | user_data | void * | 要传递的参数 |
1.8.3 回调函数介绍
1.8.3.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
void uart_call (const struct device *dev, struct uart_event* evt,void* user_data) | uart事件回调 | 无 |
1.8.3.2 参数介绍
这些参数会在回调时由uart驱动传递进来
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 | evt | struct uart_event* | 发生的事件 | user_data | void* | 传递参数 |
1.9?uart_irq_callback_set(旧版)
1.9.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
static inline void uart_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb) | 设置IRQ中断回调函数 | 无 |
1.9.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 | cb | uart_irq_callback_user_data_t | 回调函数原型 |
1.9.2 回调函数介绍
1.9.2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
static void func_irq(const struct device *dev, void *user_data) | 当UART硬件产生中断时回调的函数 | 无 |
1.9.2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 产生中断的Uart设备驱动 | user_data | void * | 传递的数据指针 |
1.9.3 注释
这个函数不支持传递数据指针
2.0?uart_irq_callback_user_data_set
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
static inline void uart_irq_callback_user_data_set(const struct device *dev, uart_irq_callback_user_data_t cb, void *user_data) | 设置IRQ回调函数,支持传递参数 | 无 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 | cb | uart_irq_callback_user_data_t | 回调函数原型 | user_data | void * | 传递参数 |
2.3 注释
回调参数原型参考uart_irq_callback_set函数介绍
2.1?uart_irq_tx_enable
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
void uart_irq_tx_enable(const struct device *dev) | 将IER(中断使能寄存器)寄存器中的TX开启,允许产生TX中断 | 无 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 |
2.2?uart_irq_tx_disable
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
void uart_irq_tx_disable(const struct device *dev) | 将IER(中断使能寄存器)寄存器中的TX关闭,禁止产生TX中断 | 无 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 |
2.3 uart_irq_rx_enable
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
void uart_irq_rx_enable(const struct device *dev) | 将IER(中断使能寄存器)寄存器中的RX开启,允许产生RX中断 | 无 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 |
2.2?uart_irq_rx_disable
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
void uart_irq_rx_disable(const struct device *dev) | 将IER(中断使能寄存器)寄存器中的RX关闭,禁止产生RX中断 | 无 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 |
2.3?uart_irq_tx_ready
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
static inline int uart_irq_tx_ready(const struct device *dev) | 检查TX的Ready位是否有效,即判断TX FIFO输入缓冲区是否可以接收新的数据 | 可以写入返回1,不能写入返回0 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 |
2.4?uart_irq_update
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
int uart_irq_update(const struct device *dev) | 更新中断标志位 | 成功返回1,否则返回0 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 |
2.3 注释
在硬件中断开发时我们是需要手动更新中断标志位的,这个函数的作用就是如此
2.5?uart_irq_rx_ready
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
static inline int uart_irq_rx_ready(const struct device *dev) | 检查RX Ready位是否为1,即检查输入缓冲区是否有新的数据来 | 返回1则代表有新数据可以读取,非1则无数据 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 |
2.5?uart_irq_tx_ready
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
static inline int uart_irq_tx_ready(const struct device *dev) | 检查TX Ready位是否为1,即检查输出缓冲区是否有新的数据可以输出 | 返回1则代表有新数据可以输出,非1则无数据 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 |
2.5 IRQ相关函数使用事项
在使用IRQ相关的函数时,Zephry要求在中断处理函数中,先调用uart_irq_update更新中断标志寄存器,然后调用uart_irq_tx_ready或uart_irq_rx_ready来检查输入输出是否有效,然后在进行相关读写操作
其次uart_irq_callback_set与uart_callback_set是不同的,uart_irq_callback_set是用来设置硬件方面的中断的回调,uart_callback_set是用来Zephry Uart驱动内部回调事件,比如Uart驱动写入了一个数据它自己会产生一个消息为DONE,即写入完成的事件然后回调函数并将事件传入
2.6?uart_fifo_fill
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
static inline int uart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) | 向FIFO缓冲区写入数据 | 返回写入字节长度 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 | tx_data | const uint8_t * | 输入缓冲区指针 | size | int | 缓冲区大小 |
2.7 uart_fifo_fill
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
static inline int uart_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) | 从FIFO缓冲区读取数据 | 返回读取字节长度 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device * | 指向Uart设备驱动指针 | rx_data | const uint8_t * | 接收数据缓冲区指针 | size | int | 缓冲区大小 |
2.8 uart_drv_cmd
2.1 函数介绍
函数原型 | 作用 | 返回值 |
---|
int uart_drv_cmd(const struct device* dev, uint32_t cmd, uint32_t p) | 向串口发送命令 | 0成功,非0失败 |
2.2 参数介绍
参数名 | 类型 | 作用 |
---|
dev | const struct device* | 指向Uart设备驱动指针 | cmd | uint32_t | 命令 | p | uint32_t | 命令附加参数 |
2.3 注释
该函数需要驱动程序实现uart命令,详细请参考Zephry Uart驱动
2 结构体介绍
1. uart_config
1.1 结构体原型
struct uart_config {
uint32_t baudrate;
uint8_t parity;
uint8_t stop_bits;
uint8_t data_bits;
uint8_t flow_ctrl;
};
1.2 成员介绍
成员名 | 类型 | 作用 |
---|
baudrate | uint32_t | 波特率 | parity | uint8_t | 奇偶校验位 | stop_bits | uint8_t | 停止位 | data_bits | uint8_t | 数据位 | flow_ctrl | uint8_t | 流控制位 |
1.3 位相关枚举
1.3.1 奇偶校验位:uart_config_parity
枚举定义 | 作用 |
---|
UART_CFG_PARITY_NONE | 无奇偶校验位 | UART_CFG_PARITY_ODD | 奇校验位,1为奇数校验位为0,偶数校验位为1 | UART_CFG_PARITY_EVEN | 偶校验位,1为奇数校验位为1,偶数校验位为0 | UART_CFG_PARITY_MARK | 校验位始终为1 | UART_CFG_PARITY_SPACE | 校验位始终为0 |
1.3.2 停止位:uart_config_stop_bits
枚举定义 | 作用 |
---|
UART_CFG_STOP_BITS_0_5 | 停止位为0或5个BIT | UART_CFG_STOP_BITS_1 | 停止位为1个BIT | UART_CFG_STOP_BITS_1_5 | 停止位为1或5个BIT | UART_CFG_STOP_BITS_2 | 停止位为2个BIT |
1.3.3 数据位: uart_config_data_bits
枚举定义 | 作用 |
---|
UART_CFG_DATA_BITS_5 | 数据位为5BIT | UART_CFG_DATA_BITS_6 | 数据位为6BIT | UART_CFG_DATA_BITS_7 | 数据位为7BIT | UART_CFG_DATA_BITS_8 | 数据位为8BIT | UART_CFG_DATA_BITS_9 | 数据位为9BIT |
1.3.4 流控制位:uart_config_flow_control
枚举定义 | 作用 |
---|
UART_CFG_FLOW_CTRL_NONE | 无流控制 | UART_CFG_FLOW_CTRL_RTS_CTS | RTS/CTS线有效 | UART_CFG_FLOW_CTRL_DTR_DSR | DTR/DSR线有效 |
流控制一旦开启,需要用户手动根据当前流方向来修改RTS/CTS或DTR/DSR线的高低电平
2. uart_event
2.1 结构体原型
struct uart_event {
enum uart_event_type type;
union uart_event_data {
struct uart_event_tx tx;
struct uart_event_rx rx;
struct uart_event_rx_buf rx_buf;
struct uart_event_rx_stop rx_stop;
} data;
};
2.2 主要成员介绍
1. type
此成员是个枚举,它的值指向当前发生的事件,可以取如下值:
枚举定义 | 作用 |
---|
枚举定义 | 作用 |
---|
UART_TX_DONE | 写入完成 | UART_TX_ABORTED | 超时写入,在调用uart_tx有个超时参数,当超时时会发送此事件 | UART_RX_RDY | 接收数据已经准备好处理 | UART_RX_BUF_REQUEST | 缓冲区切换接收,当一个缓冲区在接收时调用uart_rx_buf_rsp进行无缝切换接收时会产生这个事件 | UART_RX_BUF_RELEASED, | RX接收被禁用且此时可以进行启动时候产生的事件 | UART_RX_DISABLED | RX被禁用时产生的事件 | UART_RX_STOPPED | 由于外部原因,导致RX停止接收时产生的事件 |
2.?uart_event_data
此成员是个联合体,当产生不同事件时它内部仅有一个成员有效
当产生tx相关事件时tx成员有效
rx相关时rx成员有效
产生stop时rx_stop有效
3 开发注意事项
Zephry针对不同的板子有不同的Uart驱动实现,都放在zephyrproject/zephyr/drivers/serial这个目录下打开可以看到有不同板子的实现
CMakeLists.txt Kconfig.mcux_lpsci Kconfig.test uart_lpc11u6x.c uart_rcar.c
Kconfig Kconfig.mcux_lpuart Kconfig.uart_sam uart_lpc11u6x.h uart_rom_esp32c3.c
Kconfig.altera_jtag Kconfig.miv Kconfig.usart_sam uart_mcux.c uart_rtt.c
Kconfig.apbuart Kconfig.msp432p4xx Kconfig.xlnx uart_mcux_flexcomm.c uart_rv32m1_lpuart.c
Kconfig.b91 Kconfig.native_posix Kconfig.xmc4xxx uart_mcux_iuart.c uart_sam0.c
Kconfig.cc13xx_cc26xx Kconfig.npcx leuart_gecko.c uart_mcux_lpsci.c uart_sam.c
Kconfig.cc32xx Kconfig.nrfx serial_test.c uart_mcux_lpuart.c uart_sifive.c
Kconfig.cmsdk_apb Kconfig.ns16550 uart_altera_jtag_hal.c uart_miv.c uart_stellaris.c
Kconfig.esp32 Kconfig.nuvoton uart_apbuart.c uart_msp432p4xx.c uart_stm32.c
Kconfig.esp32c3_rom Kconfig.pl011 uart_b91.c uart_native_posix.c uart_stm32.h
Kconfig.gecko Kconfig.psoc6 uart_cc13xx_cc26xx.c uart_npcx.c uart_xlnx_ps.c
Kconfig.imx Kconfig.rcar uart_cc32xx.c uart_nrfx_uart.c uart_xlnx_uartlite.c
Kconfig.leuart_gecko Kconfig.rtt uart_cmsdk_apb.c uart_nrfx_uarte.c uart_xmc4xxx.c
Kconfig.litex Kconfig.rv32m1_lpuart uart_esp32.c uart_ns16550.c usart_sam.c
Kconfig.lpc11u6x Kconfig.sam0 uart_gecko.c uart_ns16550.h
Kconfig.mcux Kconfig.sifive uart_handlers.c uart_nuvoton.c
Kconfig.mcux_flexcomm Kconfig.stellaris uart_imx.c uart_pl011.c
Kconfig.mcux_iuart Kconfig.stm32 uart_liteuart.c uart_psoc6.c
可以看到许多uart开头的实现文件,如我们的是stm32的板子,对应的驱动实现就是uart_stm32.c这个文件,如果你在使用时出现了问题,可以到这个文件中去看下驱动实现,出现了什么问题
4 示例
这里我基于FIFO模式实现了一个LOOPBACK回环的代码,我的UART RX与TX是相连的,可供大家参考与使用
注意使用前需要在prj.conf中开启驱动
CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y
CONFIG_UART_INTERRUPT_DRIVEN=y
完整实现代码:
#include <zephyr.h>
#include <sys/printk.h>
#include <device.h>
#include <drivers/uart.h>
#include <errno.h>
#include <string.h>
//Packaging structure
struct tx_data{
uint8_t* data;
size_t len;
size_t msg;
}send_data;
struct rx_data{
uint8_t* data;
size_t len;
size_t msg;
}recv_data;
static void uart_fifo_callback(const struct device *dev, void *user_data){
static int tx_send_index;
static int rx_recv_index;
struct tx_data* tx_send_data = &send_data;
struct rx_data* rx_recv_data = &recv_data;
if(uart_irq_update(dev) !=1 ){
return;
}
//if write
if(uart_irq_tx_ready(dev) && tx_send_index < tx_send_data->len && tx_send_data->msg == MSG_SEND){
uart_fifo_fill(dev,(uint8_t*)&tx_send_data->data[tx_send_index++],SEND_LEN);
if(tx_send_index >= tx_send_data->len){
tx_send_index = 0;
tx_send_data->msg = MSG_DONE;
//Stop and interrupt manually after meeting the requirements
uart_irq_tx_disable(dev);
}
}
//if read
if(uart_irq_rx_ready(dev)){
uart_fifo_read(dev,(uint8_t*)&rx_recv_data->data[rx_recv_index++],RECV_LEN);
if(rx_recv_index >= rx_recv_data->len){
uart_irq_rx_disable(dev);
}
}
}
int uart_send(const struct device* dev,uint8_t* data,size_t len){
if(dev == NULL || data == NULL || len == 0){
return -1;
}
send_data.data = data;
send_data.len = len;
send_data.msg = MSG_SEND;
//After the enable interrupt, judge whether to write or not
uart_irq_tx_enable(dev);
k_sleep(K_MSEC(TIME_OUT));
uart_irq_tx_disable(dev);
if(send_data.msg != MSG_DONE) return -2;
return 0;
}
int uart_recv(const struct device* dev,uint8_t* data,size_t len){
if(dev == NULL || data == NULL || len == 0){
return -1;
}
recv_data.data = data;
recv_data.len = len;
recv_data.msg = MSG_RECV;
uart_irq_rx_enable(dev);
k_sleep(K_MSEC(TIME_OUT));
uart_irq_rx_disable(dev);
if(send_data.msg != MSG_DONE) return -2;
return 0;
}
void main(){
printk("--- UART ---\n");
uint8_t tx_buff[BUFF_LEN] = "hello word\r\n1";
uint8_t rx_buff[BUFF_LEN] = {0};
const struct device* dev = device_get_binding("UART_6");
if(dev == NULL){ printk("ERROR bind\n"); return; }
if(!device_is_ready(dev)) { printk("device is no ready\n"); return; }
struct uart_config ut = {
.baudrate = 115200,
.parity = UART_CFG_PARITY_NONE,
.stop_bits = UART_CFG_STOP_BITS_1,
.data_bits = UART_CFG_DATA_BITS_8,
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE,
};
if(uart_configure(dev,&ut) != 0) {
printk("uart config error\n");
return;
}
uart_irq_callback_set(dev,uart_fifo_callback);
//write
recv_data.data = rx_buff;
recv_data.len = strlen(rx_buff);
uart_send(dev,tx_buff,strlen(tx_buff));
printk("%s",rx_buff);
}
上述代码需要注意我没有写接收,因为是回环相接,当产生写入硬中断并写入后会立马产生RX中断
运行结果:
ing Zephyr OS build zephyr-v2.6.0-1753-g365ff6db9f02 ***
--- UART ---
hello word
|