打印
[应用相关]

STM32CubeMX在FreeRTOS下使用串口进行数据收发

[复制链接]
3882|31
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
观海|  楼主 | 2021-8-3 15:26 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
STM32CubeMX->FreeRTOS+USART接收不定长数据
由于本人做的一个项目功能相对复杂,要求使用操作系统,且项目工程中有很多需要串口操作的外设,所以需要对串口设计不定长的收发功能,裸机跑惯了的孩子就是比较野,一天瞎吉尔弄,现在是不是GG,这里先批评一下自己。为了解决这一问题并做个笔记,就有了这篇博客。

实现思路:
​ 使用串口、定时器记录接收到的数据,每5ms进入一次处理函数(一条数据处理完毕)
​ 使用一个计数位、一个标志位、一个暂存buff
​ 标志位置1则认为数据处理完成,将数据打印出来
使用硬件功能:串口3及中断,定时器7及中断
使用操作系统元素:一个互斥量
使能RTOS后会RTOS会强制调用系统时钟,所以在使能了操作系统之后需要将系统时钟换为定时器TIM1

操作步骤
1.启动STM32CubeMX并对芯片选型
主要设置

  • 串口3及中断使能,115200,8,None,1
  • 定时器7及中断使能,预分频9000-1,自动重载50-1(时钟树APB1、2为90MHz)
  • SYS时钟源设为定时器1
  • 使能FreeRTOS,创建1个串口任务、创建1个互斥量
  • 使能两个GPIO输出驱动LED

以上都清楚的大佬转到代码功能实现继续
2.使能RCC时钟源为外部时钟




使用特权

评论回复
沙发
观海|  楼主 | 2021-8-3 15:27 | 只看该作者

3.设置SYS调试方式为串口,时钟源改为定时器1


使用特权

评论回复
板凳
观海|  楼主 | 2021-8-3 15:37 | 只看该作者
4.设置时钟树,是APB1、2为90MHz,或者自行设定,在后面定时器工作会用到

使用特权

评论回复
地板
观海|  楼主 | 2021-8-3 15:38 | 只看该作者

5.使能串口3,并启用串口3中断,串口3设置为115200,8Bit,None,1(不知道什么意思的就没有必要看下去了)


使用特权

评论回复
5
观海|  楼主 | 2021-8-3 15:41 | 只看该作者

6. 使能定时器7,并使能中断,定时器预分频系数与之前的90MHz相关,需要通过预分频系数和自动重载值将定时时间设为5ms【200Hz】,不会算的看我定时器博客(这里我用了一个不常用的定时器,用哪个都可以,只要注意好定时器隶属哪根时钟线就行)


使用特权

评论回复
6
观海|  楼主 | 2021-8-3 15:41 | 只看该作者

7. 使能FreeRTOS并创建一个串口任务和一个互斥量(互斥量用于串口数据接收完成后进入串口任务)



使用特权

评论回复
7
观海|  楼主 | 2021-8-3 15:42 | 只看该作者

8. 设置两个IO输出作为一个可视化依据


9. 项目命名、选择使用的程序编辑软件我的是MDK_ARM V5、分离.c.h文件,点击右上角GENERATE CODE生成工程

  • 打开项目,先编译没有错误之后再进行修改

使用特权

评论回复
8
观海|  楼主 | 2021-8-3 16:32 | 只看该作者
代码功能实现
首先在usart.c文件对串口3进行printf函数重定义,使串口3可用printf函数输出

内容加入到/* USER CODE BEGIN 0 */框架内

/* USER CODE BEGIN 0 */
/********************************************************************************/
#include "stdio.h"
//加入以下代码,支持printf函数,而不需要选择use MicroLIB          
//#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)       
#if 1
#pragma import(__use_no_semihosting)            
//标准库需要的支持函数                 
struct __FILE
{
        int handle;
};

FILE __stdout;      
//定义_sys_exit()以避免使用半主机模式   
void _sys_exit(int x)
{
        x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{        
        while((USART3->SR&0X40)==0);//循环发送,直到发送完毕   
        USART3->DR = (uint8_t) ch;      
        return ch;
}
#endif
/********************************************************************************/
/* USER CODE END 0 */



定义串口使用的变量

主要包括串口数据存储BUFF,串口数据长度计数BUFF,串口接收成功标志位(此处借鉴正点原子的串口处理方法)

在main.c文件下的定义/* USER CODE BEGIN PV */框架内定义

/* USER CODE BEGIN PV */
/********************************************************************************/
uint8_t RxBuffer[MAX_REC_LENGTH] = {0};                //串口数据存储BUFF                长度2048
uint8_t RxFlag = 0;                                                        //串口接收完成标志符
uint16_t RxCounter = 0;                                                //串口长度计数
uint8_t RxTemp[REC_LENGTH] = {0};                        //串口数据接收暂存BUFF        长度1

extern osSemaphoreId myBinarySem01Handle;        //操作系统定义的互斥量
/********************************************************************************/
/* USER CODE END PV */


并在usart.h文件下的/* USER CODE BEGIN Private defines */框架下定义长度和链接串口用变量

/* USER CODE BEGIN Private defines */
/********************************************************************************/
#define REC_LENGTH 1
#define MAX_REC_LENGTH 2048

extern uint8_t RxBuffer[MAX_REC_LENGTH];
extern uint8_t RxFlag;
extern uint16_t RxCounter;
extern uint8_t RxTemp[REC_LENGTH];
/********************************************************************************/         
/* USER CODE END Private defines */



使用特权

评论回复
9
观海|  楼主 | 2021-8-3 16:33 | 只看该作者
串口中断回调函数,在main.c的/* USER CODE BEGIN 4 */框架下添加串口回调函数

/********************************************************************************/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口3接收完成回调函数
{
        if(huart->Instance == USART3)
        {
                __HAL_TIM_SET_COUNTER(&htim7,0);                                                        //清除定时器7计数值       
                if(0 == RxCounter)                                                                                        //如果是首字符(每帧数据开头)则开启定时器
                {
                        __HAL_TIM_CLEAR_FLAG(&htim7, TIM_FLAG_UPDATE);                        //清除中断标志位
                        HAL_TIM_Base_Start_IT(&htim7);                                                        //开启基本定时器
                }
                RxBuffer[RxCounter] = RxTemp[0];                                                        //缓存数据放入接收数组
                RxCounter++;                                                                                                //计数器加1
                HAL_UART_Receive_IT(&huart3,(uint8_t *)RxTemp, REC_LENGTH);        //重新使能中断
        }
}
/********************************************************************************/




定时器周期回调函数,由于在设置中系统时钟使用了定时器1,所以在main.c文件存在

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)



