FreertosHAL库笔记

堆和栈


堆:一块内存空间,用于存储程序运行时动态申请的内存空间。在堆上分配内存可以根据程序的需要灵活地申请和释放不同大小的内存块。可用pvProMalloc()和vPortFree()函数来开辟和释放

栈:也是一块内存空间,主要用于函数调用、局部变量、多任务系统里保存现场

创建和删除任务

创建任务

1.动态创建

BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode, // 函数指针, 任务函数
const char * const pcName, // 任务的名字
const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为word,10表示40字节
void * const pvParameters, // 调用任务函数时传入的参数
UBaseType_t uxPriority, // 优先级
TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任务

2.静态创建

TaskHandle_t xTaskCreateStatic (
TaskFunction_t pxTaskCode, // 函数指针, 任务函数
const char * const pcName, // 任务的名字
const uint32_t ulStackDepth, // 栈大小,单位为word,10表示40字节
void * const pvParameters, // 调用任务函数时传入的参数
UBaseType_t uxPriority, // 优先级
StackType_t * const puxStackBuffer, // 静态分配的栈,就是一个buffer
StaticTask_t * const pxTaskBuffer // 静态分配的任务结构体的指针,用它来操作这个任务
);

可以使用多个函数创建多个任务也可以用一个函数创建多个任务,只不创建时要输入不同参数到该函数

删除任务

void vTaskDelete( TaskHandle_t xTaskToDelete );

Tick

对于同优先级的任务,它们“轮流”执行

FreeRTOS中,使用定时器产生固定间隔的中断。这叫Tick、滴答,比如每10ms

发生一次时钟中断

假设 t1、t2、t3 发生时钟中断 (两次中断之间的时间被称为时间片(time slice、tick period))

时间片的长度由configTICK_RATE_HZ 决定,假设configTICK_RATE_HZ为100, 那么时间片长度就是 10ms

相同优先级的任务的切换:
1.任务 2 从 t1 执行到 t2
2.在 t2 发生 tick 中断,进入 tick 中断处理函数:
选择下一个要运行的任务
3.执行完中断处理函数后,切换到新的任务:任务 1
任务 1 从 t2 执行到 t3.........
vTaskDelay(2); // 等待2个Tick,假设configTICK_RATE_HZ=100, Tick周期时10ms, 等待20ms
// 还可以使用pdMS_TO_TICKS宏把ms转换为tick

vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms 注意,基于Tick实现的延时并不精确,比如vT

注:当任务使用vTaskDelay();则在等待的时间里该任务处于阻塞状态,会被放到管理阻塞任务的链表里这时任务调度器在管理就绪态的链表里选择任务时就不会选到该任务,当等待的时间到了时该任务又会被放进就绪态任务链表,当每个函数都阻塞态时空闲任务就会出来处理那些“自杀”的任务,释放他们的栈

两个delay函数

vTaskDelay:至少等待指定个数的 Tick Interrupt 才能变为就绪状态
vTaskDelayUntil:等待到指定的绝对时刻,才能变为就绪态
void vTaskDelay( const TickType_t xTicksToDelay ); /* xTicksToDelay: 等待多少给Tick */
/* pxPreviousWakeTime: 上一次被唤醒的时间
* xTimeIncrement: 要阻塞到(pxPreviousWakeTime + xTimeIncrement)
* 单位都是Tick Count
*/
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement );

同步和互斥通信

同步:
指多个线程或进程在执行过程中,按照一定的顺序和规则进行协调,以确保它们之间的操作能够正确地相互配合。例如,一个线程需要等待另一个线程完成某个任务后才能继续执行,这就需要通过同步机制来实现。

互斥:

是指在同一时刻,只允许一个线程或进程访问某个共享资源,以防止多个线程或进程同时访问导致数据不一致或其他错误。例如,多个线程同时对一个全局变量进行写操作时,就需要使用互斥机制来保证每次只有一个线程能够成功写入

队列和队列集

队列

功能

• 任务间通信:允许不同任务之间传递数据。例如,一个任务可以将数据发送到队列,另一个任务从队列中获取数据,实现任务之间的信息交互。

• 任务同步:可以用于任务之间的同步。当一个任务向队列发送数据时,其他等待数据的任务可以被唤醒,从而实现任务之间的协调执行。

特点

• 灵活的数据类型:可以存储多种数据类型,包括基本数据类型、结构体、指针等。用户可以根据实际需求定义队列中数据的类型。

• 阻塞机制:具有阻塞特性。当任务尝试从空队列中读取数据时,任务会被阻塞,直到队列中有数据可用;当任务尝试向已满队列中写入数据时,任务也会被阻塞,直到队列有空间可用。

队列函数

1.动态创建

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

2.静态创建

QueueHandle_t xQueueCreateStatic(
UBaseType_t uxQueueLength, 
UBaseType_t uxItemSize, 
uint8_t *pucQueueStorageBuffer, 
StaticQueue_t *pxQueueBuffer
);
参数说明
uxQueueLength
队列长度,最多能存放多少个数据(item)
uxItemSize
每个数据(item)的大小:以字节为单位
pucQueueStorageBuffer
如果 uxItemSize 非 0,pucQueueStorageBuffer 必须指向一个 uint8_t 数组,此数组大小至少为"uxQueueLength * uxItemSize"
pxQueueBuffer
必须执行一个 StaticQueue_t 结构体,用来保存队列的数据结构
返回值
非 0:成功,返回句柄,以后使用句柄来操作队列
NULL:失败,因为 pxQueueBuffer 为 NULL
3.复位:把队列恢复为初始状态
/* pxQueue : 复位哪个队列;
* 返回值: pdPASS(必定成功)
*/
BaseType_t xQueueReset( QueueHandle_t pxQueue);

4.删除队列

void vQueueDelete( QueueHandle_t xQueue );

参数为队列句柄

5.写队列

/* 等同于xQueueSendToBack
* 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSend(
QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait
);

/*
* 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSendToBack(
QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait
);

/*
* 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToBackFromISR(
QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken
);

/*
* 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSendToFront(
QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait
);

/*
* 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToFrontFromISR(
QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken
);
参数说明
xQueue
队列句柄,要写哪个队列
pvItemToQueue
数据指针,这个数据的值会被复制进队列,
复制多大的数据?在创建队列时已经指定了数据大小
xTicksToWait
如果队列满则无法写入新数据,可以让任务进入阻塞状态,
xTicksToWait 表示阻塞的最大时间(Tick Count)。
如果被设为 0,无法写入数据时函数会立刻返回;
如果被设为 portMAX_DELAY,则会一直阻塞直到有空间可写
返回值
pdPASS:数据成功写入了队列
errQUEUE_FULL:写入失败,因为队列满了

6.读队列

//在任务中读
BaseType_t xQueueReceive( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait );
//在中断中读
BaseType_t xQueueReceiveFromISR(
QueueHandle_t xQueue,
void *pvBuffer,
BaseType_t *pxTaskWoken
);
参数说明
xQueue
队列句柄,要读哪个队列
pvBuffer
bufer 指针,队列的数据会被复制到这个 buffer
复制多大的数据?在创建队列时已经指定了数据大小
xTicksToWait
如果队列空则无法读出数据,可以让任务进入阻塞状态,
xTicksToWait 表示阻塞的最大时间(Tick Count)
如果被设为 0,无法读出数据时函数会立刻返回;
如果被设为 portMAX_DELAY,则会一直阻塞直到有数据可写
返回值
pdPASS:从队列读出数据入
errQUEUE_EMPTY:读取失败,因为队列空了。

7.查询

查询队列有多少数据、多少空余空间

/*
* 返回队列中可用数据的个数
*/
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
/*
* 返回队列中可用空间的个数
*/
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

8.覆盖/偷看

当队列长度为 1 时,可以使用 xQueueOverwrite()xQueueOverwriteFromISR()

来覆盖数据。

注意:队列长度必须为 1。当队列满时,这些函数会覆盖里面的数据,这也以为着这些

函数不会被阻塞

