1、为啥使用FreeRTOS操作系统
还没有说明为啥使用Freertos操作系统啊,市面上那么多操作系统Linux、uCOS、VxWorks…等等,其实有一块linux开发板,嫌弃接线太多了,每次准备大半天,懒得弄它了,等以后有空余地方了在弄它吧….后来就买了块stm32C8T6最小板,一个最小板,一个下载器,加一个串口线,搞定了,后来查了查学哪个操作系统时候发现Freertos啥啥简单啥的,文件少啊占用RAM小啊啥的,突然看到免费!!!,嗯嗯…就是它了。
最开始接触嵌入式时候,我使用的是51单片机进行裸机开发,一般都是在主函数main里面用while(1)做个大循环来完成所有的任务处理,有时候也需要一些中断来完成一些处理操作。这相对于多任务系统而言,就是所谓的单任务系统,也叫做前后台程序,中断函数为前台程序,大循环为后台程序。如图1所示。前后台程序实时性比较差,所有任务都是轮流执行,无论任务模块多么重要,只有没有轮到你,你就得乖乖的等着。但是前后台程序简单啊,资源消耗少啊,这就是它十足的优点。但是随着项目越来越复杂,应用程序越来越大,前后台程序就会慢慢的显得力不从心了。此时多任务系统的优势就会慢慢的体现出来了。
图 1 前后台程序
多任务系统会将大任务化小任务,大事化小,小的就各个击破,从而将大任务解决。各个小任务是“并行”执行的,之所以加引号是因为看上去是同一时刻执行了好多任务似的,实际上CPU在任意时刻只能执行单一任务,而每个任务执行时间又很短,所以导致看上去像每一时刻执行了多个任务一样。多任务系统将会面临一个新的问题,将大任务分解成那么多小任务,这些小任务执行顺序怎么决定啊,哪个先执行,哪个后执行?完成这个功能的便是操作系统的“大脑——任务调度器,不同的操作系统任务的任务调度器实现的方法是不一样的,Freertos 的任务调度器是抢占式的,如图2 所示
图 2 抢占式多任务系统
2、Freertos操作系统任务状态
Freertos操作系统主要有运行态、就绪态、挂起态和阻塞态,相关状态之间转换如图3所示。
- 运行态:当一个任务正在运行时,那么它便处于运行态,处于运行态的任务就是当前正在使用CPU的任务,stm32C8T6单片机是单核的,那么任意时刻只有一个任务处于运行态。
- 就绪态:处在就绪态的任务就是那些已经准备好运行的任务(没有被挂起或阻塞),为啥它没有运行呢,因为CPU此时被同一优先级或更高优先级占用着。
- 阻塞态:一个任务正在等待某个外部事件的话就说它处于阻塞态,例如调用vTaskDelay函数就会进入阻塞态,直到延时结束,任务在等待队列、信号量、事件组、通知或互斥信号量时也会进入阻塞态,任务进入阻塞态后会有个延时时间,当时间超过这个延时时间就会退出阻塞态,即使等待的事件没有到来。
- 挂起态;任务被挂起后调度器不能被调度器调用进入运行态,但是挂起态没有超时,任务挂起和任务退出挂起用vTaskSuspend和vTaskResume函数,后面使用时会详细介绍。
Freertos官网https://www.freertos.org/上有本开发手册《FreeRTOS_Reference_Manual_V10.0.0》讲解了Freertos操作系统中的API函数。具体使用在开发手册中有详细介绍。
图3 Freertos操作系统任务状态
3、Freetos创建第一个任务
创建第一个任务是需要使用到的函数主要有xTaskCreate(动态创建任务),vTaskDelay, vTaskStartScheduler。
创建任务函数:
1BaseType_t xTaskCreate(
2 TaskFunction_tpxTaskCode,
3 const char * constpcName,
4 constconfigSTACK_DEPTH_TYPE usStackDepth,
5 void* const pvParameters,
6 UBaseType_tuxPriority,
7 TaskHandle_t* const pxCreatedTask )
8
参数:
- pxTaskCode:指向的是需要执行某个特定任务的C语言函数的指针,即任务函数的函数名,需要特殊说明的是该任务函数是个无限循环的函数,不要用return等返回,不需要执行该任务时候有其他方式,以后会说到。
- pcName:任务的描述名称,方便调试,这里不做过多解释,需要说明一点名字的长度不能超过configMAX_TASK_NAME_LEN定义的字符串的最大长度。
- usStackDepth:任务的堆栈,每个任务都有自己唯一的堆栈,在创建任务时,告诉内核自己的堆栈多大,然后内核给该任务分配堆栈。需要注意的是内核实际分配给任务的堆栈是usStackDepth的四倍。
- pvParameters:需要传递给任务的参数,若不需要传递参数时,设置为NULL。
- uxPriority:任务的优先级,该任务在整个项目所有任务中执行的次序, 其数值可以从0~ (configMAX_PRIORITIES– 1),数值越大,优先级越高。
- pxCreatedTask:该任务的句柄,该句柄可以被其他API函数使用,若不需要该任务句柄,设置为NULL。
返回值:
pdPASS:任务创建成功;
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:显示没有足够的堆栈空间创建任务;
延时函数:
void vTaskDelay(TickType_t xTicksToDelay ):函数设置任务的延时时间,当使用该延时函数时,该任务阻塞,直到延时结束。
参数:xTicksToDelay延时时间参数
任务调度函数:
voidvTaskStartScheduler( void ):开启Freertos任务调度器。
4、测试
创建两个任务分别打印任务调用次数,测试代码如下:
1#include "delay.h"
2#include "USART.h"
3#include "led.h"
4#include "key.h"
5//操作系统
6#include "FreeRTOS.h"
7#include "task.h"
8#include "queue.h"
9
10/***************函数声明区*******************/
11
12#define START_TASK_PRIO 1
13#define START_STK_SIZE 128
14TaskHandle_t StartTask_Handler;
15void start_task(void *pvParameters);
16
17
18#define TASK1_TASK_PRIO 2
19#define TASK1_STK_SIZE 128
20TaskHandle_t Task1Task_Handler;
21void task1_task(void *pvParameters);
22
23
24#define TASK2_TASK_PRIO 3
25#define TASK2_STK_SIZE 128
26TaskHandle_t Task2Task_Handler;
27void task2_task(void *pvParameters);
28
29/*************************************************/
30int main(void)
31{
32/*驱动相关初始化*/
33NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
34delay_init();
35LED_Init();
36USART1_Config();
37KEY_Init();
38/*************UserFunction*********************/
39 xTaskCreate((TaskFunction_t )start_task,
40 (const char* )"start_task",
41 (uint16_t )START_STK_SIZE,
42 (void* )NULL,
43 (UBaseType_t )START_TASK_PRIO,
44 (TaskHandle_t* )&StartTask_Handler);
45 vTaskStartScheduler();
46}
47
48void start_task(void *pvParameters)
49{
50
51 xTaskCreate((TaskFunction_t )task1_task,
52 (const char* )"task1_task",
53 (uint16_t )TASK1_STK_SIZE,
54 (void* )NULL,
55 (UBaseType_t )TASK1_TASK_PRIO,
56 (TaskHandle_t* )&Task1Task_Handler);
57
58 xTaskCreate((TaskFunction_t )task2_task,
59 (const char* )"task2_task",
60 (uint16_t )TASK2_STK_SIZE,
61 (void* )NULL,
62 (UBaseType_t )TASK2_TASK_PRIO,
63 (TaskHandle_t* )&Task2Task_Handler);
64 vTaskDelete(StartTask_Handler);
65}
66
67//task1
68void task1_task(void *pvParameters)
69{
70uint8_t n=0;
71while(1)
72{
73 n++;
74 printf("task1.......%drn",n);
75 vTaskDelay(1000);
76}
77}
78
79//task2
80void task2_task(void *pvParameters)
81{
82 uint8_t i=0;
83while(1)
84{
85 i++;
86 printf("task2.......%drn",i);
87 vTaskDelay(1000);
88}
89}
测试结果如下:
为啥任务2先输出嘞?因为任务2优先级高于任务1,所以任务二首先输入打印次数。
有没有小伙伴好奇为啥单片机中能够调用prinf(“*****%dnr”),下一篇会告诉你哦~
顺便为自己的微信公众号打个广告,欢迎关注,互相讨论,互相学习。
http://weixin.qq.com/r/Zi1JUfTE8qmNrbAy93h0 (二维码自动识别)