串口
串口简介
UART(Universal Asynchronous Receiver/Transmitter)通用异步收发传输器,UART 作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。是在应用程序开发过程中使用频率最高的数据总线。
UART 串口的特点是将数据一位一位地顺序传送,只要 2 根传输线就可以实现双向通信,一根线发送数据的同时用另一根线接收数据。UART 串口通信有几个重要的参数,分别是波特率、起始位、数据位、停止位和奇偶检验位,对于两个使用 UART 串口通信的端口,这些参数必须匹配,否则通信将无法正常完成。
关于串口详细说明可查看RT-Thread官网的介绍:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart_v1/uart
串口驱动
在IoTE开发板中,串口设备有两个,按照系统默认功能分为AT串口"uart0"
和调试串口"uart1"
,menuconfig相关配置路径如下:
Windows环境:利用RT-Thread官方env工具直接在代码根目录下执行menuconfig进入配置。
Linux环境:在代码根目录下执行scons --menuconfig进入配置。
AT串口"uart0"
:
RT-Thread Components -> Network -> AT commands -> Enable AT commands server
调试串口"uart1"
:
RT-Thread Kernel -> Kernel Device Object -> Using console for rt_kprintf
注:这里区分两个串口功能,仅仅是软件默认配置不同,实际使用时可随意修改。
访问串口
访问串口,库中提供了以下接口:
函数 | 描述 |
---|---|
rt_device_find() | 查找设备 |
rt_device_control() | 控制设备 |
rt_device_open() | 打开设备 |
rt_device_read() | 读取数据 |
rt_device_write() | 写入数据 |
rt_device_set_rx_indicate() | 设置接收回调函数 |
rt_device_set_tx_complete() | 设置发送完成回调函数 |
rt_device_close() | 关闭设备 |
查找设备
查找串口设备,函数原型如下所示:
rt_device_t rt_device_find(const char* name);
-
参数
[IN]name:串口设备名称,目前注册到的系统中的串口设备名称为"uart0"或"uart1"。
-
返回值
RT_NULL:失败,设备不存在。
非RT_NULL:成功,该返回值是操作串口设备的句柄,在后续的接口中作为函数入参。
控制设备
控制串口设备,函数原型如下所示:
rt_err_t rt_device_control(rt_device_t dev, int cmd, void* arg);
-
参数
[IN]dev:串口设备句柄,查找设备函数的返回值
[IN]cmd:命令控制字,在这里主要支持以下宏定义:
// 宏定义所在文件相对路径: // rt-thread/include/rtdef.h #define RT_DEVICE_CTRL_CONFIG 0x03 // 设置配置
[INOUT]arg:对应结构体为struct serial_configure,
// 结构体定义所在路径为: // rt-thread\components\drivers\include\drivers\serial.h 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; // buf大小 rt_uint32_t reserved : 6; // 保留 }; // 关于RT-Thread定义的宏包括如下: #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_600000 600000 #define BAUD_RATE_750000 750000 #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 #ifdef _WIN32 #include <windows.h> #else #define PARITY_NONE 0 #define PARITY_ODD 1 #define PARITY_EVEN 2 #endif #define BIT_ORDER_LSB 0 #define BIT_ORDER_MSB 1 #define NRZ_NORMAL 0 /* Non Return to Zero : normal mode */ #define NRZ_INVERTED 1 /* Non Return to Zero : inverted mode */ #ifndef RT_SERIAL_RB_BUFSZ #define RT_SERIAL_RB_BUFSZ 64 #endif #define RT_SERIAL_EVENT_RX_IND 0x01 /* Rx indication */ #define RT_SERIAL_EVENT_TX_DONE 0x02 /* Tx complete */ #define RT_SERIAL_EVENT_RX_DMADONE 0x03 /* Rx DMA transfer done */ #define RT_SERIAL_EVENT_TX_DMADONE 0x04 /* Tx DMA transfer done */ #define RT_SERIAL_EVENT_RX_TIMEOUT 0x05 /* Rx timeout */ #define RT_SERIAL_DMA_RX 0x01 #define RT_SERIAL_DMA_TX 0x02 #define RT_SERIAL_RX_INT 0x01 #define RT_SERIAL_TX_INT 0x02 #define RT_SERIAL_ERR_OVERRUN 0x01 #define RT_SERIAL_ERR_FRAMING 0x02 #define RT_SERIAL_ERR_PARITY 0x03 #define RT_SERIAL_TX_DATAQUEUE_SIZE 2048 #define RT_SERIAL_TX_DATAQUEUE_LWM 30
-
返回值
0,正常
其他:错误,RT-Thread定义的错误类型。
打开设备
打开串口设备,函数原型如下所示:
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag);
-
参数
[IN]dev:串口设备句柄,查找设备函数的返回值。
[IN]oflag:打开设备模式标记,串口设备驱动框架支持如下模式,需要多个模式同时使用,可用 "|",如:
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX
,支持的定义值如下:#define RT_DEVICE_FLAG_RDONLY 0x001 // 只读 #define RT_DEVICE_FLAG_WRONLY 0x002 // 只写 #define RT_DEVICE_FLAG_RDWR 0x003 // 读写 #define RT_DEVICE_FLAG_INT_RX 0x100 // 接收中断模式 #define RT_DEVICE_FLAG_INT_TX 0x400 // 发送中断模式 #define RT_DEVICE_FLAG_STREAM 0x040 // 流模式
-
返回值
0:正常。
其他:错误,RT-Thread定义的错误类型。
读取数据
串口读取数据,函数原型如下所示:
rt_size_t rt_device_read(rt_device_t dev,
rt_off_t pos,
const void* buffer,
rt_size_t size);
-
参数
[IN]dev:串口设备句柄,查找设备函数的返回值。
[IN]pos:读取数据偏移,这里没有使用。
[IN]buffer:读取数据的指针。
[IN]size:读取数据的长度,不能超过buffer指针指向的空间的长度。
-
返回值
0:读取失败。
其他:实际读取的串口消息大小。
写入数据
使用串口设备(写入)发送数据,函数原型如下所示:
rt_size_t rt_device_write(rt_device_t dev,
rt_off_t pos,
void* buffer,
rt_size_t size);
-
参数
[IN]dev:串口设备句柄,查找设备函数的返回值。
[IN]pos:发送数据偏移,这里没有使用。
[IN]buffer:发送数据的指针。
[IN]size:发送数据的长度,buffer指针指向数据内容的长度。
-
返回值
0:发送失败。
其他:实际发送的串口消息大小。
设置接收回调函数
可以通过如下函数来设置数据接收指示,当串口收到数据时,通知上层应用线程有数据到达,函数原型如下所示:
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));
-
参数
[IN]dev:串口设备句柄,查找设备函数的返回值。
[IN]rx_ind:回调函数指针。
(回调函数)dev:设备句柄,与前面dev是同一个。
(回调函数)size:缓冲区数据大小。
-
返回值
成功:0
设置发送完成回调函数
在调用 rt_device_write()
写入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。这个回调函数会在底层硬件数据发送完成后 (例如 DMA 传送完成或 FIFO 已经写入完毕产生完成中断时) 调用。可以通过如下函数设置设备发送完成指示 :
rt_err_t rt_device_set_tx_complete(
rt_device_t dev,
rt_err_t (*tx_done)(rt_device_t dev,void *buffer));
-
参数
[IN]dev:串口设备句柄,查找设备函数的返回值。
[IN]tx_done:回调函数指针。
(回调函数)dev:设备句柄,与前面dev是同一个。
(回调函数)buffer:缓冲区数据。
-
返回值
成功:0
调用这个函数时,回调函数由调用者提供,当硬件设备发送完数据时,由设备驱动程序回调这个函数并把发送完成的数据块地址 buffer 作为参数传递给上层应用。上层应用(线程)在收到指示时会根据发送 buffer 的情况,释放 buffer 内存块或将其作为下一个写数据的缓存。
关闭设备
当设备完成操作后,可以关闭设备,函数原型如下:
rt_err_t rt_device_close(rt_device_t dev);
-
参数
[IN]dev:串口设备句柄,查找设备函数的返回值。
-
返回值
0,正常
其他:错误,RT-Thread定义的错误类型。
注:若设备已经完全关闭,不能重复关闭设备。
串口使用示例
使用示例需要配置menuconfig,使能例程与外设,步骤如下:
- 使能例程
Application Example Config -> Enable UART Example
- 使能外设驱动
Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable UART
以下提供串口测试实例,测试步骤如下:
- 根据串口设备名称,获取设备句柄。
- 设置串口配置。
- 打开串口。
- 发送数据。
- 接收数据。
#include <rtthread.h>
#ifdef APP_EXAMPLE_UART
#ifndef RT_USING_SERIAL
#error "Please enable rt-thread serial device driver"
#endif
#ifndef BSP_USING_UART
#error "Please enable on-chip peripheral uart config"
#endif
#ifndef BSP_USING_UART1
#error "Please enable on-chip peripheral uart1 config"
#endif
#include <rtdevice.h>
#include "uc_uart_app.h"
#define THREAD_STACK_SIZE 512
#define THREAD_PRIORITY 5
#define THREAD_TIMESLICE 5
#define UART_NAME "uart1"
#define UART_BAUD_RATE 9600
// #define USE_RS485
#ifdef USE_RS485
#define RS485_CTRL_PIN 17
#define RS485_TX() rt_pin_write(RS485_CTRL_PIN, PIN_HIGH)
#define RS485_RX() rt_pin_write(RS485_CTRL_PIN, PIN_LOW)
#endif
// #define USE_UART_RINGBUFF
#ifdef USE_UART_RINGBUFF
#define RINGBUFF_SIZE 512
static struct rt_ringbuffer *uart_ringbuffer = RT_NULL;
#endif
/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial;
static rt_size_t uart_output(rt_device_t dev, const void *buffer, rt_size_t size)
{
rt_size_t ret = 0;
#ifdef USE_RS485
RS485_TX();
#endif
ret = rt_device_write(dev, 0, buffer, size);
#ifdef USE_RS485
RS485_RX();
#endif
return ret;
}
/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
#ifdef USE_UART_RINGBUFF
uint8_t data_len = 0;
uint8_t uart_recv_buf[64];
/* 接收到数据后存入 ringbuffer */
data_len = rt_device_read(dev, 0, uart_recv_buf, size);
if (data_len > 0)
{
rt_ringbuffer_put(uart_ringbuffer, (const rt_uint8_t *)uart_recv_buf, data_len);
}
#endif
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
rt_sem_release(&rx_sem);
return RT_EOK;
}
static void uart_thread_entry(void *parameter)
{
char rx_buf[64];
while (1)
{
uint8_t data_len = 0;
/* 阻塞等待接收信号量,等到信号量后再次读取数据 */
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
rt_memset(rx_buf, 0x00, 64);
#ifdef USE_UART_RINGBUFF
data_len = rt_ringbuffer_get(uart_ringbuffer, (uint8_t *)rx_buf, 64);
#else
data_len = rt_device_read(serial, 0, rx_buf, 64);
#endif
if (data_len > 0)
{
uart_output(serial, rx_buf, data_len);
}
}
}
int uart_app_sample(void)
{
rt_thread_t thread = RT_NULL;
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 初始化配置参数 */
char str[] = "uart app sample\n";
#ifdef USE_RS485
rt_pin_mode(RS485_CTRL_PIN, PIN_MODE_OUTPUT);
RS485_RX();
#endif
/* step1:查找系统中的串口设备 */
serial = rt_device_find(UART_NAME);
if (serial == RT_NULL)
{
rt_kprintf("find %s failed!\n", UART_NAME);
return RT_ERROR;
}
/* step2:修改串口配置参数 */
config.baud_rate = UART_BAUD_RATE; // 修改波特率 9600
// config.data_bits = DATA_BITS_8; // 数据位 8
// config.stop_bits = STOP_BITS_1; // 停止位 1
// config.bufsz = 128; // 修改缓冲区 buff size 为 128
// config.parity = PARITY_NONE; // 无奇偶校验位
/* step3:控制串口设备。通过控制接口传入命令控制字,与控制参数 */
rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
#ifdef USE_UART_RINGBUFF
uart_ringbuffer = rt_ringbuffer_create(RINGBUFF_SIZE);
if (uart_ringbuffer == RT_NULL)
{
rt_kprintf("rt_ringbuffer_create fail!!\n");
return RT_ERROR;
}
#endif
/* 初始化信号量 */
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
/* 以中断接收及轮询发送模式打开串口设备 */
rt_device_open(serial, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
/* 设置接收回调函数 */
rt_device_set_rx_indicate(serial, uart_input);
/* 发送字符串 */
uart_output(serial, str, rt_strlen(str));
/* 创建 serial 线程 */
thread = rt_thread_create("serial",
uart_thread_entry,
RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY,
THREAD_TIMESLICE);
/* 创建成功则启动线程 */
if (RT_NULL == thread)
{
return -RT_ERROR;
}
else
{
rt_thread_startup(thread);
}
return RT_EOK;
}
#endif