/* 覆盖队列
* xQueue: 写哪个队列
* pvItemToQueue: 数据地址
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueueOverwrite(
QueueHandle_t xQueue,
const void * pvItemToQueue
);
BaseType_t xQueueOverwriteFromISR(
QueueHandle_t xQueue,
const void * pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken
);

如果想让队列中的数据供多方读取,也就是说读取时不要移除数据,要留给后来人。那

么可以使用"窥视",也就是xQueuePeek()xQueuePeekFromISR()。这些函数会从队列中

复制出数据,但是不移除数据。这也意味着,如果队列中没有数据,那么"偷看"时会导致阻

塞;一旦队列中有数据,以后每次"偷看"都会成功。

/* 偷看队列
* xQueue: 偷看哪个队列
* pvItemToQueue: 数据地址, 用来保存复制出来的数据
* xTicksToWait: 没有数据的话阻塞一会
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueuePeek(
QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait
);
BaseType_t xQueuePeekFromISR(
QueueHandle_t xQueue,
void *pvBuffer,
);

队列集

队列集(Queue Set)是一种用于管理多个队列的机制,本质也是一个队列,只不过里面存放的是各队列的句柄,它允许任务同时等待多个队列中的数据,多队列管理:可以将多个队列组合在一起,通过一个队列集句柄进行统一管理。任务可以在一个操作中等待多个队列中的任意一个有数据到来,而无需分别对每个队列进行轮询或阻塞等待。

1.创建队列集

QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength )

2.把队列加入队列集

BaseType_t xQueueAddToSet( 
QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet );

3.读取队列集

QueueSetMemberHandle_t xQueueSelectFromSet( 
QueueSetHandle_t xQueueSet,
TickType_t const xTicksToWait );

信号量

信号量是一种用于任务间同步和互斥的机制,它基于计数原理来控制对共享资源的访问

原理:维护一个计数值,当任务获取信号量时,计数值减1;当任务释放信号量时,计数值加1。计数值表示可用资源的数量

和队列的区别

两种信号量

二进制信号量:是一种特殊的计数信号量,计数值只能是0或1。它通常用于实现互斥访问,相当于一个简单的锁

动态创建:

xSemaphoreCreateBinary(void)

参数:无

返回值:成功创建则返回信号量句柄,否则返回NULL

静态创建:

xSemaphoreCreateBinaryStatic(StaticSemaphore_t *pxSemaphoreBuffer)

 参数:pxSemaphoreBuffer是一个指向StaticSemaphore_t类型结构体的指针,用于存储信号量的相关信息,由用户自行分配内存。

返回值:成功创建则返回信号量句柄,否则返回NULL。

计数型信号量:被创建时初始值最大计数值和初始计数值可以设定

动态创建:

xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount)

参数:uxMaxCount表示信号量的最大计数值,uxInitialCount表示信号量的初始计数值,且uxInitialCount不能大于uxMaxCount

返回值:成功创建则返回信号量句柄,否则返回NULL

静态创建:

xSemaphoreCreateCountingStatic(
UBaseType_t uxMaxCount, 
UBaseType_t uxInitialCount, 
StaticSemaphore_t *pxSemaphoreBuffer)

参数:uxMaxCount和uxInitialCount含义与动态创建函数中的相同。pxSemaphoreBuffer是一个指向StaticSemaphore_t类型结构体的指针,用于存储信号量的相关信息,由用户自行分配内存

返回值:成功创建则返回信号量句柄,否则返回NULL

删除信号量:

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

参数:信号量句柄

give(释放)/take(获取)信号量:

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );

BaseType_t xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore, 
BaseType_t *pxHigherPriorityTaskWoken
);
参数说明
xSemaphore
信号量句柄,释放哪个信号量
pxHigherPriorityTaskWoken
如果释放信号量导致更高优先级的任务变为了绪态,则*pxHigherPriorityTaskWoken = pdTRUE
返回值
pdTRUE 表示成功,
如果二进制信号量的计数值已经是 1,再次调用此
函数则返回失败;
如果计数型信号量的计数值已经是最大值,再次调
用此函数则返回失败
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore, 
TickType_t xTicksToWait
);

BaseType_t xSemaphoreTakeFromISR(
SemaphoreHandle_t xSemaphore, 
BaseType_t *pxHigherPriorityTaskWoken
);

互斥量

要想使用互斥量,需要在配置文件 FreeRTOSConfig.h 中定义:
##define configUSE_MUTEXES 1
/* 创建一个互斥量,返回它的句柄。
* 此函数内部会分配互斥量结构体
* 返回值: 返回句柄,非 NULL 表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutex( void );

/* 创建一个互斥量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个 StaticSemaphore_t 结构体,并传入它的
指针
* 返回值: 返回句柄,非 NULL 表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );

事件组

定义:事件组是一组相关事件的集合,它可以同时管理多个事件。每个事件在事件组中都有一个对应的位,通过对事件组的操作,可以同时设置、清除或查询多个事件的状态

创建和删除事件组

/* 创建一个事件组,返回它的句柄
* 此函数内部会分配事件组结构体
* 返回值: 返回句柄,非 NULL 表示成功
*/
EventGroupHandle_t xEventGroupCreate( void );

