FreeRTOS学习笔记-基于stm32(2)任务的创建与删除,挂起与恢复

本文详细介绍了在FreeRTOS中动态任务与静态任务的创建方法,包括任务参数定义、堆栈管理、任务删除,以及任务挂起与中断恢复的函数使用。强调了实践的重要性,鼓励读者通过实践加深理解。
摘要由CSDN通过智能技术生成

一、任务创建与删除

1、动态任务创建

xTaskCreate(	TaskFunction_t pxTaskCode,
				const char * const pcName,
				const uint16_t usStackDepth,
				void * const pvParameters,
				UBaseType_t uxPriority,
				TaskHandle_t * const pxCreatedTask );

①:使用此函数前需将宏 configSUPPORT_DYNAMIC_ALLOCATION 配置为置1

#define configSUPPORT_DYNAMIC_ALLOCATION        1                       //支持动态内存申请

②: 定义函数入口参数

        首先进行强制类型转换,避免警告(就是给定义的参数类型带上括号)

        任务函数:就指向任务函数的指针,我此处的任务函数是 void start_task( void * pvParameters ),所以任务函数就是 start_task;

        任务名称:一般与任务函数一致;

        任务堆栈大小:通过 #define TASK1_STACK_SIZE   128 宏定义,将堆栈设置为128字,即128*4字节

        传递给任务函数的参数:无,即NULL;

        任务优先级:通过 #define START_TASK_PRIO         1 宏定义,将优先级设为1;

        任务句柄:定义 TaskHandle_t 类型的任务句柄,将地址传入;

/*START_TASK任务配置*/
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128     //128字=128*4字节
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );

/*入口函数*/
void freertos_demo(void)
{
    xTaskCreate(	(TaskFunction_t )    start_task,                //任务函数
                    (char *         )    "start_task",              //任务名称
                    (uint16_t       )    START_TASK_STACK_SIZE,     //任务堆栈大小
                    (void *         )    NULL,                      //传递给任务函数的参数
                    (UBaseType_t    )    START_TASK_PRIO,           //任务优先级
                    (TaskHandle_t * )    &start_task_handler );     //任务句柄

    vTaskStartScheduler();         //开启任务调度器
}

void start_task( void * pvParameters )
{
    for(::)
    {
        //任务主体
    }
}

③:编写任务函数

        任务将会在一个无限循环中循环执行,可以使用for循环,也可以使用while循环,后者比较简单,笔者喜欢后者。此任务执行的是LED0不断闪烁并在串口打印 “task1正在运行”。

void start_task( void * pvParameters )
{
    while(1)
    {
        printf("task1正在运行\r\n");
        LED0=!LED0;
        vTaskDelay(500);
    }
}

2、静态任务创建

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 );

 ①:使用此函数前需将宏 configSUPPORT_STATIC_ALLOCATION 配置为1

#define configSUPPORT_STATIC_ALLOCATION			1						//只是静态内存

②: 实现空闲任务与软件定时器接口函数

        定义三个参数,任务控制块、任务堆栈、堆栈大小。空闲任务使用的堆栈大小在FreeRTOSConfig.h 中有定义:

#define configMINIMAL_STACK_SIZE				((unsigned short)130)   //空闲任务使用的堆栈大小

        软件定时器任务堆栈大小在FreeRTOSConfig.h 中也有定义:

#define configTIMER_TASK_STACK_DEPTH	        (configMINIMAL_STACK_SIZE*2)    //软件定时器任务堆栈大小

        在函数内部分别将定义好的参数赋值;

/*空闲任务内存分配*/
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,    //任务控制块
                                    StackType_t **ppxIdleTaskStackBuffer,   //任务堆栈
                                    uint32_t *pulIdleTaskStackSize )        //堆栈大小
{
    *ppxIdleTaskTCBBuffer=&idle_task_tcb;
    *ppxIdleTaskStackBuffer=idle_task_stack;
    *pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;
    
}

