FreeRTOS静态方法实验两个LED灯闪烁

已经移植好 FreeRTOS 工程模板,本实验就在该模板基础上增加功能代码,

可将工程模板复制一份重新命名为“任务创建--静态方法” 本章所要实现的功能是:使用静态内存方法创建 2 个任务,分别控制 2 个 LED 闪烁

1定义任务函数

任务实际上就是一个无限循环且不带返回值的 C 函数。我们创建 2 个这样 的任务,让开发板上面的 LED 灯以 1S 的频率闪烁,具体实现代码如下。

//任务 1 函数
void task1_task(void *pvParameters)
{
while(1)                  //(1)
{
LED1=0;
vTaskDelay(200);                //(2)
LED1=1;
vTaskDelay(800);
}
}
//任务 2 函数
void task2_task(void *pvParameters)
{
while(1)
{
LED2=0;
vTaskDelay(800);
LED2=1;
vTaskDelay(200);
}
}

代码(1):任务必须是一个死循环,否则任务将通过 LR 返回,如果 LR 指 向了非法的内存就会产生 HardFault_Handler,而 FreeRTOS 指向一个死循环, 那么任务返回之后就在死循环中执行,这样子的任务是不安全的,所以为避免这 种情况,任务一般都是死循环并且无返回值的。

代码(2):任务里面的延时函数必须使用 FreeRTOS 里面提供的延时函数, 并不能使用我们裸机编程中的那种延时。这两种的延时的区别是 FreeRTOS 里面 的延时是阻塞延时,即调用 vTaskDelay()函数的时候,当前任务会被挂起,调 度器会切换到其它就绪的任务,从而实现多任务。如果还是使用裸机编程中的那 种延时,那么整个任务就成为了一个死循环,如果恰好该任务的优先级是最高的, 那么系统永远都是在这个任务中运行,比它优先级更低的任务无法运行,根本无 法实现多任务。但 FreeRTOS 中的延时最小值只能是 ms,对于 us 级别的延时怎 么办呢,这个在前面移植操作系统时我们介绍过,使用 SysTick.c 文件中的延时 函数亦可,包括 ms 和 us 级延时,这两个延时函数与裸机实验中是不一样的,所 以不要搞混

2 空闲任务与定时器任务堆栈函数

当我们使用了静态创建任务的时候,configSUPPORT_STATIC_ALLOCATION 这 个宏定义必须为 1(在 FreeRTOSConfig.h 文件中),并且我们需要实现两个函 数:vApplicationGetIdleTaskMemory()与 vApplicationGetTimerTaskMemory(),这两个函数是用户设定的空闲(Idle)任 务与定时器(Timer)任务的堆栈大小,必须由用户自己分配,而不能是动态分 配,具体代码如下

/* 空闲任务任务堆栈 */
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,
98
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;/* 任务堆栈大小
*/
}

3 定义任务栈和任务控制块

目前我们只创建了 2 个任务,当任务进入延时的时候,因为没有另外就绪的 用户任务,那么系统就会进入空闲任务,空闲任务是 FreeRTOS 系统自己启动的 一个任务,优先级最低。当整个系统都没有就绪任务的时候,系统必须保证有一 个任务在运行,空闲任务就是为这个设计的。当用户任务延时到期,又会从空闲 任务切换回用户任务。 在 FreeRTOS 系统中,每一个任务都是独立的,他们的运行环境都单独的保 存在他们的栈空间当中。那么在定义好任务函数之后,我们还要为任务定义一个 栈,目前我们使用的是静态内存,所以任务栈是一个独立的全局变量,具体见代 码。任务的栈占用的是 MCU 内部的 RAM,当任务越多的时候,需要使用的栈空 间就越大,即需要使用的 RAM 空间就越多。一个 MCU 能够支持多少任务,就得 看芯片的 RAM 空间有多少。