/* 创建一个事件组,返回它的句柄
* 此函数无需动态分配内存,所以需要先有一个 StaticEventGroup_t 结构体,并传入它的
指针
* 返回值: 返回句柄,非 NULL 表示成功
*/
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer
);

/*
* xEventGroup: 事件组句柄,你要删除哪个事件组
*/
void vEventGroupDelete( EventGroupHandle_t xEventGroup )

设置事件组


/* 设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToSet: 设置哪些位
* 如果 uxBitsToSet 的 bitX, bitY 为 1, 那么事件组中的 bitX, bitY 被设置为 1
* 可以用来设置多个位,比如 0x15 就表示设置 bit4, bit2, bit0
* 返回值: 返回原来的事件值(没什么意义, 因为很可能已经被其他任务修改了) */
EventBits_t xEventGroupSetBits( 
EventGroupHandle_t xEventGroup, 
const EventBits_t uxBitsToSet );

/* 在中断中设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToSet: 设置哪些位?
* 如果 uxBitsToSet 的 bitX, bitY 为 1, 那么事件组中的 bitX, bitY 被设置为 1
* 可以用来设置多个位,比如 0x15 就表示设置 bit4, bit2, bit0
* pxHigherPriorityTaskWoken: 有没有导致更高优先级的任务进入就绪态? pdTRUE-有, p
dFALSE-没有
* 返回值: pdPASS-成功, pdFALSE-失败
*/
BaseType_t xEventGroupSetBitsFromISR( 
EventGroupHandle_t xEventGroup, 
const EventBits_t uxBitsToSet, 
BaseType_t * pxHigherPriorityTaskWoken );

等待事件

EventBits_t xEventGroupWaitBits( 
EventGroupHandle_t xEventGroup, 
const EventBits_t uxBitsToWaitFor, 
const BaseType_t xClearOnExit, 
const BaseType_t xWaitForAllBits, 
TickType_t xTicksToWait 
);
参数说明
xEventGroup
等待哪个事件组
uxBitsToWaitFor
等待哪些位?哪些位要被测试
xWaitForAllBits
"AND"还是"OR"
pdTRUE: 等待的位,全部为 1;
pdFALSE: 等待的位,某一个为 1 即可
xClearOnExit
函数退出前是否要清除事件
pdTRUE: 清除 uxBitsToWaitFor 指定的位
pdFALSE: 不清除
xTicksToWait
如果期待的事件未发生,阻塞多久
可以设置为 0:判断后即刻返回;
可设置为 portMAX_DELAY:一定等到成功才返回;
可以设置为期望的 Tick Count,一般用 pdMS_TO_TICKS() 把 ms 转换为 Tick Count
返回值
返回的是事件值,
如果期待的事件发生了,返回的是"非阻塞条件成立"时的事件 值;
如果是超时退出,返回的是超时时刻的事件值

任务通知

每个任务都有一个32位的通知值,类似于一个信号量或标志位。任务可以通过发送通知来设置这个值,以告知其他任务发生了某个事件,接收任务可以等待通知的到来,并根据通知的值进行相应的处理

任务通知有两套函数:

简化版:


//发出通知
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );

void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTa
skWoken );
//取出通知
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
xTaskNotifyGive 函数的参数说明如下:
vTaskNotifyGiveFromISR 函数的参数说明如下:

ulTaskNotifyTake 函数的参数说明如下:

专业版:

//发出通知
BaseType_t xTaskNotify( 
TaskHandle_t xTaskToNotify, 
uint32_t ulValue, 
eNotifyAction eAction );

BaseType_t xTaskNotifyFromISR( 
TaskHandle_t xTaskToNotify, 
uint32_t ulValue, 
eNotifyAction eAction, 
BaseType_t *pxHigherPriorityTaskWoken );

//取出通知
BaseType_t xTaskNotifyWait( 
uint32_t ulBitsToClearOnEntry, 
uint32_t ulBitsToClearOnExit, 
uint32_t *pulNotificationValue, 
TickType_t xTicksToWait );
xTaskNotify 函数的参数说明如下:

参数说明
xTaskToNotify
任务句柄(创建任务时得到),给哪个任务发通知
ulValue
怎么使用 ulValue,由 eAction 参数决定
eAction
见下表
返回值
pdPASS:成功,大部分调用都会成功
pdFAIL:只有一种情况会失败,当 eAction 为
eSetValueWithoutOverwrite, 并且通知状态为"pending"(表示有新数据未读),这时就会失败。

