> 文章列表 > Zephyr UART

Zephyr UART

Zephyr UART

文章目录

  • 串口驱动工作方式
  • 定义串口设备节点
  • 修改串口配置
  • 编写设备驱动程序
    • 接口定义
    • 蓝牙串口驱动实例
    • 功能演示

串口驱动工作方式

  • 与大多数外设一样,串口的工作模式有三种:

    • 中断驱动模式
    • DMA异步模式
    • 轮询模式
  • 串口支持全双工通讯,不同的使用场景会采用不同的工作模式:

    • 当传输的数据流长度不固定时,需要对每一字节进行处理,通常会采用中断接收数据,确保数据解析的实时性。
    • 发送的数据量较大时,采用DMA进行发送,在DMA发送完成之前,其他任务可以继续正常工作,发送完成后阻塞任务被唤醒继续向下执行。

定义串口设备节点

  • 与串口相连接的设备通常定义在串口对应的节点下,根据设备的不同为其添加不同的属性。
  • 例如使用串口1控制串口蓝牙模块,该模块上除了使用串口,还可以提供一个低电平复位的引脚,此处不做演示,对应的设备树如下:
&usart1{uart-bluetooth: bluetooth{compatible = "uart-bluetooth-module";status = "okay";};
};
  • 除了设备树,还需要编写对应的绑定文件,下面是uart-bluetooth-module.yaml的内容
description: uart bluetooth modulecompatible: "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 512typedef 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);}
}