#学习记录
设置sys,因为FreeRTOS操作系统也需要定时器,所以不选择systick,选用tim1
与之前相反的是优先级,序号越高优先级越高,即正序
挂起态 理解:被高优先级抢占了导致暂停的状态
阻塞态 :如osDelay(500)的延时状态
TCB任务控制块 一个结构体(约等于任务句柄?)
任务创建:
动态创建
注意事项是动态内存分配
静态创建
configSUPPORT_STATIC_ALLOCATION 静态内存分配
多任务调度:
在用户程序需要调用特权级的操作系统函数时,SVC异常被触发
系统延时API详解
临界段:
在程序访问资源时,不希望被其他(相同优先级)任务或者中断打断,这段要执行的代码,称为临界代码段
taskDISABLE_INTERRUPTS()
栈堆大小的确定:
State状态包括S、被删除D(详见手册或下面pdf截图)、就绪态R、阻塞态B
configUSE_TRACE_FACILITY 用户追踪功能
configUSE_STATS_FORMATTING_FUNCTIONS 用户实现功能
栈溢出检测方案一、二(两者大同小异,甚至能用程序更改宏定义来更改检测方案选项)
栈溢出后的回调函数
函数参数xTaskHandle xTask
和signed char *pcTaskName
分别表示发生栈溢出的任务的句柄和名称。
利用回调函数打印溢出的任务名称
方案一二两者大同小异,甚至能用程序更改宏定义来更改检测方案选项
获取任务状态的方案
两个选项使能
CPU利用率统计
configureTimerForRunTimeStats、getRunTimeCounterValue 统计运行时间、获取运行时间。
可用来初始化高精度定时器。
如: 开启定时器6的中断、初始化一个自定义统计值
消息队列
API:
xQueueSend()在任务中使用
xQueueSendFromISR()在中断中使用
代码实现:
添加队列
![](https://i-blog.csdnimg.cn/blog_migrate/fe1fa1f35462b35316447d3f6ef2f472.png)
![](https://i-blog.csdnimg.cn/blog_migrate/55c26bc7ea2d6b88635e717dc281d1de.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bf52fd8cbdb51373a8e198d7a4ec5078.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a7ec47b7a2f19d4045c87874827afcf9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ec0f7ba9b34501b38a1a63276848fae8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c388397265b13417ebd6cf32f3539895.png)
portMAX_DELAY
是一个宏定义,用于表示无限延时的值
FreeRTOS信号量
Binary二值信号量 (0-1)
Count计数信号量 (0-n)
Mutex互斥信号量(包括RecursiveMutex递归互斥信号量)
task1进入阻塞态,获取信号量后会继续运行,task1运行完后释放资源后task2才能获取资源从阻塞态出来
二值信号量函数应用:
设置、自动初始化
自动生成信号量句柄
自动初始化信号量句柄
达到想要的条件后给handle发送信号量
无限等待直到接收到信号量(return pdPASS)后执行任务内容
计数信号量函数应用:
xSemaphoreCreateCounting()
uxSemaphoreGetCount()
改为KEY1获取 KEY2释放
前置初始化之类的与二值类似
优先级翻转问题
互斥信号量函数应用:
经典死锁现象:
xSemaphoreCreateMutex()
xSemaphoreGetMutexHolder()
设置三个不同的优先级LED normal DELAY Low HIGH high
递归互斥信号量函数应用:
xSemaphoreCreateRecursiveMutex()
xSemaphoreTakeRecursive()
xSemaphoreGiveRecursive()
事件标志组函数应用
事件标志组是一组事件标志位的集合,通常可以看作是一个整数(32bit)。每个位(bit)代表一个特定的事件
Cubemx里没有事件标志组的创建设置,我们要在keil里另外自己人为设置
xEventGroupCreate()
xEventGroupSetBits()
xEventGroupSetBitsFromISR()
守护任务:操作系统不想在中断中执行的任务(软件定时器任务)
红框内选项要使能(置1)可在CubeMx实现
xEventGroupGetBits()
xEventGroupGetBitsFromISR()
xEventGroupWaitBits()
xEventGroupSync()
软件定时器函数应用
xTimerCreate()
xTimerStart()
xTimerReset()
pvTimerGetTimerID()
xTimerChangePeriod()
使能低速时钟
用LEDtask和USARTtask实现需要的功能
因为需要定时周期打印实时时间的功能,所以添加Timer:
程序部分较为复杂,可自行查看L7 TimerTask
低功耗管理(Tickless低功耗管理)
复位后串口卡住,是时钟问题,需要将TIM6先关闭
![](https://i-blog.csdnimg.cn/blog_migrate/cd16fda691f1c7503bf4dd98a1cdaad7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/810431c4ee63d77483971095404565a9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d82c56982e4d125f065db1eae7f01f5c.png)
内存管理
heap_1 功能:只能分配,不能释放 (可以用于一些不能删除的数值)
heap_2 功能:支持分配,也支持释放,但是没有内存管理,容易产生内存碎片。
heap_3 功能:封装malloc/free,且任务保护
heap_4 功能:有分配、释放、管理功能
heap_5 功能:4的基础上带有外部扩展,防止空间不够
heap2、heap4的区别:
heap2释放的内存下次如要分配,就必须与之前释放的空间大小一致,否则无法使用
注意:申请时需要空间已经释放,释放时需要空间不为空(复制CubeMx工程为EventTask)
void *pvPortMalloc( size_t xWantedSize );申请
void vPortFree( void *pv );释放
size_t xPortGetFreeHeapSize( void )获取空闲堆空间大小(byte)
列表操作
pxReady TasksLists [] 优先级
xDelayed TaskList1/xDelayed TaskList2 用于systick累加溢出处理
xPending ReadyList 挂起任务就绪管理
xTasks Waiting Termination 在空闲任务中读取后删除该列表里的任务(等待删除列表)
xSuspended TaskList 单独挂起任务列表
Timer设定两个列表也是为了应对溢出的情况
List_t列表,ListItem_t 列表项,MiniList Item_t 相当于一个链式结构的head,但是在列表里ta是相当于end(尾部),即这里的列表不用头部定位,用的是尾部定位
vListInitialise() 注意,使用列表就必须以此初始化
vListInitialiseItem() 同上
vListInsert() 每个ListItem自带一个value值,根据这个值产生序号,以此来排列插入,我们可以初始化列表项时就给value赋值,如:ListItemTest[index].xItemValue = index;
vListInsertEnd()
uxListRemove()
/* USER CODE BEGIN Header_DELAY_Task */
#define ITEM_NUMBER 5
List_t ListTest;
ListItem_t ListItemTest[ITEM_NUMBER];
/**
* @brief Function implementing the DELAYTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_DELAY_Task */
void DELAY_Task(void const * argument)
{
/* USER CODE BEGIN DELAY_Task */
EventBits_t KeyEventBits;
uint8_t index;
static uint8_t record = 0;
ListItem_t* pListItem;
/*
1、列表及列表项初始化
*/
vListInitialise(&ListTest);
for(index=0;index<ITEM_NUMBER;index++){
vListInitialiseItem(&ListItemTest[index]);
ListItemTest[index].xItemValue = index;
}
/* Infinite loop */
for(;;)
{
KeyEventBits = xEventGroupWaitBits(KeyEventGroup,
KEY1_EVENT_BIT|KEY2_EVENT_BIT|KEY3_EVENT_BIT|KEY4_EVENT_BIT,
pdTRUE,
pdFALSE,
portMAX_DELAY);
printf("key is down key event bit is %x\r\n",KeyEventBits);
switch(KeyEventBits){
case KEY1_EVENT_BIT:
/*
当列表项在范围内,允许插入
插入完毕后,记录值record加一
*/
if(record < ITEM_NUMBER){
vListInsert(&ListTest,&ListItemTest[record++]);
}
else{
printf("plese press K2 remove!\r\n");
}
break;
case KEY2_EVENT_BIT:
/*
当记录值有效时,允许移除
插入完毕后,记录值record减一
*/
if((record != 0)&&(record <= ITEM_NUMBER)){
uxListRemove(&ListItemTest[--record]);
}
else{
printf("plese press K1 insert!\r\n");
}
break;
case KEY3_EVENT_BIT:
/*
1、打印有效列表项地址
2、打印有效列表项(ItemValue)
3、打印有效列表项前节点地址
4、打印有效列表项后节点地址
*/
for(pListItem = ListTest.xListEnd.pxNext;pListItem != (ListItem_t*)&ListTest.xListEnd;pListItem=pListItem->pxNext){
printf("pListItem addr = %x\r\n",pListItem);
printf("pListItem item value = %d\r\n",pListItem->xItemValue);
printf("pListItem previous addr = %x\r\n",pListItem->pxPrevious);
printf("pListItem next addr = %x\r\n",pListItem->pxNext);
}
}
osDelay(10);
}
/* USER CODE END DELAY_Task */
}