RT-Thread OS 串口应用程序(中断接收,轮询发送)

UART 串口的特点是将数据一位一位地顺序传送,只要 2 根传输线就可以实现双向通信,一根线发送数据的同时用另一根线接收数据。UART 串口通信有几个重要的参数,分别是波特率、起始位、数据位、停止位和奇偶检验位,对于两个使用 UART 串口通信的端口,这些参数必须匹配,否则通信将无法正常完成。UART 串口传输的数据格式如下图所示:

RT-Thread OS 串口应用程序(中断接收,轮询发送)_第1张图片

应用程序通过 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太大了

你可能感兴趣的:(RT-Thread,OS学习,stm32)