/*软件内存分配*/
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer,  //任务控制块
                                     StackType_t **ppxTimerTaskStackBuffer, //任务堆栈
                                     uint32_t *pulTimerTaskStackSize )      //堆栈大小
{
    *ppxTimerTaskTCBBuffer=&timer_task_tcb;
    *ppxTimerTaskStackBuffer=timer_task_stack;
    *pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;
}

③:定义函数入口参数

        与动态任务创建类似,不同的是需要再多定义一个用户分配任务堆栈和用户分配任务控制块指针,而动态创建是计算机自己分配的。静态创建好处就是可以自己规定存储位置,坏处就是比较繁琐

        用户分配任务堆栈一般定义为一个数组,数组的大小即任务堆栈的大小:START_TASK_STACK_SIZE

        xTaskCreateStatic()参数中没有句柄,那如果要找到这个任务要怎么办呢?其实它的句柄正是这个函数的返回值!!!所以还要用刚定义的句柄:task1_handler 来接住返回值。

/*START_TASK任务配置*/
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128     //128字=128*4字节
StackType_t  start_task_stack[START_TASK_STACK_SIZE];
TaskHandle_t start_task_handler;
StaticTask_t start_task_tcb;
void start_task( void * pvParameters );

/*入口函数*/
void freertos_demo(void)
{
    //创建开始任务
	start_task_handler=xTaskCreateStatic(   (TaskFunction_t	)start_task,		    //指向任务函数的指针
                                            (const char* 	)"start_task",		    //任务名称
                                            (uint32_t 		)START_TASK_STACK_SIZE,	//任务堆栈大小
                                            (void* 		  	)NULL,				    //传递给任务函数的参数
                                            (UBaseType_t 	)START_TASK_PRIO, 	    //任务优先级
                                            (StackType_t*   )start_task_stack,	    //用户分配任务堆栈,一般为数组
                                            (StaticTask_t*  )&start_task_tcb);	    //用户分配任务控制块指针
    vTaskStartScheduler();         //开启任务调度器
}

void start_task( void * pvParameters )
{
    while(1)
    {
        //任务主体
    }
}

④: 编写任务函数

void start_task( void * pvParameters )
{
    while(1)
    {
        printf("task1正在运行\r\n");
        LED0=!LED0;
        vTaskDelay(500);
    }
}

        此代码与动态任务创建很类似就不做展示了。 

3、动态静态的区别

        动态创建任务:任务的控制块以及任务的栈空间所需的内存均由 FreeRTOS 从 FreeRTOS 管理的堆中分配;

        静态创建任务:任务的任务控制块以及任务的栈空间所需的内存,需要用户分配提供。

4、任务删除

①:使用此函数前需将宏 INCLUDE_vTaskDelete 配置为1

#define INCLUDE_vTaskDelete				        1

②: 调用函数 vTaskDelete();

vTaskDelete(NULL);      //vTaskDelete(start_task_handler);

        当参数为对应任务的句柄时,将删除对应任务;

        若参数为 NULL ,则删除正在执行的任务;

        空闲任务会负责释放被删除任务中由系统分配的内存(动态创建),但由用户在任务删除前申请的内存(静态创建),则需要用户在任务被删除前提前释放,否则将导致内存泄露。

 5、完整代码

        此代码使用动态任务创建的方式,先创建了start_task任务,然后在start_task任务中创建了task1、task2、task3任务。task1 和 task2 用来让 LED0 和 LED1 闪烁,task3 用来判断 KEY0 是否被按下,如果按下则删除 task1。

1、vTaskStartScheduler();         //开启任务调度器

        此函数用来开启任务调度器,在任务调度器开启后,任务调度器就会开始去就绪列表调用任务

2、  taskENTER_CRITICAL();       //进入临界区

        taskEXIT_CRITICAL();        //退出临界区

        此函数用来关闭freertos所管理的中断以保护那些不想被打断的程序段,中断关闭后,任务将不能被调度,必须等退出临界区任务才能被调度

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "timer.h"
#include "lcd.h"
#include "FreeRTOS.h"
#include "task.h"
#include "freertos_demo.h"

/*START_TASK任务配置*/
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128     //128字=128*4字节
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );

