Skip to content

串口设备

访问的时候使用到的函数

函数描述
rt_device_find()查找设备
rt_device_open()打开设备
rt_device_read()读取数据
rt_device_write()写入数据
rt_device_control()控制设备
rt_device_set_rx_indicate()设置接收回调函数
rt_device_set_tx_complete()设置发送完成回调函数
rt_device_close()关闭设备

查找

c
rt_device_t rt_device_find(const char* name);

一般情况下,注册到系统的串口设备名称为 uart0,uart1等,使用示例如下所示:

c
#define SAMPLE_UART_NAME       "uart2"    /* 串口设备名称 */
static rt_device_t serial;                /* 串口设备句柄 */
/* 查找串口设备 */
serial = rt_device_find(SAMPLE_UART_NAME);

打开一个串口设备

c
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
c
#define RT_DEVICE_FLAG_STREAM       0x040     /* 流模式      */
/* 接收模式参数 */
#define RT_DEVICE_FLAG_INT_RX       0x100     /* 中断接收模式 */
#define RT_DEVICE_FLAG_DMA_RX       0x200     /* DMA 接收模式 */
/* 发送模式参数 */
#define RT_DEVICE_FLAG_INT_TX       0x400     /* 中断发送模式 */
#define RT_DEVICE_FLAG_DMA_TX       0x800     /* DMA 发送模式 */

串口数据接收和发送数据的模式分为 3 种:中断模式、轮询模式、DMA 模式。

默认使用轮询模式。

RT_DEVICE_FLAG_STREAM:流模式用于向串口终端输出字符串:当输出的字符是 "\n" (对应 16 进制值为 0x0A)时,自动在前面输出一个 "\r"(对应 16 进制值为 0x0D) 做分行。

c
#define SAMPLE_UART_NAME       "uart2"    /* 串口设备名称 */
static rt_device_t serial;                /* 串口设备句柄 */
/* 查找串口设备 */
serial = rt_device_find(SAMPLE_UART_NAME);

/* 以中断接收及轮询发送模式打开串口设备 */
rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);

控制

c
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
dev设备句柄
cmd命令控制字,可取值:RT_DEVICE_CTRL_CONFIG
arg控制的参数,可取类型: struct serial_configure
返回RT_EOK函数执行成功, RT_ENOSYS执行失败,dev 为空
c
struct serial_configure
{
    rt_uint32_t baud_rate;            /* 波特率 */
    rt_uint32_t data_bits    :4;      /* 数据位 */
    rt_uint32_t stop_bits    :2;      /* 停止位 */
    rt_uint32_t parity       :2;      /* 奇偶校验位 */
    rt_uint32_t bit_order    :1;      /* 高位在前或者低位在前 */
    rt_uint32_t invert       :1;      /* 模式 */
    rt_uint32_t bufsz        :16;     /* 接收数据缓冲区大小 */
    rt_uint32_t reserved     :4;      /* 保留位 */
};
c
/* 波特率可取值 */
#define BAUD_RATE_2400                  2400
#define BAUD_RATE_4800                  4800
#define BAUD_RATE_9600                  9600
#define BAUD_RATE_19200                 19200
#define BAUD_RATE_38400                 38400
#define BAUD_RATE_57600                 57600
#define BAUD_RATE_115200                115200
#define BAUD_RATE_230400                230400
#define BAUD_RATE_460800                460800
#define BAUD_RATE_921600                921600
#define BAUD_RATE_2000000               2000000
#define BAUD_RATE_3000000               3000000
/* 数据位可取值 */
#define DATA_BITS_5                     5
#define DATA_BITS_6                     6
#define DATA_BITS_7                     7
#define DATA_BITS_8                     8
#define DATA_BITS_9                     9
/* 停止位可取值 */
#define STOP_BITS_1                     0
#define STOP_BITS_2                     1
#define STOP_BITS_3                     2
#define STOP_BITS_4                     3
/* 极性位可取值 */
#define PARITY_NONE                     0
#define PARITY_ODD                      1
#define PARITY_EVEN                     2
/* 高低位顺序可取值 */
#define BIT_ORDER_LSB                   0
#define BIT_ORDER_MSB                   1
/* 模式可取值 */
#define NRZ_NORMAL                      0     /* normal mode */
#define NRZ_INVERTED                    1     /* inverted mode */
/* 接收数据缓冲区默认大小 */
#define RT_SERIAL_RB_BUFSZ              64

接收缓冲区:当串口使用中断接收模式打开时,串口驱动框架会根据 RT_SERIAL_RB_BUFSZ 大小开辟一块缓冲区用于保存接收到的数据,底层驱动接收到一个数据,都会在中断服务程序里面将数据放入缓冲区。

RT-Thread 提供的默认串口配置如下,即 RT-Thread 系统中默认每个串口设备都使用如下配置:

c
#define RT_SERIAL_CONFIG_DEFAULT           \
{                                          \
    BAUD_RATE_115200, /* 115200 bits/s */  \
    DATA_BITS_8,      /* 8 databits */     \
    STOP_BITS_1,      /* 1 stopbit */      \
    PARITY_NONE,      /* No parity  */     \
    BIT_ORDER_LSB,    /* LSB first sent */ \
    NRZ_NORMAL,       /* Normal mode */    \
    RT_SERIAL_RB_BUFSZ, /* Buffer size */  \
    0                                      \
}

在修改缓冲区大小时请注意,缓冲区大小无法动态改变,只有在 open 设备之前可以配置。open 设备之后,缓冲区大小不可再进行更改。但除缓冲区之外的其他参数,在 open 设备前 / 后,均可进行更改。

发送数据