//任务堆栈大小
#define TASK1_STK_SIZE 128
//任务堆栈
StackType_t Task1TaskStack[TASK1_STK_SIZE];
//任务堆栈大小
#define TASK2_STK_SIZE 128
//任务堆栈
StackType_t Task2TaskStack[TASK2_STK_SIZE];

在大多数系统中需要做栈空间地址对齐,在 FreeRTOS 中是以 8 字节大小 对齐,并且会检查堆栈是否已经对齐,其中 portBYTE_ALIGNMENT 是在 portmacro.h 里面定义的一个宏,其值为 8,就是配置为按 8 字节对齐,当然 用户可以选择按 1、2、4、8、16、32 等字节对齐,目前默认为 8。 定义好任务函数和任务栈之后,我们还需要为任务定义一个任务控制块,通 常我们称这个任务控制块为任务的身份证。在 C 代码上,任务控制块就是一个 结构体,里面有非常多的成员,这些成员共同描述了任务的全部信息

//任务控制块
StaticTask_t Task1TaskTCB;
//任务控制块
StaticTask_t Task2TaskTCB

4 静态创建任务函数

一个任务的三要素是任务主体函数,任务栈,任务控制块,那么怎么样把这 三个要素联合在一起?FreeRTOS 里面有一个叫静态任务创建函数 xTaskCreateStatic(),它就可以实现。它将任务主体函数,任务栈(静态的) 和任务控制块(静态的)这三者联系在一起,让任务可以随时被系统启动,具体 如下。

//创建开始任务
StartTask_Handler=xTaskCreateStatic((TaskFunction_t )start_task, //任务函数 (1)
(const char* )"start_task",//任务名称 (2)
(uint32_t )START_STK_SIZE,//任务堆栈大小(3)
(void* )NULL,//传递给任务函数的参数 (4)
(UBaseType_t )START_TASK_PRIO, //任务优先级 (5)
(StackType_t* )StartTaskStack,//任务堆栈 (6)
(StaticTask_t* )&StartTaskTCB);//任务控制块 (7

代码(1):任务入口函数,即任务函数的名称,需要我们自己定义并且实现。 代码(2):任务名字,字符串形式,最大长度由 FreeRTOSConfig.h 中定义 的 configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字 最好要与任务函数入口名字一致,方便进行调试。 代码(3):任务堆栈大小,单位为字,在 32 位的处理器下(STM32),一个 字等于 4 个字节,那么任务大小就为 128 * 4 字节。 代码(4):任务入口函数形参,不用的时候配置为 0 或者 NULL 即可。 代码(5):任务的优先级。优先级范围根据 FreeRTOSConfig.h 中的宏 configMAX_PRIORITIES 决定,如果使能 configUSE_PORT_OPTIMISED_TASK_SELECTION,这个宏定义,则最多支持 32 个 优先级;如果不用特殊方法查找下一个运行的任务,那么则不强制要求限制最大 可用优先级数目。在 FreeRTOS 中,数值越大优先级越高,0 代表最低优先级。 代码(6):任务栈起始地址,只有在使用静态内存的时候才需要提供,在使 用动态内存的时候会根据提供的任务栈大小自动创建。 代码(7):任务控制块指针,在使用静态内存的时候,需要给任务初始化函 数 xTaskCreateStatic()传递预先定义好的任务控制块的指针。在使用动态内存 的时候,任务创建函数 xTaskCreate()会返回一个指针指向任务控制块,该任务 控制块是 xTaskCreate()函数里面动态分配的一块内存。

整体代码

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"


//任务优先级
#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);


//任务优先级
#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);


/* 空闲任务任务堆栈 */
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;/* 任务堆栈大小 */
}


/*******************************************************************************
* 函 数 名         : main
* 函数功能           : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
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();          //开启任务调度
}

//开始任务任务函数
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();            //退出临界区
}

//任务1函数
void task1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//任务2函数
void task2_task(void *pvParameters)
{
    while(1)
    {
        LED2=0;
        vTaskDelay(800);
        LED2=1;
        vTaskDelay(200);
    }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值