Zephyr UART

文章目录

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

串口驱动工作方式

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

    • 中断驱动模式
    • 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 
#include 

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);
	}
}

你可能感兴趣的:(笔记,操作系统,单片机,stm32,嵌入式硬件,物联网)