/*TASK1任务配置*/
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128     //128字=128*4字节
TaskHandle_t task1_handler;
void task1( void * pvParameters );

/*TASK2任务配置*/
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128     //128字=128*4字节
TaskHandle_t task2_handler;
void task2( void * pvParameters );

/*TASK3任务配置*/
#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128     //128字=128*4字节
TaskHandle_t task3_handler;
void task3( void * pvParameters );


/*入口函数*/
void freertos_demo(void)
{
    xTaskCreate(	(TaskFunction_t )    start_task,                //任务函数
                    (char *         )    "start_task",              //任务名称
                    (uint16_t       )    START_TASK_STACK_SIZE,     //任务堆栈大小
                    (void *         )    NULL,                      //传递给任务函数的参数
                    (UBaseType_t    )    START_TASK_PRIO,           //任务优先级
                    (TaskHandle_t * )    &start_task_handler );     //任务句柄 
    vTaskStartScheduler();         //开启任务调度器
}

void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();       //进入临界区
    printf("start_task正在运行\r\n");
    xTaskCreate(	(TaskFunction_t )    task1,                //任务函数
                    (char *         )    "task1",              //任务名称
                    (uint16_t       )    TASK1_STACK_SIZE,     //任务堆栈大小
                    (void *         )    NULL,                 //传递给任务函数的参数
                    (UBaseType_t    )    TASK1_PRIO,           //任务优先级
                    (TaskHandle_t * )    &task1_handler );     //任务句柄
                    
    xTaskCreate(	(TaskFunction_t )    task2,                //任务函数
                    (char *         )    "task2",              //任务名称
                    (uint16_t       )    TASK2_STACK_SIZE,     //任务堆栈大小
                    (void *         )    NULL,                 //传递给任务函数的参数
                    (UBaseType_t    )    TASK2_PRIO,           //任务优先级
                    (TaskHandle_t * )    &task2_handler );     //任务句柄
                    
    xTaskCreate(	(TaskFunction_t )    task3,                //任务函数
                    (char *         )    "task3",              //任务名称
                    (uint16_t       )    TASK3_STACK_SIZE,     //任务堆栈大小
                    (void *         )    NULL,                 //传递给任务函数的参数
                    (UBaseType_t    )    TASK3_PRIO,           //任务优先级
                    (TaskHandle_t * )    &task3_handler );     //任务句柄
                    
    vTaskDelete(NULL);      //vTaskDelete(start_task_handler);
    taskEXIT_CRITICAL();        //退出临界区
}

void task1( void * pvParameters )
{
    while(1)
    {
        printf("task1正在运行\r\n");
        LED0=!LED0;
        vTaskDelay(500);
    }
}

void task2( void * pvParameters )
{
    while(1)
    {
        printf("task2正在运行\r\n");
        LED1=!LED1;
        vTaskDelay(500);
    }
}

void task3( void * pvParameters )
{
    uint8_t key=0;
    while(1)
    {
        printf("task3正在运行,key=%u\r\n",&key);
        key=KEY_Scan(0);
        if(key==KEY0_PRES)
        {
            if(task1_handler!=NULL)
            {
                printf("task1被删除\r\n");
                vTaskDelete(task1_handler);
                task1_handler=NULL;
            }
        }
        vTaskDelay(10);
    }
}

二、任务挂起与恢复

1、任务挂起函数

        xTaskSuspend()调用后任务将被挂起,不能再执行,直到任务被恢复。

        使用前需将此宏 INCLUDE_vTaskSuspend 配置为1;

        当参数为对应任务的句柄时,将挂起对应任务;

        若参数为 NULL ,则挂起正在执行的任务;

2、任务恢复函数(任务中恢复)

        vTaskResume()调用后,任务被恢复,并进入就绪态。

        使用前需将此宏 INCLUDE_vTaskSuspend 配置为1;

void task3( void * pvParameters )
{
    uint8_t key=0;
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY0_PRES)
        {
            printf("任务挂起");
            vTaskSuspend(task1_handler);
        }else if(key==KEY1_PRES)
        {
            printf("任务解挂");
            vTaskResume(task1_handler);
        }
        vTaskDelay(10);
    }
}

