基于STM32F103的FreeRTOS系列(九)·任务创建函数的使用·静态方法和动态方法

news2024/12/27 12:33:16

目录

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>&copy; 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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2054784.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【python基础】—利用pandas读取或写入mysql表数据

文章目录 一、read_sql()二、to_sql()三、连接数据库方式—MySQL1、用sqlalchemy包构建数据库链接2、用DBAPI构建数据库链接 四、容易遇到的问题 一、read_sql() 功能 将 SQL 查询/数据库表读入 DataFrame。 语法 读取数据库&#xff08;通过SQL语句或表名&#xff09; pand…

【书生大模型实战营(暑假场)闯关材料】基础岛:第4关 InternLM + LlamaIndex RAG 实践

基础任务 (完成此任务即完成闯关) 任务要求&#xff1a; 基于 LlamaIndex 构建自己的 RAG 知识库&#xff0c;寻找一个问题 A 在使用 LlamaIndex之前InternLM2-Chat-1.8B模型不会回答&#xff0c;借助 LlamaIndex 后 InternLM2-Chat-1.8B模型具备回答 A 的能力&#xff0c;截图…

SQL-约束篇

在数据库设计中&#xff0c;约束是确保数据完整性和准确性的关键元素。约束可以限制表中数据的类型、范围和关系&#xff0c;从而维护数据的一致性和可靠性。 1. 主键约束 (Primary Key) 主键约束用于唯一标识表中的每一行数据。一个表只能有一个主键&#xff0c;主键字段的值…

计算机毕业设计SpringBoot-VUE-python-nodeJS铁路列车安全管理-评估报告-铁路局-客运-货运-行车-站段-天气情况

1 引言 1.1 项目开发的背景 我国的普铁历史悠久&#xff0c;从19世纪至今已有百来年的历史。新中国以来&#xff0c;铁路的发展速度令人惊叹。但随着发展速度的增快&#xff0c;与之不对应的则是运营安全管理系统的落后。但这些来&#xff0c;我国铁路对于安全的重视程度已经…

代码随想录算法训练营第十六天(二叉树 四)

力扣题部分: 513.找树左下角的值 题目链接:. - 力扣&#xff08;LeetCode&#xff09; 题面: 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 思路(层序遍历): 应该是这道题最简单的方法了&#xff0…

超文本文档HTML

简单的个人网站 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>欢迎来到我的主页</title> &…

【HarmonyOS】鸿蒙应用蓝牙功能实现 (二)

【HarmonyOS】鸿蒙应用蓝牙功能实现 &#xff08;二&#xff09; 前言 蓝牙一般分为传统蓝牙(BR/EDR)&#xff0c;低功耗蓝牙(BLE)两种。 鸿蒙将蓝牙的功能模块分的非常细。 基本上我们会用到access进行蓝牙状态的开启和关闭&#xff0c;以及状态查询。 在使用connection进…

BUUCTF PWN wp--warmup_csaw_2016

第一步 先checksec一下&#xff08;没有启用NX保护、PIE、完整的RELRO和栈保护&#xff0c;还有具有RWX权限的内存段。&#xff09; 分析一下这个文件的保护机制&#xff1a; Arch: amd64-64-little 这表示该可执行文件是为64位的AMD64架构编译的&#xff0c;并且使用的是小…

科大讯飞飞凡计划面经(2024年秋招新出炉)

7月笔完一个多月约的面试 —————————————- 一面8.16 不问项目&#xff0c;上来就是八股轰炸 c11新特性 shared_ptr是线程安全的吗&#xff0c;不安全的话怎么实现线程安全的 stl容器中频繁查找用什么&#xff0c;频繁增删用什么 vector中间插入元素会发生什么&…

NVIDIA Isaac Lab 入门教程(一)

系列文章目录 前言 Isaac Lab 是一个用于机器人学习的统一模块化框架&#xff0c;旨在简化机器人研究中的常见工作流程&#xff08;如 RL、从演示中学习和运动规划&#xff09;。它建立在英伟达 Isaac Sim 的基础上&#xff0c;利用最新的仿真功能实现逼真的场景和快速高效的仿…

