FreeRTOS学习历程
总结:
关键词:动态创建 静态创建 任务级 中断级
一个任务至少有一个 do
{
vtaskDelay(10);
}while(1); //不然程序容易卡死在一个任务中出不来,让CPU有运行空闲任务的机会。
0.0FreeRTOSv9.0移植
0.1任务相关API函数(*打开相应的宏)
*uxTaskPriorityGet("任务句柄"); //获取任务优先级
*vTaskPrioritySet("任务句柄","任务优先级"); //改变某一个任务的优先级
*uxTaskGetSystemState("任务状态","任务状态数组大小","系统中的运行时间");//获取任务中所有任务的状态
*uxTaskGetNumberOfTasks();//获取任务个数
*xTaskGetCurrentTaskHandle();//获取当前任务句柄
*xTaskGetHandle("任务名字");//根据任务名字获取任务句柄
*uxTaskGetStackHighWaterMark("任务句柄");//任务堆栈历史剩余最小值
eTaskGetState("任务句柄");//任务的运行状态(0运行 1就绪 ....)
xTaskGetTickCount();//获取滴答定时器时钟节拍值
相对重要API函数:
vTaskList("定义缓冲区"); //获取所有运行任务的名字 状态(B阻塞 R就绪 S挂起 D删除) 任务优先级 任务堆栈剩余大小 任务运行编号
vTaskGetRunTimeStats:
打开相应的宏:
configGENERATE_RUN_TIME_STATS 1
configUSE_STATS_FORMATTING_FUNCTIONS 1
之后需要自实现两个宏定义:
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ConfigureTimeForRunTimeStats()//定时器3提供时间统计的时基
#define portGET_RUN_TIME_COUNTER_VALUE() FreeRTOSRunTimeTicks //获取时间统计时间值
vTaskGetRunTimeStats("定义缓冲区");//获取所有运行任务的名字 运行时间 占运行总时间的百分比
1.任务的动态创建:
遇到错误:( char i=0;//必须要赋值初始值不然为随机数 )
动态创建任务API:
//任务创建配置参数
#define START_TASK_PRIO 1 //优先级
#define START_STK_SIZE 120 //任务堆栈大小
void start_tast( void * pvParameters ); //任务函数
TaskHandle_t StartTask_Handler;//任务句柄
xTaskCreate((TaskFunction_t ) start_tast, //任务
(char * ) "start_tast", //名字
(uint16_t ) START_STK_SIZE, //任务堆栈
(void * ) NULL, //参数
(UBaseType_t ) START_TASK_PRIO,//任务优先级
(TaskHandle_t * ) &StartTask_Handler);// 任务句柄
vTaskStartScheduler(); //开启任务调度
vTaskDelete(StartTask_Handler); //删除自身也可以食用NULL或者是任务句柄
2.任务的静态创建:
遇到错误:()
重要宏和函数:
configSUPPORT_STATIC_ALLOCATION 1//静态内存
//为空闲任务和定时器任务的任务堆栈和任务控制块分配内存
//空闲任务
static StaticTask_t IdleTaskTCB;//任务控制块
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];//任务堆栈 堆栈大小
//定时任务
static StaticTask_t TimerTaskTCB;//任务控制块
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH];//任务堆栈
//空闲任务所需内存
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
//定时器任务所需内存
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
//静态创建API
//任务创建配置参数
#define START_STK_SIZE 128
#define START_TASK_PRIO 1
StackType_t StartTaskStack[START_STK_SIZE];
StaticTask_t StartTaskTCB;
TaskHandle_t StartTask_Handler;
void start_task( void * pvParameters );
StartTask_Handler=xTaskCreateStatic(( TaskFunction_t) start_task,
(char * ) "start_task",
(uint32_t ) START_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(StackType_t * ) StartTaskStack,
(StaticTask_t * ) &StartTaskTCB );
vTaskStartScheduler(); //开启任务调度
vTaskDelete(StartTask_Handler); //删除自身也可以食用NULL或者是任务句柄
3.任务的挂起和恢复:
遇到错误:()
临界区:(临界区代码一定要精简,临界区会关闭中断,导致优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断得不到及时响应)
任务级临界代码保护:
taskENTER_CRITICAL(); //进入临界区
taskEXIT_CRITICAL(); //退出临界区
任务:
vTaskSuspend("任务句柄"); //挂起任务
vTaskResume("任务句柄"); //恢复任务
4.开关中断:
遇到错误:(任务中如果使用到延时函数要使用delay_xms();因为其他延时中带有中断,关闭中断之后可能一调用延时立马就把中断给打开。所以看不到效果 )
需要设置FreeRTOS的PendSV SysTick中断优先级
临界区:(临界区代码一定要精简,临界区会关闭中断,导致优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断得不到及时响应)
中断级临界代码保护:
x = taskENTER_CRITICAL_FROM_ISR(); //进入
taskEXIT_CRITICAL_FROM_ISR( x ); //退出
中断:
portDISABLE_INTERRUPTS(); //关闭中断
portENABLE_INTERRUPTS(); //打开中断
5.列表和列表项:
遇到错误:(在末尾添加列表项,遍历没搞完全清楚)
重要宏和函数:
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1开启列表完整性检查
//创建结构体列表和列表项
List_t ListText;
ListItem_t ListItem1;
//重要函数
vListInitialise(&ListText);//创建列表ListText
vListInitialiseItem(&ListItem1);//创建列表项ListItem1
vListInsert(&ListText,&ListItem1);//插入列表项ListItem1
uxListRemove(&ListItem1);//删除列表ListItem1
6.时间管理:
相对延时:
vTaskDelay("ms"); //延时ms级
绝对延时(相对于相对延时来说):
定义变量:
const TickType_t TimeIncrement = pdMS_TO_TICKS(1000); //把时间转化为时钟节拍
PreviousWakeTime = xTaskGetTickCount(); //获取当前的时钟节拍
实现:
vTaskDelayUntil(&PreviousWakeTime,TimeIncrement);
7.消息队列:(全局变量尽量不要随意使用)
全局变量的弊端:(可以使用记录当前状态,最忌讳用来传递参数)
0.增加程序之间的耦合性,应用层也要关注驱动层的事情,导致分层不合理与模块化编程相违背
2.阅读比较吃力,可维护性极差,需要找到全局的出处和所有使用的过程,除了原创作者其他人很难维护
3.全局变量任何地方都被修改,使系统变得不可预测,软件安全性得不到保证,导致产品最终走向死亡
4.后期维护非常困难,对于全局满天飞的系统
5.程序初始化就诞生,程序结束才消亡,始终占用数据段上的空间一定程度上造成内存浪费
6.不让维护人员戳你的脊梁骨养成一个良好的编程习惯
全局变量应对之道:
0.全局变量超过3个使用结构体包起来
1.static到需要使用的文件中
2.(返回值)return出去这样属性就是只读,使用起来就不用担心使用时修改值
3.(传参)非要修改值的话可以使用函数传参方式
4.非要非要extern的话要严格控制在自己.h中,不要放到公共的.h中丢人现眼
遇到错误:
创建:
//动态创建
QueueHandle_t Key_Queue;
#define Key_Q_Temp 1
Key_Queue = xQueueCreate(Key_Q_Temp,sizeof(u8)); //队列的长度 队列中每个消息长度 return:队列句柄
//静态创建
QueueHandle_t Key_Queue;
#define Key_Q_Temp 1
u8 buff[Key_Queue+1];
StaticQueue_t QueueBuff;
Key_Queue = xQueueCreateStatic(Key_Q_Temp,sizeof(u8),buff,QueueBuff); //项目数 字节 缓存区 保存队列 return:队列句柄
任务级:
//发送
err = xQueueSend(Key_Queue,&key,10);//同 xQueueSendToBack 完全一样
err = xQueueSendToBack(Key_Queue,&key,10); //发送消息到队头 //队列 值 时钟节拍 return:判断值
err = xQueueSendToFront(Key_Queue,&key,10); //发送消息到队尾
err = xQueueOverwrite(Key_Queue,&key); //带覆盖功能,队列满之后自动覆盖旧消息
//接收
err = xQueueReceive(Key_Queue,&key,portMAX_DELAY); //读取之后删除队列项 //队列 值 接收等待时间 return:判断值
err = xQueuePeek(Key_Queue,&key,portMAX_DELAY); //读取之后不删除 //队列 值 接收等待时间 return:判断值
中断级:
//发送:
err = xQueueSendFromISR(Message_Queue,USART_RX_BUF,&HigherPriorityTaskWoken);/ /同下完全一样
err = xQueueSendToBackFromISR(Message_Queue,USART_RX_BUF,&HigherPriorityTaskWoken);//发送消息到队尾
err = xQueueSendToFrontFromISR(Message_Queue,USART_RX_BUF,&HigherPriorityTaskWoken);//发送消息到队头
err = xQueueOverwriteFromISR(Message_Queue,USART_RX_BUF,&HigherPriorityTaskWoken);//队列满自动覆盖旧值
//接收:
err = xQueueReceiveFromISR(Message_Queue,Message_Buff,&TaskWoken); //读取之后删除队列项
err = xQueuePeekFromISR(Message_Queue,Message_Buff); //读取之后不删除
锁:
上锁:
prvLockQueue();
解锁:
prvUnlockQueue();
8.信号量:(#include "semphr.h")
遇到错误:被按键队列的接收死等给卡死了
优先级翻转:低优先级任务占据信号量时间较长,高优先级任务无法使用此信号量导致高优先级任务阻塞,
使不使用此信号量的比中等优先级任务比高优先级任务早得到CPU的处理
二值信号量创建:(没有优先级继承,适用于同步。优先级翻转问题)
动态创建:
SemaphoreHandle_t BinarySemaphore;
BinarySemaphore = xSemaphoreCreateBinary(); //创建成功之后立马先释放(xSemaphoreGive)一次,不然不会运行,
静态创建:
StaticSemaphore_t SemaphoreBuff;
BinarySemaphore = xSemaphoreCreateBinaryStatic(SemaphoreBuff);
计数型信号量创建:
动态创建:
SemaphoreHandle_t CountingSemaphore;//计数信号量
CountingSemaphore = xSemaphoreCreateCounting("MAX","InitValue");//最大值 初始值
CountingValue = uxSemaphoreGetCount(CountingSemaphore); //获取信号量当前剩余值
静态创建:
SemaphoreHandle_t CountingSemaphore;
StaticSemaphore_t SemaphoreBuffer;
CountingSemaphore = xSemaphoreCreateCountingStatic( "MAX","InitValue",&SemaphoreBuffer );
互斥信号量创建:(有优先级继承,适用于简单的互斥访问。只能用于任务,不能用于中断。优化优先级翻转问题)
动态创建:
SemaphoreHandle_t MutexSemaphore;//计数信号量
MutexSemaphore = xSemaphoreCreateMutex();
静态创建:
SemaphoreHandle_t MutexSemaphore;//计数信号量
StaticSemaphore_t MutexBuffer;
MutexSemaphore = xSemaphoreCreateMutexStatic(&MutexBuffer);
递归互斥信号量创建:(与之前三种信号量的释放和获取不同。只能用于任务,不能用于中断。)
#define configUSE_RECURSIVE_MUTEXES 1;
动态创建:
SemaphoreHandle_t RecursiveMutexSemaphore;
RecursiveMutexSemaphore = xSemaphoreCreateRecursiveMutex();
静态创建:
SemaphoreHandle_t RecursiveMutexSemaphore;
StaticSemaphore_t RecursiveMutexBuffer;
RecursiveMutexSemaphore = xSemaphoreCreateRecursiveMutexStatic(&RecursiveMutexBuffer);
任务级私用信号量释放和获取:(only)
BaseType_t err;
信号量释放:
err = xSemaphoreGiveRecursive(RecursiveMutexSemaphore);
信号量获取:
err = xSemaphoreTakeRecursive(RecursiveMutexSemaphore,"portTICK_PERIOD_MS");
信号量释放和获取:
任务级:
信号量释放:
xSemaphoreGive(BinarySemaphore);
信号量获取:
xSemaphoreTake(BinarySemaphore,portMAX_DELAY); //获取信号量句柄 阻塞时间
中断级:
BaseType_t HigherPriorityTaskWoken;
BaseType_t err;
信号量释放:
err = xSemaphoreGiveFromISR(BinarySemaphore,&HigherPriorityTaskWoken);//信号量句柄 标记是否进行任务切换 retuen:Y/N
(当HigherPriorityTaskWoken为psTRUE退出中断服务函数之前一定要进行一次任务切换)
portYIELD_FROM_ISR(HigherPriorityTaskWoken); //任务切换
信号量获取:
err = xSemaphoreTakeFromISR(BinarySemaphore,&HigherPriorityTaskWoken);
在保存变量(HigherPriorityTaskWoken)为pdTRUE时退出中断服务函数之前一定要进行一次任务切换
9.软件定时器:(#include "timers.h")
遇到错误:
创建:
TimerHandle_t TimerHandle;
void CallbackFunction( TimerHandle_t xTimer );//回调函数
StaticTimer_t TimerBuffer;
动态创建:
单次/周期定时器:
TimerHandle = xTimerCreate((const char * ) "CallbackFunction",
(TickType_t ) 1000,
(UBaseType_t ) pdFALSE,//pdFALSE单次 pdTRUE周期
(void * ) 1,
(TimerCallbackFunction_t ) CallbackFunction );
静态创建:
单次/周期定时器:
PeriodTimer = xTimerCreateStatic(const char * ) "CallbackFunction",
(TickType_t ) 1000,
(UBaseType_t ) pdFALSE,//pdFALSE单次 pdTRUE周期
(void * ) 2,
(TimerCallbackFunction_t) CallbackFunction,
(StaticTimer_t * ) TimerBuffer ); //保护成员变量
复位软件定时器:.
BaseType_t err;
BaseType_t HigherPriorityTaskWoken;
任务级:
err = xTimerReset(TimerHandle,0);//句柄 阻塞时间
中断级:
err = xTimerResetFromISR(TimerHandle,&HigherPriorityTaskWoken);//句柄 时钟节拍
开启软件定时器:
BaseType_t err;
BaseType_t HigherPriorityTaskWoken;
任务级:
err = xTimerStart(TimerHandle,0);//句柄 阻塞时间
中断级:
err = xTimerStartFromISR(TimerHandle,&HigherPriorityTask);//句柄 时钟节拍
关闭软件定时器:
任务级:
err = xTimerStop(TimerHandle,0);//句柄 阻塞时间
中断级:
err = xTimerStopFromISR(TimerHandle,&HigherPriorityTask);//句柄 时钟节拍
10.事件标志组:(#include "event_groups.h")
遇到错误:
悉知:事件标志组的高八位不能用,16(16-8)位只能表示8个事件,32位能表示24(32-8)个事件
创建:
EventGroupHandle_t EventGroupHandle;//创建函数返回的句柄
EventGroupHandle_t EventGroupBuffer; //静态创建保存数据的数组
动态创建:
EventGroupHandle = xEventGroupCreate();
静态创建:
EventGroupHandle = xEventGroupCreateStatic(&EventGroupBuffer); //保存数据所需数组
设置事件位:
#define BIT_0 (1 << 0)
#define BIT_1 (1 << 1)
#define BIT_ALL (BIT_0|BIT_1)
置0:
任务级:
EventBits_t EventGroupValue;
EventGroupValue = xEventGroupClearBits(EventGroupHandle,BIT_0);
中断级:
BaseType_t EventGroupValue;
EventGroupValue = xEventGroupClearBitsFromISR(EventGroupHandle,BIT_0);
置1:
任务级:
EventBits_t EventGroupValue;
EventGroupValue = xEventGroupSetBits(EventGroupHandle,BIT_0);
中断级:
BaseType_t EventGroupValue;
BaseType_t HigherPriorityTaskWoken;
EventGroupValue = xEventGroupSetBitsFromISR(EventGroupHandle,BIT_0,&HigherPriorityTaskWoken);
在保存变量(HigherPriorityTaskWoken)为pdTRUE时退出中断服务函数之前一定要进行一次任务切换
获取事件标志组值:
EventBits_t err;
任务级:
err = xEventGroupGetBits(EventGroupHandle);
中断级:
err = xEventGroupGetBitsFromISR(EventGroupHandle);
等待指定的事件位:
EventBits_t EventValue;
EventValue = xEventGroupWaitBits((EventGroupHandle_t ) EventGroupHandle,
(EventBits_t ) BIT_ALL,//操作的标志位
(BaseType_t ) pdFALSE,//pdTRUE标志位被清除 pdFALSE不会被清除
(BaseType_t ) pdFALSE, //pdTRUE 满足所有位 pdFALSE 满足其中一位
(TickType_t ) portMAX_DELAY);//阻塞时间
11.任务通知:(#include "tasks.h")
遇到错误:
悉知:需要打开宏才能使用 #define configUSE_TASK_NOTIFICATIONS 1
//枚举
typedef enum
{
eNoAction = 0, /* 通知任务而不更新其通知值. */
eSetBits, /* 更新指定bit. */
eIncrement, /* 通知值加1. */
eSetValueWithOverwrite, /* 覆写的方式更新通知值. */
eSetValueWithoutOverwrite /* 不覆写通知值. */
} eNotifyAction;
发送任务通知:
任务级:
xTaskNotifyGive(Task2Task_Handler); //任务句柄
err = xTaskNotify(Task2Task_Handler,Value,"枚举");//任务句柄 任务通知值 任务通知更新的办法
err = xTaskNotifyAndQuery(Task2Task_Handler,Value,"枚举",&PreviousNotifyValue);
//任务句柄 任务通知值 任务通知更新办法 保存更新前的任务通知值
中断级:
vTaskNotifyGiveFromISR(Task2Task_Handler,&HigherPriorityTaskWoken);//任务句柄 保存变量
err = xTaskNotifyFromISR(Task2Task_Handler,value,"枚举",&HigherPriorityTaskWoken);
//任务句柄 任务通知值 任务通知更新的办法 保存变量
err = xTaskNotifyAndQueryFromISR(Task2Task_Handler,value,"枚举",&PreviousNotifyValue,&HigherPriorityTaskWoken);
//任务句柄 任务通知值 任务通知更新办法 保存更新前的任务通知值 保存变量
在保存变量(HigherPriorityTaskWoken)为pdTRUE时退出中断服务函数之前一定要进行一次任务切换
获取任务通知:
vlaue = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//推荐模拟二值信号量 计数型信号量使用
//pdFALSE 任务通知值加1(类似于计数型信号量) pdTRUE任务通知值清0(类似于二值信号量)return:任务通知值减少清零的值
err = xTaskNotifyWait(0,0xffffffff,&NotificationValue,portMAX_DELAY);//可以获取任何模拟的任务通知 消息邮箱 事件标志组等
//不理解,不理解,保存任务通知值 ,阻塞时间 return:pdTRUE获取任务通知成功 pdFALSE获取失败
12.低功耗Tickless:
打开宏:#define configUSE_TICKLESS_IDLE 1 //1启用低功耗tickless模式
自定义两个函数对接官方给的宏:
//低功耗模式Tickless
#define configPRE_SLEEP_PROCESSING PreSleepProcessing
#define configPOST_SLEEP_PROCESSING PostSleepProcessing
函数继承声明:
extern void PreSleepProcessing(uint32_t ulExpectedIdleTime);
extern void PostSleepProcessing(uint32_t ulExpectedIdleTime);
主函数进行函数实现:
//进入低功耗模式前需要处理的事情
//ulExpectedIdleTime:低功耗模式运行时间
void PreSleepProcessing(uint32_t ulExpectedIdleTime)
{
//关闭某些低功耗模式下不使用的外设时钟,
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, DISABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, DISABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, DISABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, DISABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, DISABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, DISABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH, DISABLE);
}
//退出低功耗模式以后需要处理的事情
//ulExpectedIdleTime:低功耗模式运行时间
void PostSleepProcessing(uint32_t ulExpectedIdleTime)
{
//退出低功耗模式以后打开那些被关闭的外设时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH, ENABLE);
}
13.空闲任务钩子函数:
打开宏#define configUSE_IDLE_HOOK 1 //1,使用空闲钩子;0,不使用
实现钩子函数 void vApplicationIdleHook(void)
void vApplicationIdleHook(void)
{
__disable_irq();
__dsb( portSY_FULL_READ_WRITE );
__isb( portSY_FULL_READ_WRITE );
BeforeEnterSleep(void); //进入睡眠之前关闭不使用的外设时钟,函数自己实现(模拟低功耗函数实现)
__wfi(); //进入睡眠模式
AfterExitSleep(void); //退出低功耗模式之后打开关闭的外设时钟,函数自己实现(如上)
__dsb( portSY_FULL_READ_WRITE );
__isb( portSY_FULL_READ_WRITE );
__enable_irq();
}
14.内存管理:
u8 *buffer;
size_t freemem;
内存申请:
buffer=pvPortMalloc(30);
内存释放:
vPortFree(buffer);
buffer=NULL;//释放完内存之后必须要要对内存进行清空,不然会造成假清空
获取剩余内存大小:
freemem = xPortGetFreeHeapSize();
FreeRTOS学习笔记
最新推荐文章于 2022-06-10 13:13:13 发布