xTaskNotifyFromISR最后一个参数说明如下:

被通知的任务,可能正处于阻塞状态 , xTaskNotifyFromISR 函数发出通知后,会把接收任务从阻塞状态切换为就绪态
如果被唤醒的任务的优先级,高于当前任务的优先级,则 "*pxHigherPriorityTaskWoken"被设置为 pdTRUE ,这表示在中断返回之前要进行任务切换

xTaskNotifyWait函数列表如下:

参数说明
ulBitsToClearOnEntry
在 xTaskNotifyWait 入口处,要清除通知值的哪些位
通知状态不是"pending"的情况下,才会清除
它的本意是:我想等待某些事件发生,所以先把"旧数据" 的某些位清零
能清零的话:通知值 = 通知值 &
~(ulBitsToClearOnEntry)。
比如传入 0x01,表示清除通知值的 bit0;
传入 0xffffffff 即 ULONG_MAX,表示清除所有位,即把值 设置为 0
ulBitsToClearOnExit
在 xTaskNotifyWait 出口处,如果不是因为超时推出,而是因为得到了数据而退出时
通知值 = 通知值 & ~(ulBitsToClearOnExit)。
在清除某些位之前,通知值先被赋给
"*pulNotificationValue"
比如入 0x03,表示清除通知值的 bit0、bit1;
传入 0xffffffff 即 ULONG_MAX,表示清除所有位,即把值 设置为 0
pulNotificationValue
用来取出通知值。
在函数退出时,使用 ulBitsToClearOnExit 清除之前,把通 知值赋给"*pulNotificationValue"。
如果不需要取出通知值,可以设为 NULL。
xTicksToWait
任务进入阻塞态的超时时间,它在等待通知状态变为
"pending"。
0:不等待,即刻返回;
portMAX_DELAY:一直等待,直到通知状态变为
"pending";
其他值:Tick Count,可以用 pdMS_TO_TICKS() 把 ms 转 换为 Tick Count
返回值
1. pdPASS:成功
这表示 xTaskNotifyWait 成功获得了通知:
可能是调用函数之前,通知状态就是"pending";
也可能是在阻塞期间,通知状态变为了"pending"。
2. pdFAIL:没有得到通知

软件定时器

软件定时器也可以完成两类事情:
                     1.在 " 未来 " 某个时间点,运行函数
                     2.周期性地运行函数
使用定时器跟使用手机闹钟是类似的:
       1.指定时间:启动定时器和运行回调函数,两者的间隔被称为定时器的周期 (period)。
       2.指定类型,定时器有两种类型:
    一次性 (One-shot timers)
               这类定时器启动后,它的回调函数只会被调用一次,可以手工再次启动它,但是不会自动启动它。
自动加载定时器 (Auto-reload timers )
               这类定时器启动后,时间到之后它会自动启动它,这使得回调函数被周期性地调用。
      3.指定要做什么事,就是指定回调函数

FreeRTOS中有一个Tick中断,软件定时器基于Tick来运行,在Tick中断里执行: 在 Tick 中断中判断定时器是否超时 ,如果超时了,调用它的回调函数。

FreeRTOS 是 RTOS,它不允许在内核、在中断中执行不确定的代码:如果定时器函数

很耗时,会影响整个系统。 所以,FreeRTOS中,不在Tick中断中执行定时器函数。

在哪里执行?在某个任务里执行,这个任务就是:RTOS Damemon TaskRTOS守护任

务。以前被称为"Timer server",但是这个任务要做并不仅仅是定时器相关,所以改名为:

RTOS Damemon Task。 当FreeRTOS的配置项configUSE_TIMERS被设置为1时,在启动调度器时,会自动创 建RTOS Damemon Task。 我们自己编写的任务函数要使用定时器时,是通过"定时器命令队列"(timer command queue)和守护任务交互,如下图所示:

定时器函数

创建定时器:

/* 使用动态分配内存的方法创建定时器
* pcTimerName:定时器名字, 用处不大, 尽在调试时用到
* xTimerPeriodInTicks: 周期, 以 Tick 为单位
* uxAutoReload: 类型, pdTRUE 表示自动加载, pdFALSE 表示一次性
* pvTimerID: 回调函数可以使用此参数, 比如分辨是哪个定时器
* pxCallbackFunction: 回调函数(类型:void ATimerCallback( TimerHandle_t xTimer );)
* 返回值: 成功则返回 TimerHandle_t, 否则返回 NULL
*/
TimerHandle_t xTimerCreate( 
const char * const pcTimerName, 
const TickType_t xTimerPeriodInTicks, 
const UBaseType_t uxAutoReload, 
void * const pvTimerID, 
TimerCallbackFunction_t pxCallbackFunction );


/* 使用静态分配内存的方法创建定时器
* pcTimerName:定时器名字, 用处不大, 尽在调试时用到
* xTimerPeriodInTicks: 周期, 以 Tick 为单位
* uxAutoReload: 类型, pdTRUE 表示自动加载, pdFALSE 表示一次性
* pvTimerID: 回调函数可以使用此参数, 比如分辨是哪个定时器
* pxCallbackFunction: 回调函数(类型:typedef void (* TimerCallbackFunction_t)( TimerHandle_t xTimer );)
* pxTimerBuffer: 传入一个 StaticTimer_t 结构体, 将在上面构造定时器
* 返回值: 成功则返回 TimerHandle_t, 否则返回 NULL
*/
TimerHandle_t xTimerCreateStatic(
const char * const pcTimerName, 
TickType_t xTimerPeriodInTicks, 
UBaseType_t uxAutoReload, 
void * pvTimerID, 
TimerCallbackFunction_t pxCallbackFunction, 
StaticTimer_t *pxTimerBuffer );

删除定时器:

/* 删除定时器
* xTimer: 要删除哪个定时器
* xTicksToWait: 超时时间
* 返回值: pdFAIL 表示"删除命令"在 xTicksToWait 个 Tick 内无法写入队列
* pdPASS 表示成功
*/
BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );

启动和停止定时器:

/* 启动定时器
* xTimer: 哪个定时器
* xTicksToWait: 超时时间
* 返回值: pdFAIL 表示"启动命令"在 xTicksToWait 个 Tick 内无法写入队列
* pdPASS 表示成功
*/
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );


/* 启动定时器(ISR 版本) 
* xTimer: 哪个定时器
* pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒, 如果守护任务的优先级比当前任务的高, 则"*pxHigherPriorityTaskWoken = pdTRUE", 表示需要进行任务调度
* 返回值: pdFAIL 表示"启动命令"无法写入队列
* pdPASS 表示成功
*/
BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken );


/* 停止定时器
* xTimer: 哪个定时器
* xTicksToWait: 超时时间
* 返回值: pdFAIL 表示"停止命令"在 xTicksToWait 个 Tick 内无法写入队列
* pdPASS 表示成功
*/
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );


/* 停止定时器(ISR 版本) 
* xTimer: 哪个定时器
* pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒, 如果守护任务的优先级比当前任务的高, 则"*pxHigherPriorityTaskWoken = pdTRUE", 表示需要进行任务调度
* 返回值: pdFAIL 表示"停止命令"无法写入队列
* pdPASS 表示成功
*/
BaseType_t xTimerStopFromISR( TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken );

复位:

/* 复位定时器
* xTimer: 哪个定时器
* xTicksToWait: 超时时间
* 返回值: pdFAIL 表示"复位命令"在 xTicksToWait 个 Tick 内无法写入队列
* pdPASS 表示成功
*/
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );

/* 复位定时器(ISR 版本)
* xTimer: 哪个定时器
* pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒, 如果守护任务的优先级比当前任务的高, 则"*pxHigherPriorityTaskWoken = pdTRUE", 表示需要进行任务调度
* 返回值: pdFAIL 表示"停止命令"无法写入队列
* pdPASS 表示成功
*/
BaseType_t xTimerResetFromISR( TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken );

修改周期:

/* 修改定时器的周期
* xTimer: 哪个定时器
* xNewPeriod: 新周期
* xTicksToWait: 超时时间, 命令写入队列的超时时间
* 返回值: pdFAIL 表示"修改周期命令"在 xTicksToWait 个 Tick 内无法写入队列
* pdPASS 表示成功
*/
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, TickType_t xNewPeriod, TickType_t xTicksToWait );

/* 修改定时器的周期
* xTimer: 哪个定时器
* xNewPeriod: 新周期
* pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒, 如果守护任务的优先级比当前任务的高, 则"*pxHigherPriorityTaskWoken = pdTRUE", 表示需要进行任务调度
* 返回值: pdFAIL 表示"修改周期命令"在 xTicksToWait 个 Tick 内无法写入队列
* pdPASS 表示成功
*/
BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer, TickType_t xNewPeriod, BaseType_t *pxHigherPriorityTaskWoken );