在函数 /* USER CODE BEGIN Callback 1 */框架下添加定时器7的回调函数

  /* USER CODE BEGIN Callback 1 */
        /********************************************************************************/
        if(htim->Instance == TIM7)
        {
                HAL_GPIO_TogglePin(DS1_GPIO_Port, DS1_Pin);        //LED反转一次
                RxFlag = 1;                                                                                                                                        //接收标志位置1
                HAL_TIM_Base_Stop_IT(&htim7);                                                                //关闭定时器
                osSemaphoreRelease(myBinarySem01Handle);                //释放二值信号量,进入串口任务
        }
        /********************************************************************************/
  /* USER CODE END Callback 1 */



对freeRTOS添加任务

首先在freertos.c文件的/* USER CODE BEGIN Includes */ 框架下添加串口使用的头文件

/* USER CODE BEGIN Includes */     
/********************************************************************************/
#include "usart.h"
#include "stdio.h"
/********************************************************************************/
/* USER CODE END Includes */


使用特权

评论回复
10
观海|  楼主 | 2021-8-3 16:34 | 只看该作者
在freertos主任务中启动串口中断以及添加LED闪烁任务

/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN StartDefaultTask */
        HAL_UART_Receive_IT(&huart3,(uint8_t *)RxTemp, REC_LENGTH);
  /* Infinite loop */
  for(;;)
  {
        HAL_GPIO_TogglePin(DS0_GPIO_Port,DS0_Pin);
    osDelay(200);
  }
  /* USER CODE END StartDefaultTask */
}



在freertos串口子任务中添加串口数据打印功能

/* USER CODE END Header_Start_U3_Task */
void Start_U3_Task(void const * argument)
{
  /* USER CODE BEGIN Start_U3_Task */
  /* Infinite loop */
  for(;;)
  {
        /********************************************************************************/  
        osSemaphoreWait(myBinarySem01Handle,100);                                                //等待二值信号量
        if(RxFlag == 1)                                                                                                        //数据接收完成
        {
                for(int i = 0; i<RxCounter; i++)                                                        //打印接收数组存储的内容
                        printf("%c",RxBuffer[i]);       
         printf("\r\n");                                                                                        //打印完成换行
                RxFlag = 0;                                                                                                        //接收标志清零
                RxCounter = 0;                                                                                                //接收计数清零
                memset(RxBuffer ,0, MAX_REC_LENGTH);                                                //清空接收数组
        }
        /********************************************************************************/
    osDelay(1);
  }
  /* USER CODE END Start_U3_Task */
}


使用特权

评论回复
11
观海|  楼主 | 2021-8-3 16:54 | 只看该作者
实验结果
编译下载程序,打开串口调试助手,用串口转USB模块将串口3接入电脑,使用串口调试助手测试

上图为测试结果,发送ASWaterbenben后回复相同内容,每帧数据用换行符隔开,至此,实验成功!

DS0灯200ms反转一次,每次接收到一帧完整数据DS1灯就会反转一次。这个效果难以展示,你们自己试试就好!


使用特权

评论回复
12
51xlf| | 2021-8-4 20:26 | 只看该作者
用FreeRTOS+DMA吧  

使用特权

评论回复
13
i1mcu| | 2021-8-4 20:26 | 只看该作者
freertos有多少个api函数

使用特权

评论回复
14
pmp| | 2021-8-4 20:26 | 只看该作者
怎么设置freertos的最大任务数  

使用特权

评论回复
15
mmbs| | 2021-8-4 20:26 | 只看该作者
freertos能用全局变量吗

使用特权

评论回复
16
1988020566| | 2021-8-4 20:27 | 只看该作者
stm32 freertos 之串口中断   

使用特权

评论回复
17
lzbf| | 2021-8-4 20:27 | 只看该作者
你要带FreeRTOS的  

使用特权

评论回复
18
youtome| | 2021-8-4 20:28 | 只看该作者
使用MODBUS-RTU标准协议通过串口进行通信  

使用特权

评论回复
19
cemaj| | 2021-8-4 20:28 | 只看该作者
FreeRTOS多任务共用串口?

使用特权

评论回复
20
jimmhu| | 2021-8-4 20:29 | 只看该作者
FreeRTOS中串口接收数据中断  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

99

主题

4093

帖子

1

粉丝