一、引言
我在学习这个部分的时候遇到了很多问题,但好在搜集了很多资料后得以解决,于是打算在这里演示这个项目的全过程,即当做复习的资料,也希望能够帮助到一些人,话不多说,直接开整!
二、项目简介
创建以下三个任务:
- 任务1:LED1每500ms翻转一次
- 任务2:LED2每500ms翻转一次
- 任务3:当按下KEY1的时候删除任务1
三、步骤
1. 实现基本的工程配置
a. 引脚配置:
PA0为红色LED,PA1为绿色LED,PA5为按键1,PA9和PA10用作串口发送
- 设置
PA0为GPIO_Output,添加标签LED_RED - 设置
PA1为GPIO_Output,添加标签LED_Green - 设置
PA5为GPIO_Intput,添加标签KEY1 - 设置
PA9为USART1_TX,PA10为USART1_RX

因为我的按键另一端连接的是正极,所以我的PA5设置为了下拉输入,让PA5默认为低电平,当按键按下时,PA5会表现为高电平:
图片来自B站视频UP:keysking
b. 系统配置:
- 将
RCC的时钟来源设置为HSE,调整系统时钟为72MHZ:

- 设置
SYS的Debug为Serial Wire,Timebase Source为TIM4前者是为了能把代码下载进单片机,后者是因为HAL本身和FreeRTOS都默认依赖SysTick,可能出现卡死的问题,所以为了保险起见,可以考虑在SYS选择HAL时钟源的时候换成其他的,比如TIM4

- 设置
Time base: TIM4 global interrupt和System tick timer的优先级都为1 - 打开USART1的中断,优先级设置为0

- 设置USART为异步模式:

- 打开
Project Manager中的Code Generator,勾选每个外设生成一对".c/.h文件"初始化
至此,项目基本配置完成,Ctrl+S生成代码,进入代码编辑页面
- 在根目录下新建Hardware文件夹,新建Key.h和Key.h文件
c
#ifndef __KEY_H
#define __KEY_H
#include "main.h"
uint8_t Key_Is_Press();
#endif
c
#include "Key.h"
uint8_t Key_Is_Press()
{
uint8_t Key_Is_Press = 0;
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_SET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_SET)
{
Key_Is_Press = 1;
}
}
return Key_Is_Press;
}
2. printf函数重定向
参考上一篇文章
3. 开始移植
1. 文件移植
在项目根目录下新建FreeRTOS文件夹,复制源码/Source/include文件夹到FreeRTOS文件夹下,FreeRTOS文件夹下新建source文件夹和portable文件夹,复制源码Source文件夹下的7个.c文件到FreeRTOS/source文件夹,把源码/portable/MemMang文件夹复制到FreeRTOS/portable文件夹,只保留heap_4.c文件,把源码/Source/portable/GCC文件夹复制到FreeRTOS/portable文件夹,只保留ARM_CM3文件夹,把源码/Demo/CORTEX_STM32F103_Primer_GCC下的FreeRTOSConfig.h文件复制到项目/Core/Inc文件夹下

2. 工程配置
- 把Hardware文件夹和FreeRTOS文件夹加入项目编译范围 右键项目名字,点击Properties,进入如下页面:

- 把include文件夹和ARM_CM3文件夹和Hardware文件夹添加到Include directories目录
- 把FreeRTOS文件夹和Hardware文件夹添加到Source Location


