串口驱动工作方式
-
与大多数外设一样,串口的工作模式有三种:
- 中断驱动模式
- DMA异步模式
- 轮询模式
-
串口支持全双工通讯,不同的使用场景会采用不同的工作模式:
- 当传输的数据流长度不固定时,需要对每一字节进行处理,通常会采用中断接收数据,确保数据解析的实时性。
- 发送的数据量较大时,采用DMA进行发送,在DMA发送完成之前,其他任务可以继续正常工作,发送完成后阻塞任务被唤醒继续向下执行。
定义串口设备节点
- 与串口相连接的设备通常定义在串口对应的节点下,根据设备的不同为其添加不同的属性。
- 例如使用串口1控制串口蓝牙模块,该模块上除了使用串口,还可以提供一个低电平复位的引脚,此处不做演示,对应的设备树如下:
&usart1{
uart-bluetooth: bluetooth{
compatible = "uart-bluetooth-module";
status = "okay";
};
};
- 除了设备树,还需要编写对应的绑定文件,下面是uart-bluetooth-module.yaml的内容
description: uart bluetooth module
compatible: "uart-bluetooth-module"
include: uart-device.yaml
- 绑定文件的查找位置,在CMake编译设备树之前,会从一些目录查找绑定文件与设备树中的compatible属性进行匹配,如果匹配成功,会按照绑定文件中声明的类型将其编译成对应的宏,下面这些目录的子目录dts/bindings目录如果存在yaml文件,都会进行匹配:
- zephyr源码仓库
- 你的应用程序目录
- zephyr源码中的boards下所使用的开发板对应的目录
- 任何shield目录
- 为了方便的添加设备,会在应用程序目录下新建一个dts/bindings目录,用于存放yaml文件,如果不在上述目录中添加,那么程序中访问对应节点时会报错找不到宏,此时可以通过生成的头文件devicetree_generated.h查看是否存在与该属性相关的宏,如果没有则需要检查绑定文件的目录是否正确。
修改串口配置
- 此处需要使用串口中断,因此需要将 CONFIG_UART_INTERRUPT_DRIVEN 使能。
编写设备驱动程序
接口定义
- 在这里将串口驱动用一组接口来进行表示,方便对功能进行扩展,首先是接口头文件:
#pragma once
#include <stdint.h>
#include <stdbool.h>
typedef void com_inface_t;
typedef struct
{
const char *name;
const void *user_data;
void (*init)(com_inface_t *obj);
int (*read)(com_inface_t *obj, void *buf, uint32_t length, uint32_t timeout_ms);
int (*write)(com_inface_t *obj, const void *src, uint32_t size, uint32_t timeout_ms);
void (*flush)(com_inface_t *obj);
} com_drv_t;
void com_init(com_inface_t *p);
const char *com_device_name(com_inface_t *p);
int com_read(com_inface_t *p, void *buf, uint32_t length, uint32_t timeout_ms);
int com_write(com_inface_t *p, const void *src, uint32_t size, uint32_t timeout_ms);
void com_flush(com_inface_t *p);
- 接口源文件
#include "com_interface.h"
#define DEBUG_ASSERT(VALUE) \
while (!(VALUE)) \
{ \
}
void com_init(com_inface_t *p)
{
DEBUG_ASSERT(((com_drv_t *)p)->init);
((com_drv_t *)p)->init(p);
}
const char *com_device_name(com_inface_t *p)
{
return (p) ? (((com_drv_t *)p)->name) : ("");
}
int com_read(com_inface_t *p, void *buf, uint32_t length, uint32_t timeout_ms)
{
DEBUG_ASSERT(((com_drv_t *)p)->read);
return ((com_drv_t *)p)->read(p, buf, length, timeout_ms);
}
int com_write(com_inface_t *p, const void *src, uint32_t size, uint32_t timeout_ms)
{
DEBUG_ASSERT(((com_drv_t *)p)->write);
return ((com_drv_t *)p)->write(p, src, size, timeout_ms);
}
void com_flush(com_inface_t *p)
{
DEBUG_ASSERT(((com_drv_t *)p)->flush);
((com_drv_t *)p)->flush(p);
}
蓝牙串口驱动实例
- 蓝牙模块连接在串口1上,可以通过 DT_PARENT 获取父节点:
#define BLUETOOTH_NODE DT_NODELABEL(uart_bluetooth)
#if !DT_NODE_HAS_STATUS(BLUETOOTH_NODE, okay)
#error "Unsupported bluetooth: please check devicetree and retry"
#endif
#define BLUETOOTH_UART_NODE DT_PARENT(BLUETOOTH_NODE )
#define BLUETOOTH_UART_RX_RING_BUFFER_SIZE 512
#define BLUETOOTH_UART_TX_RING_BUFFER_SIZE 512
typedef struct
{
const struct device *dev;
uint16_t rx_front;
uint16_t rx_rear;
uint8_t rx_ring_buffer[BLUETOOTH_UART_RX_RING_BUFFER_SIZE];
struct k_sem rx_sem;
uint16_t tx_front;
uint16_t tx_rear;
uint8_t tx_ring_buffer[BLUETOOTH_UART_TX_RING_BUFFER_SIZE];
struct k_sem tx_sem;
} bluetooth_uart_t;
bluetooth_uart_t s_bluetooth_uart = {.dev = DEVICE_DT_GET(BLUETOOTH_UART_NODE)};
- 初始化串口
void bluetooth_uart_init(com_inface_t *p)
{
com_drv_t *obj = (com_drv_t *)p;
bluetooth_uart_t *uart = (bluetooth_uart_t *)obj->user_data;
if (obj)
{
if (!device_is_ready(uart->dev))
{
printk("bluetooth uart not ready\n");
return;
}
k_sem_init(&uart->rx_sem, 0, K_SEM_MAX_LIMIT);
k_sem_init(&uart->tx_sem, BLUETOOTH_UART_TX_RING_BUFFER_SIZE - 1, K_SEM_MAX_LIMIT);
uart_irq_callback_user_data_set(uart->dev, bluetooth_uart_isr, uart);
uart_irq_rx_enable(uart->dev);
}
}
- 串口中断回调函数
static int bluetooth_uart_irq_input(bluetooth_uart_t *uart, uint8_t c)
{
int rear = uart->rx_rear + 1;
if (rear >= BLUETOOTH_UART_RX_RING_BUFFER_SIZE)
{
rear = 0;
}
if (rear == uart->rx_front)
{
/* input was lost */
return 1;
}
uart->rx_ring_buffer[uart->rx_rear] = c;
uart->rx_rear = rear;
k_sem_give(&uart->rx_sem);
return 1;
}
static void bluetooth_uart_isr(const struct device *dev, void *user_data)
{
uint8_t c = 0;
bluetooth_uart_t *uart = user_data;
uart_irq_update(dev);
if (uart_irq_rx_ready(dev))
{
while (1)
{
if (uart_fifo_read(dev, &c, 1) == 0)
{
break;
}
bluetooth_uart_irq_input(uart, c);
}
}
if (uart_irq_tx_ready(dev))
{
if (uart->tx_front == uart->tx_rear)
{
uart_irq_tx_disable(dev);
}
else
{
uart_fifo_fill(dev, &uart->tx_ring_buffer[uart->tx_front++], 1);
if (uart->tx_front >= BLUETOOTH_UART_RX_RING_BUFFER_SIZE)
{
uart->tx_front = 0U;
}
k_sem_give(&uart->tx_sem);
}
}
}
- 串口读取函数
static int bluetooth_uart_read_char(bluetooth_uart_t *uart, uint32_t timeout)
{
int ret = 0;
uint8_t c = 0;
unsigned int key = 0;
ret = k_sem_take(&uart->rx_sem, SYS_TIMEOUT_MS(timeout));
if (ret < 0)
{
return ret;
}
key = irq_lock();
c = uart->rx_ring_buffer[uart->rx_front++];
if (uart->rx_front >= BLUETOOTH_UART_RX_RING_BUFFER_SIZE)
{
uart->rx_front = 0U;
}
irq_unlock(key);
return c;
}
static int bluetooth_uart_read(com_inface_t *p, void *buf, uint32_t length, uint32_t timeout)
{
uint32_t end_time = k_uptime_get_32() + timeout;
uint32_t remaining_time = timeout;
com_drv_t *obj = (com_drv_t *)p;
bluetooth_uart_t *uart = (bluetooth_uart_t *)obj->user_data;
int res = 0;
int out = 0;
uint8_t *data = buf;
while (length && (remaining_time <= timeout))
{
res = bluetooth_uart_read_char(uart, remaining_time);
if (res < 0)
{
if (out == 0)
{
errno = -res;
return res;
}
return out;
}
*data++ = (uint8_t)res;
++out;
--length;
if (timeout != SYS_FOREVER_MS)
{
remaining_time = end_time - k_uptime_get_32();
}
}
return out;
}
- 串口写入函数
static int bluetooth_uart_write_char(bluetooth_uart_t *uart, uint8_t c, uint32_t timeout)
{
int ret = 0;
int tx_rear = 0;
unsigned int key = 0;
ret = k_sem_take(&uart->tx_sem, SYS_TIMEOUT_MS(timeout));
if (ret < 0)
{
return ret;
}
key = irq_lock();
tx_rear = uart->tx_rear + 1;
if (tx_rear >= BLUETOOTH_UART_TX_RING_BUFFER_SIZE)
{
tx_rear = 0;
}
if (tx_rear == uart->tx_front)
{
irq_unlock(key);
return -ENOSPC;
}
uart->tx_ring_buffer[uart->tx_rear] = c;
uart->tx_rear = tx_rear;
irq_unlock(key);
uart_irq_tx_enable(uart->dev);
return 0;
}
static int bluetooth_uart_write(com_inface_t *p, const void *src, uint32_t size, uint32_t timeout)
{
uint32_t end_time = k_uptime_get_32() + timeout;
uint32_t remaining_time = timeout;
com_drv_t *obj = (com_drv_t *)p;
bluetooth_uart_t *uart = (bluetooth_uart_t *)obj->user_data;
int res = 0;
int out = 0;
const uint8_t *data = src;
while (size && (remaining_time <= timeout))
{
res = bluetooth_uart_write_char(uart, *data++, remaining_time);
if (res < 0)
{
if (out == 0)
{
errno = -res;
return res;
}
return out;
}
++out;
--size;
if (timeout != SYS_FOREVER_MS)
{
remaining_time = end_time - k_uptime_get_32();
}
}
return out;
}
- 清空接收缓存
static void bluetooth_uart_flush(com_inface_t *p)
{
unsigned int key = 0;
com_drv_t *obj = (com_drv_t *)p;
bluetooth_uart_t *uart = (bluetooth_uart_t *)obj->user_data;
k_sem_reset(&uart->rx_sem);
key = irq_lock();
uart->rx_front = 0;
uart->rx_rear = 0;
irq_unlock(key);
}
- 最后是获取实例的方法,仅有该函数可以在外部访问:
com_drv_t *bluetooth_uart_drv_get(void)
{
static com_drv_t drv = {
.name = usart1,
.init = bluetooth_uart_init,
.flush = bluetooth_uart_flush,
.read = bluetooth_uart_read,
.write = bluetooth_uart_write,
.user_data = &s_bluetooth_uart};
return &drv;
}
- 头文件声明
#pragma once
#include "com_interface.h"
com_drv_t *bluetooth_uart_drv_get(void);
功能演示
void main(void)
{
com_inface_t *drv = bluetooth_uart_drv_get();
com_init(drv);
while (1)
{
com_write(drv, "hello world\r\n", strlen("hello world\r\n"), K_TICKS_FOREVER);
k_msleep(1000);
}
}