UART 串口的特点是将数据一位一位地顺序传送,只要 2 根传输线就可以实现双向通信,一根线发送数据的同时用另一根线接收数据。UART 串口通信有几个重要的参数,分别是波特率、起始位、数据位、停止位和奇偶检验位,对于两个使用 UART 串口通信的端口,这些参数必须匹配,否则通信将无法正常完成。UART 串口传输的数据格式如下图所示:
应用程序通过 RT-Thread提供的 I/O 设备管理接口来访问串口硬件,相关接口如下所示:
函数 | 描述 |
---|---|
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() | 关闭设备 |
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
oflags 参数支持下列取值 (可以采用或的方式支持多种取值):
#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 发送模式 */
/* 以中断接收及轮询发送模式打开串口设备 */
rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
/* 以 DMA 接收及轮询发送模式打开串口设备 */
rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX);
通过控制接口,应用程序可以对串口设备进行配置,如波特率、数据位、校验位、接收缓冲区大小、停止位等参数的修改。控制函数如下所示:
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 为空 |
其他错误码 | 执行失败 |
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 初始化配置参数 */
初始化默认配置115200
向串口中写入数据,可以通过如下函数完成:
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
参数 | 描述 |
---|---|
dev | 设备句柄 |
pos | 写入数据偏移量,此参数串口设备未使用,一般写0 |
buffer | 内存缓冲区指针,放置要写入的数据 |
size | 写入数据的大小 |
返回 | —— |
写入数据的实际大小 | 如果是字符设备,返回大小以字节为单位; |
0 | 需要读取当前线程的 errno 来判断错误状态 |
rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer));
当串口接收到数据时,自动调用回调函数,释放信号量,让获取该信号的线程执行,读取数据。
可调用如下函数读取串口接收到的数据:
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
复制
本次通信使用两块stm32开发板,均使用uart2来连接通信,其中接收方运行rt-thread os。
发送方每隔一秒发送一次数据,发送方是裸机程序。
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
unsigned char buf[10] ;
unsigned char TData[]={"hello world"};
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
// UART_HandleTypeDef huart2;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
printf("UART TEST...\n");
HAL_UART_Receive_IT( &huart2, buf, 4); //要进入中断接收,要先在初始化时运行该指令
HAL_TIM_Base_Start_IT(&htim1); // 开启定时器1中断
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
//定时器1回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 定时1S
HAL_UART_Transmit( &huart2, TData, sizeof(TData),0xfff);
}
#include
#include
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include
rt_device_t uart_demo;
struct rt_semaphore uart_rx_sem;
rt_thread_t read_th;
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 初始化串口配置参数 */
rt_err_t rx_callback(rt_device_t dev,rt_size_t size)
{
/*串口接收到数据后,马上释放信号量,以便读取数据的线程运行*/
// LOG_D("rx_callback runing..\n");
rt_sem_release(&uart_rx_sem);
return RT_EOK;
}
void uart_rx_thread_entry(void *parameter)
{
//线程创建时分配空间是1024,不要在线程中定义太大的变量,否则系统运行时会出错
char ch ;
LOG_D("uart_rx_thread_entry runing..\n");
while(1){
while (rt_device_read(uart_demo, 0, &ch, 1) != 1)
{
/* 阻塞等待接收信号量,等到信号量后再次读取数据 */
rt_sem_take(&uart_rx_sem, RT_WAITING_FOREVER);
}
rt_kprintf("%c",ch);
}
}
int main(void)
{
rt_err_t ret = 0;
char buffer[] = {"@Play,6666,$"};
/*1.首先查找串口设备获取设备句柄。*/
uart_demo = rt_device_find("uart2");//通过设备名字获取设备句柄
if(uart_demo == RT_NULL){
LOG_E("rt_device_find failed[uart]...\n");
return RT_EEMPTY;
}
/*2.创建信号量,在串口接收回调函数中释放信号量,然后以读写及中断接收方式打开串口设备*/
ret = rt_sem_init(&uart_rx_sem, "uart_sem", 0, RT_IPC_FLAG_FIFO);
if(ret != RT_EOK){
LOG_E("rt_sem_init failed...\n");
}
/* 3. 配置串口波特率等参数*/
// config.baud_rate = 9600; //默认配置115200
rt_device_control(uart_demo, RT_DEVICE_CTRL_CONFIG, &config);
rt_device_open(uart_demo,RT_DEVICE_FLAG_INT_RX );
/*4.设置串口设备的接收回调函数,之后发送字符串,并创建读取数据线程。*/
rt_device_set_rx_indicate(uart_demo, rx_callback);
rt_device_write(uart_demo, 0, buffer, sizeof(buffer)-1);
read_th = rt_thread_create("rx_data", uart_rx_thread_entry, NULL, 1024, 5, 5);
if(read_th == RT_NULL){
LOG_E("rt_thread_create failedread_th...\n");
}
rt_thread_startup(read_th);
return RT_EOK;
}
运行结果:
[2022-10-30_15:57:48:549]hello world hello world
[2022-10-30_15:57:50:246] \ | /
[2022-10-30_15:57:50:246]- RT - Thread Operating System
[2022-10-30_15:57:50:246] / | \ 4.0.3 build Oct 29 2022
[2022-10-30_15:57:50:246] 2006 - 2020 Copyright by rt-thread team
[2022-10-30_15:57:50:246][0m[D/main] uart_rx_thread_entry runing..
[2022-10-30_15:57:50:246][0m
[2022-10-30_15:57:50:246]msh >hello world hello world hello world hello world hello world hello world hello world hello world hello world
1. 首先查找串口设备获取设备句柄。
uart_demo = rt_device_find("uart2"); //通过设备名字获取设备句柄
2. 创建信号量,用于串口接收回调函数中释放信号量,
ret = rt_sem_init(&uart_rx_sem, "uart_sem", 0, RT_IPC_FLAG_FIFO);
3. 设置串口通信参数,如波特率,数据位等
// config.baud_rate = 9600; //默认配置115200
rt_device_control(uart_demo, RT_DEVICE_CTRL_CONFIG, &config);
4.. 然后以读写及中断接收方式打开串口设备
rt_device_open(uart_demo,RT_DEVICE_FLAG_INT_RX );
5. 设置串口设备的接收回调函数,在此回调函数中释放信号量。
rt_device_set_rx_indicate(uart_demo, rx_callback);
读取数据线程会尝试读取一个字符数据,如果没有数据则会挂起并等待信号量,当串口设备接收到一个数据时会触发中断并调用接收回调函数,此函数会发送信号量唤醒线程,此时线程会马上读取接收到的数据。
6. 创建读取数据线程,获取信号量,读取数据
read_th = rt_thread_create("rx_data", uart_rx_thread_entry, NULL, 1024, 5, 5);
线程创建时分配空间是1024,不要在线程中定义太大的变量,否则系统运行时会出错
比如在线程入口函数中定义 char buffer[1024],这是不行的。buffer太大了