3、任务恢复函数(中断中恢复)

        xTaskResumeFromISR()调用后,任务被恢复,并进入就绪态(专用于中断服务函数)。

        使用前需将宏 INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 都配置为1;

        需判断返回值(返回值类型为:BaseType_t):

        pdTRUE:说明任务优先级大于正在执行的任务,任务恢复后需要进行任务切换;

        pdFALSE:任务恢复后不需要进行任务切换;

        中断服务程序中只要调用了FreeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级FreeRTOS所管理的中断优先级范围是:5~15

        为了方便处理,中断分组需设置到第4组,,即抢占优先级0~15,子优先级0。

extern TaskHandle_t task1_handler;

void EXTI0_IRQHandler(void)        //中断服务函数
{
    delay_xms(10);    //消抖
    BaseType_t xYieldRequired;    //定义变量来接收返回值
	if(WK_UP==1)
	{	
        printf("中断解挂");
		xYieldRequired=xTaskResumeFromISR(task1_handler);
	}
    if(xYieldRequired==pdTRUE)
    {
        portYIELD_FROM_ISR(xYieldRequired);//任务切换
    }
	EXTI_ClearITPendingBit(EXTI_Line0);  //清除EXTI0线路挂起位
}

        由于在中断服务程序中,在 a.c 中定义的变量要在 b.c 中使用,就需要将 task1_handler 定义为全局变量:

extern TaskHandle_t task1_handler;

        任务切换函数:

portYIELD_FROM_ISR(xYieldRequired);//任务切换

三、总结

        大家一定要多动手。不管再简单的知识,再简单的任务,自己动手敲一遍总能遇到一些意想不到的问题。而解决这些问题就是我们不断进步的阶梯!!!

  • 27
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: FreeRTOS是一个开源的实时操作系统内核,为嵌入式设备提供了实时多任务处理、优先级管理和多线程等功能,非常适合一些资源有限的嵌入式设备进行开发。CANopen则是一种通信协议,可以用于各种嵌入式系统中的控制和数据传输,通常用于工业控制和自动化领域。 STM32F407则是意法半导体生产的一款Cortex-M4内核的微控制器,具有高性能、低功耗、高集成度等特点,非常适合应用于需要高要求的嵌入式设备中。而将这三者结合起来,可以实现很多嵌入式应用的功能,提高设备 控制的精度和可靠性。 具体而言,使用FreeRTOS可以使得STM32F407实现多任务处理,并在任务之间进行调度管理,提高系统的运行效率。CANopen则可以方便地实现各种设备之间的通信,实现控制指令和数据传输功能。在整个开发过程中,通过FreeRTOS和CANopen的结合,可以快速开发出高效、可靠的嵌入式系统。 ### 回答2: FreeRTOS是一个免费的实时操作系统内核,可用于大量基于嵌入式系统的微控制器的开发。CanOpen是针对CAN总线的一个通信协议,它能够实现不同的设备之间的通讯。而STM32F407是由ST推出的基于ARM Cortex-M4微控制器的产品系列之一。 在使用STM32F407进行嵌入式系统的开发时,FreeRTOS和CanOpen是非常有用的工具。FreeRTOS可以提供实时多任务支持,从而可以在单个系统中运行多个任务,并减少系统的复杂性。而CanOpen可实现在CAN总线上的设备之间的通信,从而实现大规模设备的控制和监控。 在使用这些工具时,需要注意一些配置和设置,以确保系统和设备的正常运行。例如,需要在STM32F407上启用CAN总线功能和FreeRTOS支持,这需要了解其软件开发工具,如Keil和IAR等。同时,在设备之间设置正确的CanOpen节点ID,使其互相识别并进行通信。 总之,对于使用STM32F407进行嵌入式系统开发的人员来说,FreeRTOS和CanOpen是非常有用的工具。它们可以帮助优化系统性能,提高设备之间的互操作性,从而实现更好的系统控制和管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值