目录
1. 前期准备
我们之前往江科大的代码中移植了FreeRTOS并进行了简单的使用:
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:
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 代码
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(); //退出临界区
}