使用Zephyr在STM32上配置UART通信

https://docs.zephyrproject.org/latest/hardware/peripherals/uart.html#uart-interrupt-api

本文基本上就是翻译官方文档,写个笔记

zephyr提供串口的三种方式

1、Polling API(轮询方式)
2、Interrupt-driven API(中断方式)
3、Asynchronous API using Direct Memory Access (DMA)(异步DMA)

配置项

CONFIG_SERIAL
//打开串口驱动,有许多配置项需要依赖这一项
CONFIG_UART_INTERRUPT_DRIVEN
//使能串口中断
CONFIG_UART_ASYNC_API
//使能异步串口中断(DMA)
CONFIG_UART_WIDE_DATA
//使能16字节的api(驱动支持的情况下),比如uart_poll_in_u16、uart_fifo_fill_u16等
CONFIG_UART_USE_RUNTIME_CONFIGURE
//启用UART控制器的运行时配置,允许应用程序在运行时调用uart_configure()来配置UART控制器,并调用uart_config_get()来检索配置
//如果禁用此功能,则UART控制器依赖UART驱动程序的初始化功能来正确配置控制器(dts配置)
CONFIG_UART_LINE_CTRL
//使能流控制
CONFIG_UART_DRV_CMD
//允许使用api向驱动程序发送一些命令

硬件流控制可以参考这位的博客

常用api

此处列举文档中的functions,方便查询,具体参数和返回值详见文档

api 简述
int uart_err_check(const struct device *dev) 检查串口是否错误
int uart_configure(const struct device *dev, const struct uart_config *cfg) 配置串口
int uart_config_get(const struct device *dev, struct uart_config *cfg) 获取当前串口配置
int uart_line_ctrl_set(const struct device *dev, uint32_t ctrl, uint32_t val) 设置串口的流控制(宏控制)
int uart_line_ctrl_get(const struct device *dev, uint32_t ctrl, uint32_t *val) 获取串口的流控制等数据
int uart_drv_cmd(const struct device *dev, uint32_t cmd, uint32_t p) 发送额外的命令给驱动(宏控制)
struct uart_config {
	uint32_t baudrate;  /* 波特率 */
	uint8_t parity;     /* 奇偶校验 */
	uint8_t stop_bits;  /* 停止位 */
	uint8_t data_bits;  /* 数据位 */
	uint8_t flow_ctrl;  /* 流控制 */
};

其他函数中所需的结构体详见代码,路径如下:

zephyr/include/zephyr/drivers/uart.h

轮询方式(Polling)

api 简述
int uart_poll_in(const struct device *dev, unsigned char *p_char) 串口接收一个字节的数据
int uart_poll_in_u16(const struct device *dev, uint16_t *p_u16) 接收两个字节的数据(宏配置)
void uart_poll_out(const struct device *dev, unsigned char out_char) 串口输出一个字节的数据
void uart_poll_out_u16(const struct device *dev, uint16_t out_u16) 串口输出两个字节数据

说明:串口输出直接使用printf puts或printk更便捷
测试demo

/*dts中需要将uart1这个label起个别名*/
aliases {
		single-line-uart1 = &usart1;
	};
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/uart.h>

#define UART_NODE1 DT_ALIAS(single_line_uart1)

const struct device *const sl_uart1 = DEVICE_DT_GET(UART_NODE1);

int main(void)
{
	unsigned char recv;

	if (!device_is_ready(sl_uart1))
	{
		printk("uart devices not ready\n");
		return 0;
	}

	while (true)
	{
		int ret = uart_poll_in(sl_uart1, &recv);
		if (ret < 0)
		{
			printk("Receiving failed. Error: %d\n", ret);
		}
		else
		{
			printk("Received %c\n", recv);
		}

		k_sleep(K_MSEC(1000));
	}
	return 0;
}
*** Booting Zephyr OS build zephyr-v3.5.0 ***
Receiving failed. Error: -1
Receiving failed. Error: -1
Receiving failed. Error: -1
Received 2

中断方式

函数指针 功能
typedef void (*uart_irq_callback_user_data_t)(const struct device *dev, void *user_data static inline int uart_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb)的第二个参数,函数指针
typedef void (*uart_irq_config_func_t)(const struct device *dev) 配置串口的函数指针
函数 简述
static inline int uart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) 发送8位data到FIFO
static inline int uart_fifo_fill_u16(const struct device *dev, const uint16_t *tx_data, int size) 发送16位data到FIFO
static inline int uart_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) 从FIFO接收8位
static inline int uart_fifo_read_u16(const struct device *dev, uint16_t *rx_data, const int size) 从FIFO接收16位

上述的api,使用的时候,需要先判断 uart_irq_tx_ready() uart_irq_rx_ready(),如果返回1,则必须在中断处理函数中调用

api brief
void uart_irq_tx_enable(const struct device *dev) enable TX
void uart_irq_tx_disable(const struct device *dev) disable TX
static inline int uart_irq_tx_ready(const struct device *dev) 检查发送缓冲区是否有数据
void uart_irq_rx_enable(const struct device *dev) enable RX
void uart_irq_rx_disable(const struct device *dev) disable RX
static inline int uart_irq_tx_complete(const struct device *dev) 检查tx是否传输完成
static inline int uart_irq_rx_ready(const struct device *dev) 检查接收缓冲区是否有数据
void uart_irq_err_enable(const struct device *dev) enable error interrupt
void uart_irq_err_disable(const struct device *dev) Disable error interrupt
int uart_irq_is_pending(const struct device *dev) 检查是否有等待的中断
int uart_irq_update(const struct device *dev) 开始中断
static inline int uart_irq_callback_user_data_set(const struct device *dev, uart_irq_callback_user_data_t cb, void *user_data) 设置中断请求函数指针,指定用户uart_irq_callback_user_data_t
static inline int uart_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb) 设置中断请求函数指针

注:1、中断处理函数中,在调用uart_irq_tx_ready之前,必须调用 uart_irq_update()函数
2、中断处理函数中,在调用uart_irq_tx_complete之前,必须调用 uart_irq_update()函数
3、文档中ISR指Interrupt Service Routine,中断服务程序
4、文档中IRQ指Interrupt Request, 中断请求
5、uart_irq_update函数是中断开始时调用的第一个函数

测试demo可以使用下面的sample,代码中用中断的方式来获取串口接收到的数据,并且判断是否以“\r\n”结尾,如果是,表示一个数据包接收完成,然后将接收到的数据发送到消息队列中,main中的while阻塞等到消息队列是否有数据,如果有,则输出。

/zephyr/samples/drivers/uart/echo_bot/src

dma的方式后续有空再补

作者:靖歆

物联沃分享整理
物联沃-IOTWORD物联网 » 使用Zephyr在STM32上配置UART通信

发表评论