移植到STM32中FreeRTOS的业务创建与删除,挂起与恢复

API函数

描述

xTaskCreate()

动态方式创建任务,FreeRTOS自动管理内存

xTaskCreateStatic()

静态方式创建任务,手动分配内存,适用精细控制

vTaskDelete()

删除任务

API函数

描述

vTaskSuspend()挂起任务, 类似暂停,可恢复

vTaskResume()

恢复被挂起的任务
xTaskResumeFromISR()在中断中恢复被挂起的任务

启动调度器:

vTaskStartScheduler();

//自动帮我们创建空闲调度和软件定时器调度等
/*问是每一次创建新任务都要写一次启动调度器吗?不是的,启动调度器就相当去给FreeRTOS开机,用一次即行*/ 

 动态创建任务函数:

#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 128 )
/*最小栈大小(128*4个字节内存空间),单位4字节,
32位平台单位就是4字节,如果是16位平台单位就是2字节*/
  •  使用动态分配的前提
    • 将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1。
    • 定义函数入口参数。(准备用xTaskCreate服务的函数)
    • 编写任务函数。(创建对应的xTaskCreate函数)
  • 此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。

在keil5工程中,ctrl+f填写对应内容找到配置支持动态分配的宏定义,发现其是有默认值1的 

void start_task(void * pvParameters){}
/*1.创建一个启动任务*/
xTaskCreate((TaskFunction_t)start_task,//FreeRTOS为start_task函数服务
      (char *)"stare_task", //任务别名,我一般选择同名好记住
      (uint16_t) START_TASK_STACK, //给函数创建的资源任务栈的大小
     (void *) NULL,//为start_tack函数形参,不传参就置空NULL
      (UBaseType_t) START_TASK_Priority, //执行优先级,数值越大优先级越高
      (TaskHandle_t *) &start_task_handle);//叫句柄其实就是会将TCP地址赋值给它,即启动任务的地址

返回值: 

pdPASS:任务创建成功。 

errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:任务创建失败。

 任务删除函数:

在函数体内写该函数:传入参数NULL表示执行完自己的所有代码后删除自己,传入其他函数的句柄表示删除其他函数

用于删除已被创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除

注意:使用删除函数,空闲任务会负责释放被删除任务中由系统分配的内存(例如:动态创建任务的内存由FreeRTOS分配算法实现就属于系统内存和静态创建任务内存),但是由用户在任务删除前申请的内存(静态创建任务中我们手动分配的内存),则需要由用户在任务被删除前提前释放,否则将导致内存泄露。

如果删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行(目的:实现先执行完自身的所有代码后再实现彻底的删除自身);如果删除其他任务(直接删除),释放内存,任务数量--。

void vTaskDelete( TaskHandle_t xTaskToDelete )

使用前提和方法:

需将宏INCLUDE_vTaskDelete 配置为 1(默认值是关闭的)

入口参数输入需要删除的任务句柄(NULL代表删除本身)

代码例子:(涵盖动态创建任务,删除任务,进入/退出临界区,启用调度器)

思路:

1.创建启动任务(创建其他任务) 

#ifndef __FREERTOS_DEMO_H
#define __FREERTOS_DEMO_H
/*注意FREERTOS的优先级和32优先级的数值定义不同,FREERTOS是数大的优先级高*/
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"
#include "main.h"
#include "stdio.h"



void freertos_start(void);
void task1(void * pvParameters);
void task2(void * pvParameters);
void task3(void * pvParameters);
void start_task(void * pvParameters);

#endif

注意创建任务传参我们不需要记,知道如何用通过跳转到定义,复制定义内容到实际操作文件,复制后根据形参提示更改实参即可

#include "FreeRTOS_demo.h"


#define START_TASK_STACK 128//栈内存空间
#define START_TASK_Priority 1//优先级,FreeRTOS的优先级是数值越高优先级越大
TaskHandle_t  start_task_handle;//定义句柄变量

#define START_TASK_STACK 128
#define TASK1_Priority 3
TaskHandle_t  task1_handle;


 #define START_TASK_STACK 128
 #define TASK2_Priority 4
 TaskHandle_t  task2_handle;

