前言:本文基于之前的标准库的写的 freeRTOS小项目练习—智能门锁 用CubeMx进行移植,以此学习CubeMX和HAL库。
目录
一、项目的基本配置
1、RCC中选择外置晶振
2、SYS中选定Debug方法,Serial Wire
3、配置系统频率,同时要选定外部晶振源HSE、使能CSS。
4、Project Manager
- 工程名以及保存路径不能含中文
- 选定自己的编译器Toolchain/IDE
- Code Generator中选定用到什么库就复制什么库;选择.c/.h文件分别生成
5、GENERATE CODE
二、配置相关项
1、配置串口printf重定向:记得开启 MicroLIB
#include "usart.h"
/* USER CODE BEGIN 0 */
#include <stdio.h>
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);
return (ch);
}
/* USER CODE END 0 */
UART_HandleTypeDef huart1;
2、freeRTOS的配置;
MDK的编译器不能使用V6版本会报错:
我这里是将ARM_CM3进行替换(因为我用的103ZET6是Cortex-M3的核),而非ARM_CM4F(ARM_CM4F目录,是针对Cortex-M4 FPU功能的),查看port.c的引用路局为可以发现为ARM_CM3
【Keil】CubeMX配置的FreeRTOS利用V6编译出错_ac6 freertos编译不通过_米杰的声音的博客-CSDN博客
参考文章:
FreeRTOS+CubeMX系列第一篇——初识FreeRTOS_cubemx freertos_冬瓜~的博客-CSDN博客
CubeMX使用FreeRTOS编程指南_cubemx freertos_Top嵌入式的博客-CSDN博客
继续配置:
- Config parameters 堆栈大小设置TOTAL_HEAPS = 15360
- Advanced settings :Enabled Newlib settings
- V10版本的流缓冲区和消息缓冲区(用于数据量大长度不定)
任务优先级的理解 任务优先级分配和修改 这样freeRTOS设置了56个优先级对应配置中的MAX_PRIORITIES FreeRTOS 任务优先级问题
补充:中断优先级和任务优先级区别
初学者也容易在这两个概念上面出现问题。 简单的说,这两个之间没有任何关系,不管中断的优先级是多少,中断的优先级永远高于任何任务的优先级,即任务在执行的过程中,中断来了就开始执行中断服务程序。另外对于 STM32F103,F407 和 F429 来说,中断优先级的数值越小,优先级越高。 而 FreeRTOS的任务优先级是,任务优先级数值越小,任务优先级越低
三、项目移植
将这个项目进行CubeMX的移植
1:RC552
①:直接用图形化配置添加RC552的任务
②:RC552改用硬件SPI MFRC-522 无线射频IC卡驱动教程
③:存在问题print汉字输出错误
④:基于CubeMX、HAL库修改开发
⑤:在Keil中添加自己的.c/.h文件后要保存,这样用CubeMX重新生成代码时才不会被删除。
用 strncmp((char *)readUid,(char *)UID,4)
替换 (cid[0] == ID[0]) && (cid[1] == ID[1])&& (cid[2] == ID[2]) && (cid[3] == ID[3]) 做对比
2:AS608
①:HAL库配置外部中断 外部中断基础
②:移植之前的失败参考新方式 串口空闲中断的方式 AS608((HAL库 )
对于这段函数的理解:串口不定长数据改用空闲中断的方式
/*指纹模块初始化*/ uint8_t as608_init(void) { //设置uart3接收中断 HAL_UART_Receive_IT(&AS608_UART,USART2_RX_BUF,sizeof( USART2_RX_BUF));//接收数据,且产生中断 //使能空闲中断 __HAL_UART_ENABLE_IT(&AS608_UART,UART_IT_IDLE);// return AS608_Check(); }
void USART2_IRQHandler(void) { /* USER CODE BEGIN USART2_IRQn 0 */ //串口接收不定长数据 //判断是否为空闲中断,如果是就认为接收数据完成 if(__HAL_UART_GET_FLAG(&AS608_UART,UART_FLAG_IDLE) != RESET) { //认为数据接收完成,进行处理 //1、清除空闲中断 __HAL_UART_CLEAR_IDLEFLAG(&AS608_UART); //2、获取接收大小 //3、清空接收状态 AS608_UART.RxXferCount = sizeof(USART2_RX_BUF); AS608_UART.pRxBuffPtr = USART2_RX_BUF; USART2_RX_STA = 1;//接收数据完成 return ; } /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); /* USER CODE BEGIN USART2_IRQn 1 */ /* USER CODE END USART2_IRQn 1 */ }
3:CPU使用情况 添加系统任务状态及任务运行显示
①:设置一个是freeRTOS心跳(1ms)10倍小(0.1ms)的定时器,以及中断
1. auto-reload precload=Disable:自动重装载寄存器写入新值后,计数器立即产生计数溢出,然后开始新的计数周期
2. auto-reload precload=Enable:自动重装载寄存器写入新值后,计数器完成当前旧的计数后,再开始新的计数周期所以这里Disable或者Enable都可以。
任务栈的大小要够大因为有任务信息要打印栈太小会导致任务失败
②:开启freeRTOS的
GENERATE_RUN_TIME_STATS
、USE_TRACE_FACILITY
、USE_STATS_FORMATTING_FUNCTIONS
③:创建按键任务来触发
void key_Task(void *argument) { /* USER CODE BEGIN key_Task */ /* Infinite loop */ uint8_t CPU_RunInfo[400]; uint8_t key; for(;;) { memset(CPU_RunInfo,0,sizeof(CPU_RunInfo)); vTaskList((char *)&CPU_RunInfo); //获取任务列表 printf("---------------------------------------------\r\n"); printf("任务名 任务状态 优先级 剩余栈 任务序号\r\n"); printf("%s", CPU_RunInfo); printf("---------------------------------------------\r\n"); memset(CPU_RunInfo,0,400); vTaskGetRunTimeStats((char *)&CPU_RunInfo); //获取任务运行状态 printf("任务名 运行计数 利用率\r\n"); printf("%s", CPU_RunInfo); printf("---------------------------------------------\r\n"); osDelay(1000); } /* USER CODE END key_Task */ }
//统计CPU利用率的TIM7定时器中断函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM7) { g_osRuntimeCounter++;//g_osRuntimeCounter为一个全局变量 } }
实现FreeRTOSConfig.h中定义的两函数
void configureTimerForRunTimeStats(void) { g_osRuntimeCounter = 0; } unsigned long getRunTimeCounterValue(void) { return g_osRuntimeCounter; }
最后CubeMX配置完后要手动打开配置的定时器,自动生成的不会打开。
没有打开的话运行占用率是不会打印出来,只会打印出与任务信息;
要自己打开定时器
/* USER CODE BEGIN 2 */ HAL_TIM_Base_Start_IT(&htim7); /* USER CODE END 2 */
④:模块中的delay_ms全部用freeRTOS的osDelay任务利用率会降低
4:舵机 HAL库PWM输出驱动舵机
①:定时器的配置
②:要注意项目中的延迟应该都用freeRTOS的延迟函数,这样才能切换到别的任务运行,而不是死等。
③:这里配置的是PWM的占空比,舵机0°对应的是500,但是配置成500舵机精度问题会到不了一直卡死,所以这里配置大一点。
4:ESP866
①:按照之前的移植
②:串口不定长数据改用空闲中断的方式
四、模块通信
之前采用的事件组的方式进行同步现在改用任务通知的ICP通信方式
任务完整代码
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "myconfig.h"
#include "tim.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
void Set_Angle(float angle);
void Suspen_Servo(void);
void Warn(void);
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define EVENT1 (0X01 << 0)
#define EVENT2 (0X01 << 1)
#define EVENT3 (0X01 << 2)
#define ULONG_MAX (0xFFFFFFFF)
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
extern volatile uint8_t finger_status;
uint8_t err=0;
/* USER CODE END Variables */
/* Definitions for Start */
osThreadId_t StartHandle;
const osThreadAttr_t Start_attributes = {
.name = "Start",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityBelowNormal,
};
/* Definitions for LED */
osThreadId_t LEDHandle;
const osThreadAttr_t LED_attributes = {
.name = "LED",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for AS608 */
osThreadId_t AS608Handle;
const osThreadAttr_t AS608_attributes = {
.name = "AS608",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal1,
};
/* Definitions for KEY */
osThreadId_t KEYHandle;
const osThreadAttr_t KEY_attributes = {
.name = "KEY",
.stack_size = 512 * 4,
.priority = (osPriority_t) osPriorityNormal2,
};
/* Definitions for SERVO */
osThreadId_t SERVOHandle;
const osThreadAttr_t SERVO_attributes = {
.name = "SERVO",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal3,
};
/* Definitions for ESP */
osThreadId_t ESPHandle;
const osThreadAttr_t ESP_attributes = {
.name = "ESP",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal4,
};
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
/* USER CODE END FunctionPrototypes */
void Start_Task(void *argument);
void led_Task(void *argument);
void AS608_Task(void *argument);
void key_Task(void *argument);
void Servo_Task(void *argument);
void ESP_Task(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/* Hook prototypes */
void configureTimerForRunTimeStats(void);
unsigned long getRunTimeCounterValue(void);
/* USER CODE BEGIN 1 */
/* Functions needed when configGENERATE_RUN_TIME_STATS is on */
__weak void configureTimerForRunTimeStats(void)
{
}
__weak unsigned long getRunTimeCounterValue(void)
{
return 0;
}
/* USER CODE END 1 */
/**
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of Start */
StartHandle = osThreadNew(Start_Task, NULL, &Start_attributes);
/* creation of LED */
LEDHandle = osThreadNew(led_Task, NULL, &LED_attributes);
/* creation of AS608 */
AS608Handle = osThreadNew(AS608_Task, NULL, &AS608_attributes);
/* creation of KEY */
KEYHandle = osThreadNew(key_Task, NULL, &KEY_attributes);
/* creation of SERVO */
SERVOHandle = osThreadNew(Servo_Task, NULL, &SERVO_attributes);
/* creation of ESP */
ESPHandle = osThreadNew(ESP_Task, NULL, &ESP_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_Start_Task */
/**
* @brief Function implementing the Start thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_Start_Task */
void Start_Task(void *argument)
{
/* USER CODE BEGIN Start_Task */
MFRC_Init();
PCD_Reset();
printf("RC522初始化完成\r\n");
osStatus_t result;
uint8_t readUid[5];
uint8_t UID[5]={0x83,0xFE,0x28,0xA1};//
BaseType_t xReturn = pdPASS;
unsigned char cid[4];
/* Infinite loop */
for(;;)
{
if(!readCard(readUid,NULL))
{
printf("卡号:%.2X-%.2X-%.2X-%.2X\r\n",readUid[0],readUid[1],readUid[2],readUid[3]);
if(!strncmp((char *)readUid,(char *)UID,4))
{
//比对卡号正确要做什么
printf("卡号正确\r\n");
xReturn = xTaskNotify((TaskHandle_t)SERVOHandle,
(uint32_t)EVENT3,
(eNotifyAction)eSetBits);
if(xReturn == pdTRUE) printf("send event3 succeed\r\n");
else printf("send event3 fail\r\n");
}else{
//比对卡号错误要做什么
printf("卡号错误\r\n");
err++;
Suspen_Servo();
}
}
osDelay(100);
}
/* USER CODE END Start_Task */
}
/* USER CODE BEGIN Header_led_Task */
/**
* @brief Function implementing the LED thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_led_Task */
void led_Task(void *argument)
{
/* USER CODE BEGIN led_Task */
printf("led_Task\r\n");
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
osDelay(1000);
}
/* USER CODE END led_Task */
}
/* USER CODE BEGIN Header_AS608_Task */
/**
* @brief Function implementing the AS608 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_AS608_Task */
void AS608_Task(void *argument)
{
/* USER CODE BEGIN AS608_Task */
osStatus_t result;
printf("AS608_Task\r\n");
as608_init();
BaseType_t xReturn = pdPASS;
// /* Infinite loop */
for(;;)
{
if(finger_status == FINGER_EXIST)
{
finger_status = FINGER_NO_EXIST;
if(press_FR())
{
xReturn = xTaskNotify((TaskHandle_t)SERVOHandle,
(uint32_t)EVENT1,
(eNotifyAction)eSetBits);
if(xReturn == pdTRUE) printf("send event1 succeed\r\n");
else printf("send event1 fail\r\n");
}
else
{
err++;
Suspen_Servo();
}
}
osDelay(100);
}
/* USER CODE END AS608_Task */
}
/* USER CODE BEGIN Header_key_Task */
/**
* @brief Function implementing the KEY thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_key_Task */
void key_Task(void *argument)
{
/* USER CODE BEGIN key_Task */
/* Infinite loop */
uint8_t CPU_RunInfo[400];
uint8_t key;
for(;;)
{
key=KEY_Scan(0); //扫描按键
if(key == KEY_UP_PRESS)
{
memset(CPU_RunInfo,0,sizeof(CPU_RunInfo));
vTaskList((char *)&CPU_RunInfo); //获取任务列表
printf("---------------------------------------------\r\n");
printf("任务名 任务状态 优先级 剩余栈 任务序号\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n");
memset(CPU_RunInfo,0,400);
vTaskGetRunTimeStats((char *)&CPU_RunInfo); //获取任务运行状态
printf("任务名 运行计数 利用率\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n");
}
else if(key == KEY1_PRESS)
{
HAL_TIM_Base_Stop_IT(&htim4);
printf("舵机任务恢复\r\n");
vTaskResume(SERVOHandle);
err = 0;
}
osDelay(10);
}
/* USER CODE END key_Task */
}
/* USER CODE BEGIN Header_Servo_Task */
/**
* @brief Function implementing the SERVO thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_Servo_Task */
void Servo_Task(void *argument)
{
/* USER CODE BEGIN Servo_Task */
uint8_t key;
BaseType_t xReturn = pdPASS;
uint32_t r_event = 0;
/* Infinite loop */
for(;;)
{
xReturn = xTaskNotifyWait(0x00,ULONG_MAX,&r_event,portMAX_DELAY);
if(xReturn == pdTRUE)
{
if(r_event | EVENT1| EVENT2 | EVENT3)
{
Warn();
printf("OPEN\r\n");
Set_Angle(0);
osDelay(1000);
Set_Angle(170);
osDelay(1000);
err = 0;
}
}
osDelay(10);
}
/* USER CODE END Servo_Task */
}
/* USER CODE BEGIN Header_ESP_Task */
/**
* @brief Function implementing the ESP thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_ESP_Task */
void ESP_Task(void *argument)
{
/* USER CODE BEGIN ESP_Task */
ESP8266_Init();
osStatus_t result;
BaseType_t xReturn = pdPASS;
/* Infinite loop */
for(;;)
{
if(USART3_RX_STA)
{
if(strstr((const char*)USART3_RX_BUF,"on"))
{
printf("%s\r\n",USART3_RX_BUF);
xReturn = xTaskNotify((TaskHandle_t)SERVOHandle,
(uint32_t)EVENT2,
(eNotifyAction)eSetBits);
if(xReturn == pdTRUE) printf("send event2 succeed\r\n");
else printf("send event2 fail\r\n");
}
memset(USART3_RX_BUF,0,sizeof(USART3_RX_BUF));
USART3_RX_STA = 0;
}
osDelay(1000);
}
/* USER CODE END ESP_Task */
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
void Set_Angle(float angle)
{
int servo_temp;
if(angle > 180) angle = 180;
else if(angle < 0) angle = 0;
servo_temp = angle*2000/180 + 600;
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,(uint16_t)servo_temp);
}
//Suspen_Servo
void Suspen_Servo(void)
{
printf("ERRO:%d",err);
if(err == 3)
{
HAL_TIM_Base_Start_IT(&htim4);
vTaskSuspend(SERVOHandle);
printf("舵机任务挂起\r\n");
}
}
void Warn(void)
{
HAL_TIM_Base_Start_IT(&htim4);
osDelay(100);
HAL_TIM_Base_Stop_IT(&htim4);
osDelay(100);
HAL_TIM_Base_Start_IT(&htim4);
osDelay(100);
HAL_TIM_Base_Stop_IT(&htim4);
}
/* USER CODE END Application */
实验效果同库函数的效果一样 详见freeRTOS小项目练习—智能门锁_rtos项目_kedvellek的博客-CSDN博客
①:学习cubeMX的配置 用于开发STM的确实方便
②:HAL库学习,HAL库与标准库比会更加简单感觉
③:freeRTOS的进一步了解学习
23.5.28
五、完整项目工程代码
(用的是普中的PZ6806L开发板)