STM32CubeIDE手动移植FreeRTOS-动态创建任务和删除

一、引言

我在学习这个部分的时候遇到了很多问题,但好在搜集了很多资料后得以解决,于是打算在这里演示这个项目的全过程,即当做复习的资料,也希望能够帮助到一些人,话不多说,直接开整!

二、项目简介

创建以下三个任务:

  • 任务1:LED1每500ms翻转一次
  • 任务2:LED2每500ms翻转一次
  • 任务3:当按下KEY1的时候删除任务1

三、步骤

1. 实现基本的工程配置

a. 引脚配置:

PA0为红色LED,PA1为绿色LED,PA5为按键1,PA9和PA10用作串口发送

  • 设置PA0GPIO_Output,添加标签LED_RED
  • 设置PA1GPIO_Output,添加标签LED_Green
  • 设置PA5GPIO_Intput,添加标签KEY1
  • 设置PA9USART1_TX,PA10USART1_RX

因为我的按键另一端连接的是正极,所以我的PA5设置为了下拉输入,让PA5默认为低电平,当按键按下时,PA5会表现为高电平:
图片来自B站视频UP:keysking

b. 系统配置:

  • RCC的时钟来源设置为HSE,调整系统时钟为72MHZ

  • 设置SYSDebugSerial WireTimebase SourceTIM4 前者是为了能把代码下载进单片机,后者是因为HAL本身和FreeRTOS都默认依赖SysTick,可能出现卡死的问题,所以为了保险起见,可以考虑在SYS选择HAL时钟源的时候换成其他的,比如TIM4
  • 设置Time base: TIM4 global interruptSystem 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 */

至此,所有工作完成,编译并下载代码即可

四、总结

才开始写文章,感觉文章写得有点乱,有任何意见或者问题都可以私信我,我也刚开始学这个,大家可以一起交流,希望这篇文章可以帮助到大家,感谢!

相关推荐
不会武功的火柴2 天前
UVM验证入门(2)-uvm常用类的继承关系
嵌入式
二狗就是我2 天前
YOLOv5 移植 RK3588 踩坑记录
嵌入式
编程墨客3 天前
基于ESP8266的智能桌面天气站
嵌入式·diy
csdn_aspnet4 天前
嵌入式赋能生活的各个领域
嵌入式·生活
s1ckrain5 天前
数字逻辑笔记—绪论
笔记·嵌入式
闲猿类6 天前
嵌入式第九天学习
linux·c语言·学习·算法·嵌入式
SundayBear6 天前
嵌入式操作系统进阶C语言
c语言·开发语言·嵌入式
一枝小雨6 天前
单片机内存布局管理:sct分散加载详解
stm32·单片机·嵌入式·编译链接·sct分散加载·单片机内存布局
飞凌嵌入式6 天前
飞凌嵌入式RK3568开发板的TFTP烧写文件系统指南
linux·嵌入式硬件·嵌入式