#define START_TASK_STACK 128
#define TASK3_Priority 5
TaskHandle_t  task3_handle;


/**
 * @description: 启动Freertos
 * @return: {*}
 */
void freertos_start(void){
/*1.创建一个启动任务*/

//强转实参确保安全性

xTaskCreate((TaskFunction_t)start_task,//创建任务其对应执行函数是
      (char *)"stare_task", //任务别名
      (uint16_t) START_TASK_STACK, //资源任务栈的大小
     (void *) NULL,//函数形参
      (UBaseType_t) START_TASK_Priority, //执行优先级
      (TaskHandle_t *) &start_task_handle);//叫句柄其实就是会将TCP地址赋值给它,即启动任务的地址
/*2.启动调度器*/
vTaskStartScheduler();//自动帮我们创建空闲调度和软件定时器调度等
/*问是每一次创建新任务都要写一次启动调度器吗?不是的,启动调度器就相当去给FreeRTOS开机,用一次即行*/
}
/**
 * @description:开始任务就起到个创建多任务的作用,用完就丢掉
 * @return: 返回值说明
 */
void start_task(void * pvParameters){
    taskENTER_CRITICAL();/* 进入临界区 ,避免顺序乱套*/
    xTaskCreate((TaskFunction_t)task1,//创建任务其对应执行函数是
    (char *)"task1", //任务别名
    (uint16_t) START_TASK_STACK, //资源任务栈的大小
   (void *) NULL,//函数形参
    (UBaseType_t) TASK1_Priority, //执行优先级
    (TaskHandle_t *) &task1_handle);//叫句柄其实就是会将TCP地址赋值给它,即启动任务的地址

     xTaskCreate((TaskFunction_t)task2,//创建任务其对应执行函数是
       (char *)"task2", //任务别名
       (uint16_t) START_TASK_STACK, //资源任务栈的大小
       (void *) NULL,//函数形参
       (UBaseType_t) TASK2_Priority, //执行优先级
       (TaskHandle_t *) &task2_handle);//叫句柄其实就是会将TCP地址赋值给它,即启动任务的地址

      xTaskCreate((TaskFunction_t)task3,//创建任务其对应执行函数是
      (char *)"task3", //任务别名
      (uint16_t) START_TASK_STACK, //资源任务栈的大小
     (void *) NULL,//函数形参
      (UBaseType_t) TASK3_Priority, //执行优先级
      (TaskHandle_t *) &task3_handle);//叫句柄其实就是会将TCP地址赋值给它,即启动任务的地址

vTaskDelete(NULL);//删除自身是逻辑删除,执行完自身一遍时才会真正删除
 taskEXIT_CRITICAL();/* 退出临界区 */
}

2.写具体实现函数体

/**
 * @description: 实现LED1每500ms闪烁一次
 * @return: 返回值说明
 */
void task1(void * pvParameters){
    while(1){
        char *msg1 = "任务1\r\n";
        HAL_UART_Transmit(&huart1,(uint8_t *)msg1,sizeof(msg1)-1,0xff);
 HAL_GPIO_TogglePin(GPIOA,LED1_Pin);
//    //开中断
//    if(HAL_GPIO_ReadPin(GPIOA,KEY4_Pin)==0){
//        HAL_Delay(10);//阻塞调度不让时间片
//        if(HAL_GPIO_ReadPin(GPIOA,KEY4_Pin)==0){
//   printf("关中断\r\n");
//    portDISABLE_INTERRUPTS();//会将5~15的中断优先级屏蔽掉
}
vTaskDelay(1000);//让出时间片,实现任务调度
}
/**
 * @description: 实现LED2每500ms闪烁一次
 * @return: 返回值说明
 */
 void task2(void * pvParameters){

     while(1){
         char *msg2 = "任务2";
     HAL_UART_Transmit(&huart1,(uint8_t *)msg2,sizeof(msg2)-1,0xff);
         HAL_GPIO_TogglePin(GPIOA,LED2_Pin);
     vTaskDelay(1000);
     }
 }
/**
 * @description: 按下KEY1删除task1
 * @return: 返回值说明
 */