3. 系统配置文件修改
- FreeRTOSConfig.h中添加如下3个配置:
c
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define INCLUDE_xTaskGetSchedulerState 1
把#define configUSE_TICK_HOOK 1修改为0
- 修改stm32f1xx_it.c文件
引入头文件:
c
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
#include "task.h"
/* USER CODE END Includes */
注释掉2个函数:
c
// void SVC_Handler(void)
// {
// }
// void PendSV_Handler(void)
// {
// }
添加SysTick时钟中断服务函数:
c
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern void xPortSysTickHandler(void);
/* USER CODE END PV */
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
/* USER CODE BEGIN SysTick_IRQn 1 */
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
/* USER CODE END SysTick_IRQn 1 */
}
4. 动态创建任务
- FreeRTOSConfig.h文件新加
#define configSUPPORT_DYNAMIC_ALLOCATION - 在Core/Inc下新建FreeRTOS_demo.h,在Core/src下新建FreeRTOS_demo.c
- FreeRTOS_demo.c代码:
c
#include "FreeRTOS_demo.h"
/* 启动任务函数 */
#define START_TASK_PRIORITY 1
#define START_TASK_STACK_DEPTH 128
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);
/* Task1 任务 配置 */
#define TASK1_PRIORITY 2
#define TASK1_STACK_DEPTH 128
TaskHandle_t task1_handler;
void Task1(void *pvParameters);
/* Task2 任务 配置 */
#define TASK2_PRIORITY 3
#define TASK2_STACK_DEPTH 128
TaskHandle_t task2_handler;
void Task2(void *pvParameters);
/* Task3 任务 配置 */
#define TASK3_PRIORITY 4
#define TASK3_STACK_DEPTH 128
TaskHandle_t task3_handler;
void Task3(void *pvParameters);
/**
* @description: FreeRTOS入口函数:创建任务函数并开始调度
* @return {*}
*/
void FreeRTOS_Start(void)
{
xTaskCreate((TaskFunction_t)Start_Task,
(char *)"Start_Task",
(configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
(void *)NULL,
(UBaseType_t)START_TASK_PRIORITY,
(TaskHandle_t *)&start_task_handler);
vTaskStartScheduler();
}
void Start_Task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
xTaskCreate((TaskFunction_t ) Task1,
(char * ) "Task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_DEPTH,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIORITY,
(TaskHandle_t * ) &task1_handler );
xTaskCreate((TaskFunction_t ) Task2,
(char * ) "Task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_DEPTH,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIORITY,
(TaskHandle_t * ) &task2_handler );
xTaskCreate((TaskFunction_t ) Task3,
(char * ) "Task2",
(configSTACK_DEPTH_TYPE ) TASK3_STACK_DEPTH,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIORITY,
(TaskHandle_t * ) &task3_handler );
vTaskDelete(NULL);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/**
* @description: LED1每500ms翻转一次
* @param {void *} pvParameters
* @return {*}
*/
void Task1(void * pvParameters)
{
while(1)
{
printf("task1运行....\r\n");
HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin);
vTaskDelay(500);
}
}
/**
* @description: LED2每500ms翻转一次
* @param {void *} pvParameters
* @return {*}
*/
void Task2(void * pvParameters)
{
while(1)
{
printf("task2运行....\r\n");
HAL_GPIO_TogglePin(LED_Green_GPIO_Port, LED_Green_Pin);
vTaskDelay(500);
}
}
/**
* @description:
* @param {void *} pvParameters
* @return {*}
*/
void Task3(void * pvParameters)
{
while(1)
{
printf("task3运行...\r\n");
if(Key_Is_Press())
{
if(task1_handler != NULL)
{
printf("删除task1任务...\r\n");
vTaskDelete(task1_handler);
task1_handler = NULL;
}
}
vTaskDelay(500);
}
}
- FreeRTOS_demo.h代码:
c
#ifndef __FREERTOS_DEMO_H
#define __FREERTOS_DEMO_H
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "Key.h"
void FreeRTOS_Start(void);
#endif
- main.c文件对应位置加入:
c
/* USER CODE BEGIN Includes */
#include "FreeRTOS_demo.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */
FreeRTOS_Start();
/* USER CODE END 2 */
至此,所有工作完成,编译并下载代码即可
四、总结
才开始写文章,感觉文章写得有点乱,有任何意见或者问题都可以私信我,我也刚开始学这个,大家可以一起交流,希望这篇文章可以帮助到大家,感谢!