74、docker容器编译安装lnmp

一、docker部署lnmp l linux n nginx 1.22 172.111.0.10 docker–nginx m mysql 8.0.30 172.111.0.20 docker–mysql p php 8.1.27 172.111.0.30 docker–php docker&#xff1a;单节点部署&#xff0c;在一台机器上部署&#xff0c;跨了机器容器无法通信&#xff0c;做高…

git 如何生成sshkey公钥

打开git客户端 输入 ssh-keygen -t rsa -b 4096 -C "xxxxxxexample.com" 然后根据提示按enter 或者y 直到出现下图所示 打开 c盘的路径下的文件&#xff0c;/c/Users/18159/.ssh/id_rsa.pub 将id_rsa.pub中的公钥贴到git 网站上的SSH keys即可

网络编程(TCP/UDP)

网络编程 一.网络编程 1.1 概述 ​ Java是 Internet 上的语言&#xff0c;它从语言级上提供了对网络应用程序的支持&#xff0c;程序员能够很容易开发常见的网络应用程序。 ​ Java提供的网络类库&#xff0c;可以实现无痛的网络连接&#xff0c;联网的底层细节被隐藏在Java…

【Hot100】LeetCode—2. 两数相加

目录 1- 思路思路 2- 实现⭐2. 两数相加——题解思路 3- ACM 实现 原题连接&#xff1a;2. 两数相加 1- 思路 思路 分为几个步骤 ①数据结构&#xff1a;遍历指针,进位符、②遍历两个链表、③处理最后的进位符 1- 数据结构 定义 curA 和 curB 用来遍历两个链表定义 carry 记…

ubuntu22.04新机器安装五笔wb、onlyoffice、pyenv、pipenv和虚拟环境virtualenv

wb安装方法 重新开机后右上角点击输入法&#xff0c;在下面打开的界面可以点击配置 左下角shift可快速切换五笔和英文 输入 johnjohn-hp:~$ sudo apt-get install fcitx-table-wbpy johnjohn-hp:~$ sudo apt-get install fcitx-googlepinyin johnjohn-hp:~$ sudo apt-get ins…

二叉树OJ刷题与讲解

1.单值二叉树 题目链接&#xff1a;单值二叉树 题目描述&#xff1a; 这道题要求判断是否是单值二叉树也就是说每个节点的值都要相等才是单值二叉树&#xff0c;如果相等就返回true否则返回false&#xff0c;我们可以使用递归来解这道题&#xff0c;我们把它分为根左子树右子…

Bean对象生命周期流程图

Bean生命周期流程图&#xff1a;https://www.processon.com/view/link/5f8588c87d9c0806f27358c1 Spring扫描底层流程&#xff1a;https://www.processon.com/view/link/61370ee60e3e7412ecd95d43

EmguCV学习笔记 VB.Net 4.5 像素距离和连通区域

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 教程VB.net版本请访问&#xff1a;EmguCV学习笔记 VB.Net 目录-CSDN博客 教程C#版本请访问&#xff1a;EmguCV学习笔记 C# 目录-CSD…

【数据结构与算法 | 图篇】最小生成树之Kruskal(克鲁斯卡尔)算法

1. 前言 克鲁斯卡尔算法&#xff08;Kruskals algorithm&#xff09;是一种用于寻找加权图的最小生成树&#xff08;Minimum Spanning Tree, MST&#xff09;的经典算法。这种算法是由约瑟夫克鲁斯卡尔&#xff08;Joseph Kruskal&#xff09;提出的&#xff0c;并且适用于所有…

七夕情人节动态花瓣表白网页源码

软件介绍 七夕情人节动态花瓣表白网页源码是一款功能强大的表白网页程序&#xff0c;拥有超级好看的花瓣表白效果和多种不同的动态切换效果&#xff0c;非常适合七夕表白活动。 拥有独特的设计风格和花瓣表白效果&#xff0c;可以让用户在表白过程中更加浪漫和温馨。该程序还…