定时器ID:

在定时器结构体里面有一项pvTimerID,他就是定时器的ID

/* 获得定时器的 ID
* xTimer: 哪个定时器
* 返回值: 定时器的 ID
*/
void *pvTimerGetTimerID( TimerHandle_t xTimer );

/* 设置定时器的 ID
* xTimer: 哪个定时器
* pvNewID: 新 ID
* 返回值: 无
*/
void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID );

两套API函数

资源管理

屏蔽中断:

/* 在任务中,当前时刻中断是使能的
 * 执行这句代码后,屏蔽中断
*/
taskENTER_CRITICAL();
/* 访问临界资源 */
/* 重新使能中断 */
taskEXIT_CRITICAL();


//在ISR中屏蔽中断
void vAnInterruptServiceRoutine( void )
{
/* 用来记录当前中断是否使能 */
UBaseType_t uxSavedInterruptStatus;
/* 在 ISR 中,当前时刻中断可能是使能的,也可能是禁止的
 * 所以要记录当前状态, 后面要恢复为原先的状态
 * 执行这句代码后,屏蔽中断
*/
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
/* 访问临界资源 */
/* 恢复中断状态 */
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
/* 现在,当前 ISR 可以被更高优先级的中断打断了 */
}

暂停调度器:

/* 暂停调度器 */
void vTaskSuspendAll( void );

/* 恢复调度器
* 返回值: pdTRUE 表示在暂定期间有更高优先级的任务就绪了
* 可以不理会这个返回值
*/
BaseType_t xTaskResumeAll( void );


示例:
vTaskSuspendScheduler();
/* 访问临界资源 */
xTaskResumeScheduler();

优化设计

使用以下函数需配置的宏:

#define configGENERATE_RUN_TIME_STATS  1
#define configUSE_TRACE_FACILITY  1
#define configUSE_STATS_FORMATTING_FUNCTIONS  1

获取系统中所有任务的信息:

UBaseType_t uxTaskGetSystemState(
TaskStatus_t * const pxTaskStatusArray, 
const UBaseType_t uxArraySize, 
uint32_t * const pulTotalRunTime);

/*参数:

 pxTaskStatusArray:指向TaskStatus_t结构体数组的指针,用于存储任务状态信息。

 uxArraySize:指定pxTaskStatusArray数组的大小,即要获取状态信息的最大任务数量。

 pulTotalRunTime:用于接收系统运行的总时间(以滴答数为单位)。

返回值:返回实际填充到pxTaskStatusArray中的任务数量。*/

获得任务统计信息,形式为可读字符串:

void vTaskList( signed char *pcWriteBuffer );

//最好在空闲任务的钩子函数里运行
/*参数

pcWriteBuffer:指向一个字符数组的指针,用于存储任务列表信息。该数组的大小需要足够大,以容纳所有任务的信息。

返回值

无。函数执行后,任务列表信息将被写入到pcWriteBuffer指向的字符数组中*/

获取任务运行时间统计:



void vTaskGetRunTimeStats(char *pcWriteBuffer);
//最好在空闲任务的钩子函数里运行
/*参数

pcWriteBuffer:指向一个字符数组的指针,用于存储任务运行时间统计信息。数组大小应足够大,以容纳所有任务的相关信息。

返回值

无。函数执行后,任务运行时间统计信息将被写入到pcWriteBuffer指向的字符数组中*/

当前系统中正在运行的任务数量:

UBaseType_t uxTaskGetNumberOfTasks(void);

/*
参数:无。

返回值:当前系统中正在运行的任务数量*/

返回指定任务的当前状态:

eTaskState eTaskGetState(TaskHandle_t xTask);

/* 参数:xTask是要查询状态的任务句柄。

返回值:返回指定任务的当前状态,是eTaskState枚举类型的值,如eRunning(运行态)、eReady(就绪态)、eBlocked(阻塞态)、eSuspended(挂起态)*/

查看任务剩余的栈:

uint32_t ulTaskGetStackHighWaterMark(TaskHandle_t xTask);

/*参数:xTask为要获取堆栈高水位标记的任务句柄。

 返回值:返回指定任务堆栈中剩余的最少字节数,即堆栈的高水位标记*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值