c
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
参数描述
dev设备句柄
pos写入数据偏移量,此参数串口设备未使用
buffer内存缓冲区指针,放置要写入的数据
size写入数据的大小
返回写入数据的实际大小, 0需要读取当前线程的 errno 来判断错误状态

发送的回调函数

c
rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer));

回调函数由调用者提供,当硬件设备发送完数据时,由设备驱动程序回调这个函数并把发送完成的数据块地址 buffer 作为参数传递给上层应用。

接收回调函数

c
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));

若串口以中断接收模式打开,当串口接收到一个数据产生中断时,就会调用回调函数,并且会把此时缓冲区的数据大小放在 size 参数里,把串口设备句柄放在 dev 参数里供调用者获取。

串口以 DMA 接收模式打开,当 DMA 完成一批数据的接收后会调用此回调函数。

接收数据

c
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
参数描述
dev设备句柄
pos读取数据偏移量,此参数串口设备未使用
buffer缓冲区指针,读取的数据将会被保存在缓冲区中
size读取数据的大小
返回读到数据的实际大小, 0: 需要读取当前线程的 errno 来判断错误状态
c
static rt_device_t serial;                /* 串口设备句柄 */
static struct rt_semaphore rx_sem;    /* 用于接收消息的信号量 */

/* 接收数据的线程 */
static void serial_thread_entry(void *parameter)
{
    char ch;

    while (1)
    {
        /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
        while (rt_device_read(serial, -1, &ch, 1) != 1)
        {
            /* 阻塞等待接收信号量,等到信号量后再次读取数据 */
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        }
        /* 读取到的数据通过串口错位输出 */
        ch = ch + 1;
        rt_device_write(serial, 0, &ch, 1);
    }
}

关闭

c
rt_err_t rt_device_close(rt_device_t dev);

实际使用

使用中断的模式

串口中断接收及轮询发送序列图

  1. 在board.h文件里面
c
/*-------------------------- UART CONFIG BEGIN --------------------------*/

/** After configuring corresponding UART or UART DMA, you can use it.
 *
 * STEP 1, define macro define related to the serial port opening based on the serial port number
 *                 such as     #define BSP_USING_UART1
 *
 * STEP 2, according to the corresponding pin of serial port, define the related serial port information macro
 *                 such as     #define BSP_UART1_TX_PIN       "PA9"
 *                             #define BSP_UART1_RX_PIN       "PA10"
 *
 * STEP 3, if you want using SERIAL DMA, you must open it in the RT-Thread Settings.
 *                 RT-Thread Setting -> Components -> Device Drivers -> Serial Device Drivers -> Enable Serial DMA Mode
 *
 * STEP 4, according to serial port number to define serial port tx/rx DMA function in the board.h file
 *                 such as     #define BSP_UART1_RX_USING_DMA
 *
 */

#define BSP_USING_UART1
#define BSP_UART1_TX_PIN       "PA9"
#define BSP_UART1_RX_PIN       "PA10"

#define BSP_USING_UART2
#define BSP_UART1_TX_PIN       "PA2"
#define BSP_UART1_RX_PIN       "PA3"

image-20240128211934937

这个是老师的失误, 不需要, 需要的头文件rt_device.h

c
#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <rtdevice.h>
rt_device_t u2_dev;
rt_sem_t u2_sem;
rt_thread_t u2_thread;
rt_err_t rx_callback(rt_device_t dev, rt_size_t size)
{
    //一个回调函数, 主要是进行一个解锁
    rt_sem_release(u2_sem);
    return RT_EOK;
}

void u2_deal(void *parameter){
    int8_t ch;
    while(1){
        //进行读取, 有数据的话进行回显, 不然进入阻塞
        while(rt_device_read(u2_dev, -1, &ch, 1) != 1){
            rt_sem_take(u2_sem, RT_WAITING_FOREVER);
        }
        rt_device_write(u2_dev, -1, &ch, 1);
    }
}


int main(void)
{
    rt_err_t ret;
    //使用默认的配置
    struct serial_configure u1_config = RT_SERIAL_CONFIG_DEFAULT;;
    //初始化一个信号量, 以及一个处理线程
    u2_sem = rt_sem_create("u2_sem", 0, RT_IPC_FLAG_FIFO);
    u2_thread = rt_thread_create("u2_deal", u2_deal, RT_NULL, 512, 20, 20);
    //开始运行这一个线程
    rt_thread_startup(u2_thread);
    //寻找使用的USART设备
    u2_dev = rt_device_find("uart2");

    if(u2_dev == RT_NULL)
    {
        LOG_E("rt_device_find fail...\n");
        return -EINVAL;
    }
    //配置一下模式
    rt_device_control(u2_dev, RT_DEVICE_CTRL_CONFIG, (void *)&u1_config);
    //设置一个回调函数
    rt_device_set_rx_indicate(u2_dev, rx_callback);
    //打开一个USART用的是中断的接收的模式
    ret = rt_device_open(u2_dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
    if(ret<0)
    {
        LOG_E("rt_device_open fail...\n");
        return ret;
    }

    rt_device_write(u2_dev, 0, "Uart1 config ...\n", rt_strlen("Uart1 config ...\n"));
    return 0;
}

使用DMA的模式

c
/*
 * STEP 3, if you want using SERIAL DMA, you must open it in the RT-Thread Settings.
 *                 RT-Thread Setting -> Components -> Device Drivers -> Serial Device Drivers -> Enable Serial DMA Mode
 *
 * STEP 4, according to serial port number to define serial port tx/rx DMA function in the board.h file
 *                 such as     #define BSP_UART1_RX_USING_DMA
 *
 */

image-20240129205149106

之后直接关闭, 然后保存就可以了

  1. 定义一个宏定义

image-20240129210417498