目录
1. 前期准备
1.1 中断文件修改
1.2 SysTick文件修改
1.3 任务创建函数API
2. 任务创建(静态方法)
2.1 创建两个任务函数
2.2 静态创建开始任务函数
2.3 创建开始任务的任务函数
2.4 补充
2.5 代码
3. 任务创建(动态方法)
3.1 创建两个任务函数
3.2 动态创建开始任务函数
3.3 创建开始任务的任务函数
3.4 代码
1. 前期准备
我们之前往江科大的代码中移植了FreeRTOS并进行了简单的使用:
简易版·江协科技/江科大STM32代码移植FreeRTOS实时操作系统-CSDN博客
1.1 中断文件修改
对于FreeRTOS我们需要写一个心跳时钟来证明“他在干活”,需要用到“SysTick”,对于“SysTick”的启动配置,我们可以去“port”函数看一下:
/*
* Setup the SysTick timer to generate the tick interrupts at the required
* frequency.
*/
#if configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0
void vPortSetupTimerInterrupt( void )
{
/* Calculate the constants required to configure the tick interrupt. */
#if configUSE_TICKLESS_IDLE == 1
{
ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
}
#endif /* configUSE_TICKLESS_IDLE */
/* Configure SysTick to interrupt at the requested rate. */
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
}
#endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */
这里我们只要在“stm32f10x_it.c”文件调用即可,找到void SysTick_Handler(void)函数,将之前的注释取消掉,编写如下代码:
void SysTick_Handler(void)
{
xPortSysTickHandler();
}
为了确保在系统初始化时正确配置 SysTick 定时器,并且xPortSysTickHandler()函数是由 FreeRTOS 提供的且已经正确集成到你的项目中,我们可以使用xTaskGetSchedulerState()函数检查调度器是否已启动。只有当调度器已经启动时,才会调用xPortSysTickHandler()。
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
}
记得在最上面把头文件包含以下:
#include "FreeRTOS.h"
#include "task.h"
完整代码,直接替换即可:
/**
******************************************************************************
* @file Project/STM32F10x_StdPeriph_Template/stm32f10x_it.c
* @author MCD Application Team
* @version V3.5.0
* @date 08-April-2011
* @brief Main Interrupt Service Routines.
* This file provides template for all exceptions handler and
* peripherals interrupt service routine.
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>© COPYRIGHT 2011 STMicroelectronics</center></h2>
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"
#include "FreeRTOS.h" //FreeRTOS使用
#include "task.h"
/** @addtogroup STM32F10x_StdPeriph_Template
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/******************************************************************************/
/* Cortex-M3 Processor Exceptions Handlers */
/******************************************************************************/
/**
* @brief This function handles NMI exception.
* @param None
* @retval None
*/
void NMI_Handler(void)
{
}
/**
* @brief This function handles Hard Fault exception.
* @param None
* @retval None
*/
void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Memory Manage exception.
* @param None
* @retval None
*/
void MemManage_Handler(void)
{
/* Go to infinite loop when Memory Manage exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Bus Fault exception.
* @param None
* @retval None
*/
void BusFault_Handler(void)
{
/* Go to infinite loop when Bus Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Usage Fault exception.
* @param None
* @retval None
*/
void UsageFault_Handler(void)
{
/* Go to infinite loop when Usage Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles SVCall exception.
* @param None
* @retval None
*/
//void SVC_Handler(void)
//{
//}
/**
* @brief This function handles Debug Monitor exception.
* @param None
* @retval None
*/
void DebugMon_Handler(void)
{
}
/**
* @brief This function handles PendSVC exception.
* @param None
* @retval None
*/
//void PendSV_Handler(void)
//{
//}
extern void xPortSysTickHandler(void);
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
}
/******************************************************************************/
/* STM32F10x Peripherals Interrupt Handlers */
/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */
/* available peripheral interrupt handler's name please refer to the startup */
/* file (startup_stm32f10x_xx.s). */
/******************************************************************************/
/**
* @brief This function handles PPP interrupt request.
* @param None
* @retval None
*/
/*void PPP_IRQHandler(void)
{
}*/
/**
* @}
*/
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
1.2 SysTick文件修改
在FreeRTOS中任务的切换即每个任务运行的时间是由SysTick定时器提供的。
这里可以先参考:
STM32F1之SysTick系统定时器详细解析-CSDN博客
了解一下SysTick相关。
FreeRTOS的SysTick代码和上面讲的还有一些区别:
链接: https://pan.baidu.com/s/1UZubCwp847-TGX_tI4vQHQ?pwd=yfk6
提取码: yfk6
FreeRTOS想要使用SysTick,肯定需要根据FreeRTOS的系统时钟节拍来初始化滴答定时器,FreeRTOS的系统时钟节拍根据FreeRTOSConfig.h的:
#define configTICK_RATE_HZ (( TickType_t )1000)
来实现的,这个值可以随意设置,但是设置完后需要根据这个值来初始化系统滴答定时器,在这里,我们使用的是STM32F1系列的,其SysTick 有两种模式:一种是 8 分频模式(AHB/8),一种是 FCLK 模式(AHB)。AHB 最大频率可为 SYSCLK (在 f1系列为 72M,f4 系列为 168M )。详细参看《STM32F10x 参考手册》。
//初始化延迟函数
//SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
//SYSCLK:系统时钟频率
void SysTick_Init(u8 SYSCLK)
{
u32 reload;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟 HCLK
fac_us=SystemCoreClock/1000000; //不论是否使用OS,fac_us都需要使用
reload=SystemCoreClock/1000000; //每秒钟的计数次数 单位为M
reload*=1000000/configTICK_RATE_HZ; //根据configTICK_RATE_HZ设定溢出时间
//reload为24位寄存器,最大值:16777216,在72M下,约合0.233s左右
fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD=reload; //每1/configTICK_RATE_HZ秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
}
这里对于SystemCoreClock进行跳转:
在对于SYSCLK_FREQ_72MHz进行跳转:
这里实现1us的一个计数值,然后根据:
#define configTICK_RATE_HZ (( TickType_t )1000)
搭配:
reload*=1000000/configTICK_RATE_HZ; //根据configTICK_RATE_HZ设定溢出时间
//reload为24位寄存器,最大值:16777216,在72M下,约合0.233s左右
fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位
实现毫秒的计数。
然后就是配置SysTick的三个寄存器:
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD=reload; //每1/configTICK_RATE_HZ秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
至此SysTick初始化使能完成。
对于延时函数:
void delay_ms(u32 nms)
void delay_xms(u32 nms)
这两个的区别,对于void delay_ms(u32 nms)是和FreeRTOS相关的是需要进行任务的调度的,他的值和FreeRTOS的节拍相关,需要引发任务的调度的:
而void delay_xms(u32 nms)就可以理解为普通的delay函数,就单出的是个延时函数,相当于你点个灯延时一下,和任务调度没关系。
1.3 任务创建函数API
并且了解了任务创建函数的相关API:
基于STM32F103的FreeRTOS系列(六)·如何进行FreeRTOS任务创建·逐行代码解析-CSDN博客
2. 任务创建(静态方法)
2.1 创建两个任务函数
在开始静态任务创建前,我们先创建两个任务,简单实现两个LED灯的交替闪烁:
//任务优先级
#define TASK1_TASK_PRIO 2
//任务堆栈大小
#define TASK1_STK_SIZE 128
//任务堆栈
StackType_t Task1TaskStack[TASK1_STK_SIZE];
//任务控制块
StaticTask_t Task1TaskTCB;
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);
//任务优先级
#define TASK2_TASK_PRIO 3
//任务堆栈大小
#define TASK2_STK_SIZE 128
//任务堆栈
StackType_t Task2TaskStack[TASK2_STK_SIZE];
//任务控制块
StaticTask_t Task2TaskTCB;
//任务句柄
TaskHandle_t Task2Task_Handler;
//任务函数
void task2_task(void *pvParameters);
//任务1函数
void task1_task(void *pvParameters)
{
while(1)
{
led1_on();
vTaskDelay(200);
led1_off();
vTaskDelay(800);
}
}
//任务2函数
void task2_task(void *pvParameters)
{
while(1)
{
led2_on();
vTaskDelay(800);
led2_off();
vTaskDelay(200);
}
}
对于任务所需引脚声明:
led.c文件:
#include "led.h"
/*******************************************************************************
* 函 数 名 : LED_Init
* 函数功能 : LED初始化函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;//定义结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1; //选择你要设置的IO口
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //设置推挽输出模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
GPIO_ResetBits(GPIOA,GPIO_Pin_0 | GPIO_Pin_1); //将LED端口拉高,熄灭所有LED
}
void led1_on(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_0);
}
void led1_off(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
}
void led2_on(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
void led2_off(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
led.h文件:
#ifndef _led_H
#define _led_H
#include "system.h"
/* LED时钟端口、引脚定义 */
#define LED1_PORT GPIOA
#define LED1_PIN GPIO_Pin_0
#define LED1_PORT_RCC RCC_APB2Periph_GPIOA
#define LED2_PORT GPIOA
#define LED2_PIN GPIO_Pin_1
#define LED2_PORT_RCC RCC_APB2Periph_GPIOA
void LED_Init(void);
void led1_on(void);
void led1_off(void);
void led2_on(void);
void led2_off(void);
#endif
2.2 静态创建开始任务函数
要创建静态任务函数,需要调用xTaskCreateStatic()函数,其函数在task.c文件的内容为:
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
TCB_t *pxNewTCB;
TaskHandle_t xReturn;
configASSERT( puxStackBuffer != NULL );
configASSERT( pxTaskBuffer != NULL );
if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
{
/* The memory used for the task's TCB and stack are passed into this
function - use them. */
pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */
pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
/* Tasks can be created statically or dynamically, so note this
task was created statically in case the task is later deleted. */
pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL );
prvAddNewTaskToReadyList( pxNewTCB );
}
else
{
xReturn = NULL;
}
return xReturn;
}
#endif /* SUPPORT_STATIC_ALLOCATION */
可以看到需要满足:
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
函数才会执行,因此我们需要找到FreeRTOSConfig.h中的:
//支持静态内存
#define configSUPPORT_STATIC_ALLOCATION 1
然后使用函数,该函数的相关参数:
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer )
这些可以跳转全是更改的宏定义,其实上述代码相当于,函数跳转可以看见:
TaskHandle_t xTaskCreateStatic( void * pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
unsigned long uxPriority,
uint32_tt * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer )
解释:
① pxTaskCode: 任务函数。
② pcName: 任务名字,一般用于追踪和调试,任务名字长度不能超过。configMAX_TASK_NAME_LEN。
③ usStackDepth: 任务堆栈大小,由于本函数是静态方法创建任务,所以任务堆栈由用户给出,一般是个数组,此参数就是这个数组的大小。
④ pvParameters: 传递给任务函数的参数。
⑤ uxPriotiry: 任务优先级,范围 0~ configMAX_PRIORITIES-1。
⑥ puxStackBuffer: 任务堆栈,一般为数组,数组类型要为 StackType_t 类型。
⑦ pxTaskBuffer: 任务控制块。
我们在主函数中进行创建:
//创建开始任务
StartTask_Handler=xTaskCreateStatic((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint32_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(StackType_t* )StartTaskStack, //任务堆栈
(StaticTask_t* )&StartTaskTCB); //任务控制块
相关参数的宏定义:
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务堆栈
StackType_t StartTaskStack[START_STK_SIZE];
//任务控制块
StaticTask_t StartTaskTCB;
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
2.3 创建开始任务的任务函数
主函数开始运行后,运行创建开始任务函数,然后调用vTaskStartScheduler(); 进行任务调用,调用开始任务的任务函数“start_task”:
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init();
USART1_Init(115200);
//创建开始任务
StartTask_Handler=xTaskCreateStatic((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint32_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(StackType_t* )StartTaskStack, //任务堆栈
(StaticTask_t* )&StartTaskTCB); //任务控制块
vTaskStartScheduler(); //开启任务调度
}
对于任务函数“start_task”,为了确保在创建任务的过程中,任务调度不会被中断,可以调用taskENTER_CRITICAL();和taskEXIT_CRITICAL();进行进入和退出临界区,先进行任务的创建,防止中断服务例程调用调度函数,导致任务调度在任务创建过程中进行,从而导致潜在的错误或资源争用。
任务创建涉及到操作内部的数据结构,例如任务控制块(TCB)和任务堆栈。如果这些操作在任务调度过程中被打断,可能会导致数据结构不一致或任务创建失败。
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建TASK1任务
Task1Task_Handler=xTaskCreateStatic((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint32_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(StackType_t* )Task1TaskStack,
(StaticTask_t* )&Task1TaskTCB);
//创建TASK2任务
Task2Task_Handler=xTaskCreateStatic((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint32_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(StackType_t* )Task2TaskStack,
(StaticTask_t* )&Task2TaskTCB);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
然后调用vTaskDelete(StartTask_Handler);函数这样就会将开始函数删除,之后就会在任务调度器的调度下,运行task1_task和task2_task两个任务。
2.4 补充
对于静态任务的使用,需要用户来提供获取空闲任务地任务堆栈和任务控制块内存,因此需要:
/* 空闲任务任务堆栈 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];
/* 定时器任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];
/* 空闲任务控制块 */
static StaticTask_t Idle_Task_TCB;
/* 定时器任务控制块 */
static StaticTask_t Timer_Task_TCB;
//获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的
//静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该
//有用户来提供,FreeRTOS提供了接口函数vApplicationGetIdleTaskMemory()
//实现此函数即可。
//ppxIdleTaskTCBBuffer:任务控制块内存
//ppxIdleTaskStackBuffer:任务堆栈内存
//pulIdleTaskStackSize:任务堆栈大小
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer=&Idle_Task_TCB;
*ppxIdleTaskStackBuffer=Idle_Task_Stack;
*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;
}
//获取定时器任务的任务堆栈和任务控制块内存
//ppxTimerTaskTCBBuffer : 任务控制块内存
//ppxTimerTaskStackBuffer: 任务堆栈内存
//pulTimerTaskStackSize : 任务堆栈大小
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */
*ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */
*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */
}
2.5 代码
任务创建·静态任务.rar资源-CSDN文库
3. 任务创建(动态方法)
3.1 创建两个任务函数
对于动态方法,相比于静态方法,不需要自己手动分配内存,程序自行分配,因此在创建任务是不需要再声明:
//任务堆栈
StackType_t Task1TaskStack[TASK1_STK_SIZE];
//任务控制块
StaticTask_t Task1TaskTCB;
动态任务创建的代码:
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
//任务优先级
#define LED2_TASK_PRIO 3
//任务堆栈大小
#define LED2_STK_SIZE 50
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);
//任务1函数
void led1_task(void *pvParameters)
{
while(1)
{
led1_on();
vTaskDelay(200);
led1_off();
vTaskDelay(800);
}
}
//任务2函数
void led2_task(void *pvParameters)
{
while(1)
{
led2_on();
vTaskDelay(800);
led2_off();
vTaskDelay(200);
}
}
其中led.c和led.h可以使用静态的声明,只改主函数的内容。
3.2 动态创建开始任务函数
这里记得将静态的先置零,将静态功能禁用:
//支持静态内存
#define configSUPPORT_STATIC_ALLOCATION 0
在主函数进行开始任务创建:
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
相关参数的宏定义:
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
剩下的流程类似于静态,先开始任务调度:
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init();
USART1_Init(115200);
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
3.3 创建开始任务的任务函数
也需要进入临界区一次,原因和静态方法一样:
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建LED2任务
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led2_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
3.4 代码
基于STM32F103C8T6的FreeRTOS任务创建·动态任务资源-CSDN文库
FreeRTOS_时光の尘的博客-CSDN博客