void task3(void * pvParameters){

    while(1){
        char *msg2 = "T3";
    HAL_UART_Transmit(&huart1,(uint8_t *)msg2,sizeof(msg2)-1,0xff);
        if(HAL_GPIO_ReadPin(GPIOA,KEY1_Pin)==0){
         HAL_Delay(10);//阻塞调度不让时间片
         if(HAL_GPIO_ReadPin(GPIOA,KEY1_Pin)==0){
 if(task1_handle!=NULL){
             vTaskDelete(task1_handle);//删除后让句柄置空,就不会反复执行删除函数
             task1_handle=NULL;
          }}
开中断
//					 char *msg2 = "开中断....\r\n";
//           HAL_UART_Transmit(&huart1,(uint8_t *)msg2,sizeof(msg2)-1,0xff);
//portENABLE_INTERRUPTS();
        }
    vTaskDelay(10);//让时间片,其底层逻辑是进入临界区(关中断)和退出临界区(通过开中断实现开中断)
    }
}

 进入临界区:

进入临界区,避免顺序乱套,例如创建启动函数去创建3个任务,成功创建任务1,由于任务1 比启动任务的优先级高,所以启动任务就被挂起,任务1就开始运行,等到释放出时间片时,才会轮到启动任务去创建任务2和3,但是我们原先设定的逻辑的优先级任务3>任务2>任务1>启动任务,你还没有创建完所有任务,就开始工作了,赶鸭子上架,不符合逻辑,所以要用临界区来限制

 taskENTER_CRITICAL();/* 进入临界区 ,避免顺序乱套*/

退出临界区: 

 taskEXIT_CRITICAL();/* 退出临界区*/

静态创建任务函数:

TaskHandle_t xTaskCreateStatic
( 
    TaskFunction_t pxTaskCode,          /* 指向任务函数的指针 */
    const char * const pcName,          /* 任务函数名 */
    const uint32_t ulStackDepth,        /* 任务堆栈大小,单位是4字节 */
    void * const pvParameters,          /* 传递的任务函数参数 */
    UBaseType_t uxPriority,             /* 任务优先级 */
    StackType_t * const puxStackBuffer, /* 任务堆栈,一般为数组,由用户分配 */
    StaticTask_t * const pxTaskBuffer   /* 任务控制块指针,由用户分配 */
)

返回值:

NULL:用户没有提供相应的内存,任务创建失败。

其他值:任务句柄,任务创建成功。

实现步骤:

(1)将宏configSUPPORT_STATIC_ALLOCATION 配置为 1。

(2)定义空闲任务&定时器任务的任务堆栈及TCB。

(3)实现接口函数:

vApplicationGetIdleTaskMemory()应用获取空闲任务内存

vApplicationGetTimerTaskMemory()(如果开启软件定时器)应用获取定时器任务内存

(4)定义函数入口参数。

(5)编写任务函数。 此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。 

按要求定义变量还要注意数据类型 我们可以查看下:转到声明可以看到StackType_t的数据类型是uint32_t 

 以此类推StaticTask_t类型是一个结构体类型

数值长度就是栈深度,FreeRTOS的configFreeRTOS.h文件(里面都是配置FreeRTOS的要求)有要求

3

那么我们就将任务堆栈的大小也定成FreeRTOS可以接收的最小值,具体分配看你业务要求

一下代码和动态创建的业务逻辑一致,所以例子只举了需要手动创建内存的部分

在FreeRTOS.h文件中开启配置支持静态创建任务

#define configSUPPORT_STATIC_ALLOCATION 1 

静态创建方式需要实现的两个接口函数: 

关于创建静态任务还要开启vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );函数:一下是函数的要求

 


    void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                        StackType_t ** ppxIdleTaskStackBuffer,
                                        uint32_t * pulIdleTaskStackSize ); 

 使用软件定时器,会创建定时器任务,帮助我们创建静态任务

在FreeRTOS.h文件中开启,开启后启动任务调度器(vTaskStartScheduler();),就会自动创建空闲任务和软件定时器

//开启软件定时器
#define configUSE_TIMERS 1

 

关于创建静态任务还要开启vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize );函数:一下是函数的要求 

vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
                                         StackType_t ** ppxTimerTaskStackBuffer,
                                         uint32_t * pulTimerTaskStackSize );

在FreeRTOS.h文件中添加软件定时器相关定义: 

/* 软件定时器相关定义 */
#define configUSE_TIMERS                                1                               /* 1: 使能软件定时器, 默认: 0 */
#define configTIMER_TASK_PRIORITY                       ( configMAX_PRIORITIES - 1 )    /* 定义软件定时器任务的优先级, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_QUEUE_LENGTH                        5                               /* 定义软件定时器命令队列的长度, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_TASK_STACK_DEPTH                    ( configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小, 无默认configUSE_TIMERS为1时需定义 */

//按要求定义变量还要注意数据类型
 //StackType_t * const puxStackBuffer, /* 任务堆栈,一般为数组,由用户分配 */
StackType_t buffer[configMINIMAL_STACK_SIZE];

  //  StaticTask_t * const pxTaskBuffer   /* 任务控制块指针,由用户分配 */
 StaticTask_t * stack_task_tcb;



task1_handle = xTaskCreateStatic((TaskFunction_t)task1,//创建任务其对应执行函数是
    (char *)"task1", //任务别名
    (uint16_t) START_TASK_STACK, //资源任务栈的大小
   (void *) NULL,//函数形参
    (UBaseType_t) TASK1_Priority, //执行优先级
  (StackType_t *)  buffer, //静态任务的任务栈
    (StaticTask_t *) stack_task_tcb //静态任务的TCB结构体 /* 任务控制块指针,由用户分配, */
)
//这两个函数创建出来即可,不用在主函数中调用,就像中断处理函数一样,存在即会被调用
//分配空闲任务的资源

    void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                        StackType_t ** ppxIdleTaskStackBuffer,
                                        uint32_t * pulIdleTaskStackSize )
{

 * ppxIdleTaskTCBBuffer=&buffer;
 * ppxIdleTaskStackBuffer=&stack_task_tcb
 * pulIdleTaskStackSize=configMINIMAL_STACK_SIZE
}

//按要求定义变量还要注意数据类型
 //StackType_t * const puxStackBuffer, /* 任务堆栈,一般为数组,由用户分配 */
StackType_t timer_task[ configTIMER_TASK_STACK_DEPTH ]

  //  StaticTask_t * const pxTaskBuffer   /* 任务控制块指针,由用户分配 */
 StaticTask_t * stack_timer_task_tcb;

//分配软件定时器任务的资源
vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
                                         StackType_t ** ppxTimerTaskStackBuffer,
                                         uint32_t * pulTimerTaskStackSize )

{
* ppxTimerTaskTCBBuffer=&timer_task;
* ppxTimerTaskStackBuffer=&stack_timer_task_tcb;
pulTimerTaskStackSize= configTIMER_TASK_STACK_DEPTH ;
}

任务挂起:

void vTaskSuspend( TaskHandle_t xTaskToSuspend )

使用方式:

xTaskToSuspend:待挂起任务的任务句柄,为NULL表示挂起任务自身。

需将宏INCLUDE_vTaskSuspend配置为 1

任务恢复和任务挂起搭配使用: 

void vTaskResume( TaskHandle_t xTaskToResume )//传入任务句柄

 不论任务被使用 vTaskSuspend() 挂起多少次,只需调用 vTaskResume() 一次,即可使其继续执行。被恢复的任务会重新进入就绪状态

 中断中恢复

BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume )//传入任务句柄

使用条件:INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 必须定义为 1。

在中断服务程序中调用FreeRTOS的API函数时,中断的优先级不能高于FreeRTOS所管理的最高中断优先级。

 调度器的恢复挂起和恢复:

vTaskSuspendAll():挂起任务调度器,调度器不会进行任务切换,当前任务一直运行。

xTaskResumeAll():恢复任务调度器,调度器继续任务切换。

查看任务状态: 

void vTaskList( char * pcWriteBuffer )

使用条件: 

在FreeRTOSconfig.h中定义宏,并将其置1:

#define configUSE_TRACE_FACILITY 1

#define configUSE_STATS_FORMATTING_FUNCTIONS 1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值