1.创建函数和删除
- 动态创建为FreeRTOS分配的堆栈(方便),而静态创建为人为分配空间。动态应用多
- 任务中必须有while(1)否则只会执行一次
- 任务中的延时要用 vTaskDelay(500); 延时期间执行其它任务
-
任务中的延时使用的是软件延时,即还是让CPU等待来达到延时的效果。使用RTOS的优势就是能充分发挥CPU的性能,永远不让它闲着。任务如果需要延时,也就不能再让CPU空等来实现延时的效果。RTOS中的延时叫作阻塞延时,即任务需要延时时,会放弃CPU的使用权,CPU可以去做其他的事情,当任务延时时间到,重新获取CPU使用权,任务继续运行,这样就充分地利用了CPU的资源,而不是空等
当任务需要延时而进入阻塞状态时,那CPU在做什么?如果没有其他任务可以运行,RTOS都会为CPU创建一个空闲任务,这个时候CPU就运行空闲任务。在FreeRTOS中,空闲任务是系统在启动调度器时创建的优先级最低的任务,空闲任务主体主要是做一些系统内存的清理工作。
-
- 任务创建后马上就执行,一个MCU能够支持多少任务,取决于RAM空间的大小。
- FreeRTOS支持抢占式和时间片两种方式同时运行。
- 进入临界区,不使用任务调度,退出临界区,开始任务调度
- 关于堆栈大小可以使用这个函数:
xGetFreeStackSpace 任务栈历史剩余最小值
动态创建任务
#define Led2_PRIO 1 /* 任务优先级 */
#define Led2_STACK_SIZE 128 /* 任务堆栈大小*/
TaskHandle_t Led2_task_handler; /* 任务句柄 */
void Led2Task(void *argument); /* 任务函数声明 */ xTaskCreate((TaskFunction_t ) Led2Task, /* 任务函数 */(char * ) "Led2", /* 任务名字 */(configSTACK_DEPTH_TYPE ) Led2_STACK_SIZE, /* 堆栈大小 */(void * ) NULL, /* 入口参数 */(UBaseType_t ) Led2_PRIO, /* 任务优先级 */(TaskHandle_t * ) &Led2_task_handler ); /* 任务句柄 */
任务函数
void Led2Task(void *argument)
{/* USER CODE BEGIN LedTask *//* Infinite loop */for(;;){LED2_ON;vTaskDelay(500);LED2_OFF;vTaskDelay(500);}/* USER CODE END LedTask */
}
创建和删除任务
vTaskDelete(task1_handler); //填写任务句柄
// 删除任务if(task1_handler!= NULL){printf("删除start_task任务\r\n");vTaskDelete(task1_handler); //填写任务句柄task1_handler = ZERO;}//创建任务if(Led2_task_handler == NULL){xTaskCreate(Led2Task,"Led2",Led2_STACK_SIZE, ZERO,Led2_PRIO,&Led2_task_handler ); }vTaskDelete(null); Delete your own tasks
保护任务:
// 保护任务taskENTER_CRITICAL(); /* 进入临界区 *//* 被保护的代码 */taskEXIT_CRITICAL(); /* 退出临界区 */
静态创建:
静态创建时,任务句柄作为返回值。
#define Led3_PRIO osPriorityLow /* 任务优先级 */
#define Led3_STACK_SIZE 128 /* 任务堆栈大小*/
TaskHandle_t Led3_task_handler; /* 任务句柄 */
StackType_t Led3_task_stack[Led3_STACK_SIZE]; /* 任务堆栈*/
StaticTask_t Led3_task_tcb; /* 任务控制块*/
void Led3Task(void *argument); /* 任务函数声明 */ Led3_task_handler = xTaskCreateStatic( (TaskFunction_t ) Led3Task, /* 任务函数 */(char * ) "Led3", /* 任务名字 */(uint32_t ) Led3_STACK_SIZE, /* 堆栈大小 */(void * ) NULL, /* 入口参数 */(UBaseType_t ) Led3_PRIO, /* 任务优先级* /(StackType_t * ) Led3_task_stack, /* 任务堆栈 */(StaticTask_t * ) &Led3_task_tcb ); /* 任务名字 */
2.任务挂起和恢复
- 任务优先级越大,优先程度越高,中断优先级数值越小,优先级越高。中断中优先级恢复任务中 , 中断优先级在5—15级
vTaskSuspend(TaskHandle_t xTaskToSuspend) //任务挂起 ——任务句柄
vTaskResume(TaskHandle_t xTaskToResume) //任务恢复 ——任务句柄
xTaskResumeFromISR(TaskHandle_t xTaskToResume) //中断中任务恢复 ——任务句柄
挂起任务与恢复任务:
vTaskSuspend(LedHandle); vTaskResume(LedHandle);
3.中断:
STM32 总共有16个优先级(0—15)
- 中断优先级在5—15级的中断中才能调用FreeRTOS中的函数,小于5的中断无法使用。且要使用尾部为FromISR的函数。
- 对 5—15 等级中断的管理
- vTaskDelay(500); 会打开中断
portENABLE_INTERRUPTS(); //开启中断portDISABLE_INTERRUPTS(); //关闭中断
5.临界段代码保护:
同一时间只能有一个人使用的资源,被称为临界资源。比如任务A、B都要使用串口来打印,串口就是临界资源。如果A、B同时使用串口,那么打印出来的信息就是A、B混杂,无法分辨。所以使用串口时,应该是这样:A用完,B再用;B用完,A再用。
什么是临界段:
临界段,用一句话概括就是一段在执行时不能被中断的代码段。在FreeRTOS中,临界段最常出现的地方就是对全局变量的操作。全局变量就像是一个靶子,谁都可以对其开枪,但是有一人开枪,其他人就不能开枪,否则就不知道是谁命中了靶子。
那么什么情况下临界段会被打断?一个是系统调度,还有一个就是外部中断。在FreeRTOS中,系统调度最终也是产生PendSV中断,在PendSV Handler中实现任务的切换,所以还是可以归结为中断。既然这样,FreeRTOS对临界段的保护最终还是回到对中断的开和关的控制。
作用: 防止中断和任务调度的打断。
应用方面:
- 通信时候,IIC SPI
- 系统自身需求
- 用户需求
特点:
- 成对使用
- 主要通过关闭和开启中断实现,和开启和关闭任务调度器
- 被保护的代码一定要短
- 关闭中断,即所有中断都不能执行;而关闭任务调度器,可以任务无法抢占,而可以中断可以打断。
//开启和关闭任务调度器
vTaskSuspendAll() ;
{… … /* 内容 */
}
xTaskResumeAll() ;// 代码中使用,关闭所有中断
taskENTER_CRITICAL() ;
{… … /* 临界区 */
}
taskEXIT_CRITICAL() ;// 中断中使用,关闭所有中断
uint32_t save_status;
save_status = taskENTER_CRITICAL_FROM_ISR();
{… … /* 临界区 */
}
taskEXIT_CRITICAL_FROM_ISR(save_status );
6.列表和列表项
- 列表和C语言中的链表类似
- 列表项为列表的子集
- FreeRTOS内部是一个双向环形列表
- 链表可以随意删除和加入成员,而且成员间地址并不是连续的,成员数字可以随意更改。
- 每个列表项就是一个任务
7.任务间通信
- 队列:
- 先进先出
- 用于传递信息
- 事件组:
- 信号量:
- 任务通知:
- 互斥量: