STM32------串口理论篇
rt-thread------串口(二)发送篇
rt-thread------串口(三)接收篇
rt-thread 之 fal移植
rt-thread 之 生成工程模板
UART(Universal Asynchronous Receiver/Transmitter)通用异步收发传输器,UART 作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输,是在应用程序开发过程中使用频率最高的数据总线。我们经常在串口上接WIFI、4G模块,也可以增加电平转换芯片变成RS232和RS485通信接口。rt-thread官网对串口做了详细的说明。这篇文章的目的是讲一下我对串口发送接收模式的理解和使用过程中遇到的问题。
rt-thread串口配置步骤:
rt_device_find()
例:
static rt_device_t serial;
#define SAMPLE_UART_NAME "uart2"
serial = rt_device_find(SAMPLE_UART_NAME);
串口配置就是我们常见的串口参数配置
uart2_config.baud_rate = BAUD_RATE_115200; //修改波特率为 9600
uart2_config.data_bits = DATA_BITS_8; //数据位 8
uart2_config.stop_bits = STOP_BITS_1; //停止位 1
uart2_config.bufsz = 128; //修改缓冲区 buff size 为 128
uart2_config.parity = PARITY_NONE; //无奇偶校验位
这里有个接收缓存区的概念,其实就是一个环形队列,可以缓存串口接收的数据。当接收数据大于缓存区会日志输出告警信息。所以接收到的消息需要及时处理。
LOG_W("Warning: There is no enough buffer for saving data,"
" please increase the RT_SERIAL_RB_BUFSZ option.");
使用rt_device_control()
函数修改串口配置参数
struct serial_configure uart2_config = RT_SERIAL_CONFIG_DEFAULT; /* 初始化配置参数 */
/* step2:修改串口配置参数 */
uart2_config.baud_rate = BAUD_RATE_115200; //修改波特率为 9600
uart2_config.data_bits = DATA_BITS_8; //数据位 8
uart2_config.stop_bits = STOP_BITS_1; //停止位 1
uart2_config.bufsz = 128; //修改缓冲区 buff size 为 128
uart2_config.parity = PARITY_NONE; //无奇偶校验位
rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &uart2_config);
当发生中断(接收中断或者空闲中断)时,会调用注册的回调函数。用户可以在回调函数中写一些无阻塞的功能。虽然回调函数但是在中断中执行,所以不能有阻塞和延时。此回调函数在中断接收和DMA接收时实现的功能不一致。以下代码是中断接收时的参考。
/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
rt_sem_release(&rx_sem);
cnt++;
return RT_EOK;
}
/* 设置接收回调函数 */
rt_device_set_rx_indicate(serial, uart_input);
打开设备时可以对串口的模式进行配置,rt-thread提供三种模式:中断模式、轮询模式、DMA 模式。发送和接收只能从中选一个,发送和接收可以配置的不一致。默认使用中断接收和轮训发送。
注:其中UART版本1中STM32中断发送模式有问题
,推荐使用V2版本。
/* 以中断接收及轮询发送模式打开串口设备 */
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
oflag支持模式参数为下列几个:
#define RT_DEVICE_FLAG_INT_RX 0x100 /**< INT mode on Rx 中断接收 */
#define RT_DEVICE_FLAG_DMA_RX 0x200 /**< DMA mode on Rx DMA接收 */
#define RT_DEVICE_FLAG_INT_TX 0x400 /**< INT mode on Tx 中断发送 */
#define RT_DEVICE_FLAG_DMA_TX 0x800 /**< DMA mode on Tx DMA接收 */
rt_device_open函数中初始化DMA模式会进行参数检查,需要确认使用串口DMA宏被定义。如:BSP_UART2_RX_USING_DMA
,否则会rt_serial_open()
返回错误
/* check device flag with the open flag */
if ((oflag & RT_DEVICE_FLAG_DMA_RX) && !(dev->flag & RT_DEVICE_FLAG_DMA_RX))
return -RT_EIO;
if ((oflag & RT_DEVICE_FLAG_DMA_TX) && !(dev->flag & RT_DEVICE_FLAG_DMA_TX))
return -RT_EIO;
if ((oflag & RT_DEVICE_FLAG_INT_RX) && !(dev->flag & RT_DEVICE_FLAG_INT_RX))
return -RT_EIO;
if ((oflag & RT_DEVICE_FLAG_INT_TX) && !(dev->flag & RT_DEVICE_FLAG_INT_TX))
return -RT_EIO;
当对应串口DMA宏在rtconfig.h中被定义后,通过宏定义设置到对应串口对象的uart_dma_flag
参数中,在串口初始化阶段通过rt_hw_usart_init()
中的rt_hw_serial_register(
)函数会把对应uart_dma_flag设置到串口dev->flag中,后续串口open时只是检查一下参数。
/* register UART device */
result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
RT_DEVICE_FLAG_RDWR
| RT_DEVICE_FLAG_INT_RX
| RT_DEVICE_FLAG_INT_TX
| uart_obj[i].uart_dma_flag
, NULL);
完整的串口配置例程代码如下所示,其实现的功能是串口收到数据后回传。
/*
* 程序清单:这是一个 串口 设备使用例程
* 例程导出了 uart_sample 命令到控制终端
* 命令调用格式:uart_sample uart2
* 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备
* 程序功能:通过串口输出字符串"hello RT-Thread!",然后错位输出输入的字符
*/
#include
#include "module_uart.h"
#include "drv_usart.h"
#define SAMPLE_UART_NAME "uart2"
struct serial_configure uart2_config = RT_SERIAL_CONFIG_DEFAULT; /* 初始化配置参数 */
/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial;
uint16_t cnt = 0;
/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
rt_sem_release(&rx_sem);
cnt++;
return RT_EOK;
}
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);
}
}
int uart2_init(void)
{
rt_err_t ret;
/* 查找系统中的串口设备 */
serial = rt_device_find(SAMPLE_UART_NAME);
if (!serial)
{
rt_kprintf("find %s failed!\n", SAMPLE_UART_NAME);
return RT_ERROR;
}
/* step2:修改串口配置参数 */
uart2_config.baud_rate = BAUD_RATE_115200; //修改波特率为 9600
uart2_config.data_bits = DATA_BITS_8; //数据位 8
uart2_config.stop_bits = STOP_BITS_1; //停止位 1
uart2_config.bufsz = 128; //修改缓冲区 buff size 为 128
uart2_config.parity = PARITY_NONE; //无奇偶校验位
rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &uart2_config);
/* 初始化信号量 */
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
/* 以中断接收及轮询发送模式打开串口设备 */
rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
/* 设置接收回调函数 */
rt_device_set_rx_indicate(serial, uart_input);
/* 创建 serial 线程 */
rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
/* 创建成功则启动线程 */
if (thread != RT_NULL)
{
rt_thread_startup(thread);
}
else
{
ret = RT_ERROR;
}
return ret;
}
///* 导出到 msh 命令列表中 */
//MSH_CMD_EXPORT(uart_sample, uart device sample);