FreeRTOS常用API
一、任务[task.h]
1、任务创建
如果任务成功创建并加入就绪列表函数返回pdPASS,否则函数返回错误码,具体参见projdefs.h。
xTaskCreate((TaskFunction_t) master_task_main, /* 任务入口函数 */
“MASTER”, /* 任务名字 */
64*1024, /* 任务栈大小 */
NULL, ,/* 任务入口函数参数 */
TASK_PRIORITY_NORMAL, /* 任务的优先级 */
&task_master_handler); /* 任务控制块指针 */
用法举例:
/* 创建任务. */
void vTaskCode( void * pvParameters )
{
for( ;; )
{
/* 任务代码放在这里 */
}
}
/* 创建任务函数 */
void vOtherFunction( void )
{
static unsigned char ucParameterToPass;
xTaskHandlexHandle;
/* 创建任务,存储句柄。注:传递的参数ucParameterToPass必须和任务具有相同的生存周期,
因此这里定义为静态变量。如果它只是一个自动变量,可能不会有太长的生存周期,因为
中断和高优先级任务可能会用到它。 */
xTaskCreate( vTaskCode, "NAME", STACK_SIZE,&ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
/* 使用句柄删除任务. */
if( xHandle !=NULL )
{
vTaskDelete( xHandle );
}
}
2、任务删除
从RTOS内核管理器中删除一个任务。任务删除后将会从就绪、阻塞、暂停和事件列表中移除。在文件FreeRTOSConfig.h中,必须定义宏INCLUDE_vTaskDelete为1,本函数才有效。
void vTaskDelete( TaskHandle_t xTask );//xTask为被删除任务的句柄,为NULL表示删除当前任务。
3、相对延时
调用vTaskDelay()函数后,任务会进入阻塞状态,持续时间由vTaskDelay()函数的参数xTicksToDelay指定,单位是系统节拍时钟周期。常量portTICK_RATE_MS用来辅助计算真实时间,此值是系统节拍时钟中断的周期,单位是毫秒。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskDelay 必须设置成1,此函数才能有效。
void vTaskDelay( portTickTypexTicksToDelay );//xTicksToDelay:延时时间总数,单位是系统时钟节拍周期。
用法举例:
voidvTaskFunction( void * pvParameters )
{
/* 阻塞500ms. */
constportTickType xDelay = 500 / portTICK_RATE_MS;
for( ;; )
{
/* 每隔500ms触发一次LED, 触发后进入阻塞状态 */
vToggleLED();
vTaskDelay( xDelay );
}
}
vTaskDelay()指定的延时时间是从调用vTaskDelay()后开始计算的相对时间。比如vTaskDelay(100),那么从调用vTaskDelay()后,任务进入阻塞状态,经过100个系统时钟节拍周期,任务解除阻塞。因此,vTaskDelay()并不适用与周期性执行任务的场合。此外,其它任务和中断活动,会影响到vTaskDelay()的调用(比如调用前高优先级任务抢占了当前任务),因此会影响任务下一次执行的时间。API函数vTaskDelayUntil()可用于固定频率的延时,它用来延时一个绝对时间。
4、获取任务优先级
获取指定任务的优先级。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskPriorityGet必须设置成1,此函数才有效。
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask );//xTask:任务句柄。NULL表示获取当前任务的优先级。
用法举例:
void vAFunction(void)
{
xTaskHandlexHandle;
// 创建任务,保存任务句柄
xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
// 使用句柄获取创建的任务的优先级
if( uxTaskPriorityGet( xHandle ) !=tskIDLE_PRIORITY )
{
// 任务可以改变自己的优先级
}
// ...
// 当前任务优先级比创建的任务优先级高?
if( uxTaskPriorityGet( xHandle ) <uxTaskPriorityGet( NULL ) )
{
// 当前优先级较高
}
}
5、任务挂起
挂起指定任务。被挂起的任务绝不会得到处理器时间,不管该任务具有什么优先级。调用vTaskSuspend函数是不会累计的:即使多次调用vTaskSuspend()函数将一个任务挂起,也只需调用一次vTaskResume()函数就能使挂起的任务解除挂起状态。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskSuspend必须设置成1,此函数才有效。
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
用法举例:
voidvAFunction( void )
{
xTaskHandlexHandle;
// 创建任务,保存任务句柄.
xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
// 使用句柄挂起创建的任务.
vTaskSuspend( xHandle );
// ...
// 任务不再运行,除非其它任务调用了vTaskResume(xHandle )
//...
// 挂起本任务.
vTaskSuspend( NULL );
// 除非另一个任务使用handle调用了vTaskResume,否则永远不会执行到这里
}
6、恢复挂起的任务
恢复挂起的任务。通过调用一次或多次vTaskSuspend()挂起的任务,可以调用一次vTaskResume()函数来再次恢复运行。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskSuspend必须置1,此函数才有效。
void vTaskResume( TaskHandle_txTaskToResume );
用法举例:
voidvAFunction( void )
{
xTaskHandle xHandle;
// 创建任务,保存任务句柄
xTaskCreate( vTaskCode, "NAME",STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
// 使用句柄挂起创建的任务
vTaskSuspend( xHandle );
// ...
//任务不再运行,除非其它任务调用了vTaskResume(xHandle )
//...
// 恢复挂起的任务.
vTaskResume( xHandle );
// 任务再一次得到处理器时间
// 任务优先级与之前相同
}
7、任务状态切换
FreeRTOS 系统中的每一个任务都有多种运行状态,具体如下:
二、队列[queue.h]
1、队列的基本用法
- 定义一个队列句柄变量,用于保存创建的队列:xQueueHandle xQueue1;
- 使用API函数xQueueCreate()创建一个队列。
- 如果希望使用先进先出队列,使用API函数xQueueSend()或xQueueSendToBack()向队列投递队列项。如果希望使用后进先出队列,使用API函数xQueueSendToFront()向队列投递队列项。如果在中断服务程序中,切记使用它们的带中断保护版本。
- 使用API函数xQueueReceive()从队列读取队列项,如果在中断服务程序中,切记使用它们的带中断保护版本。
2、队列相关的API
2.1 创建队列xQueueCreate
创建新队列,为新队列分配指定的存储空间并返回队列句柄。若返回值为NULL,则创建失败。
QueueHandle_t xQueueCreate (UBaseType_t uxQueueLength,//队列项数目
UBaseType_t uxItemSize); //每个队列项大小,单位是字节。队列项通过拷贝入队而不是通过引用入队,因此需要队列项的大小。每个队列项的大小必须相同。
用法示例:
struct AMessage
{
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
};
void vATask( void*pvParameters )
{
xQueueHandle xQueue1, xQueue2;
// 创建一个队列,队列能包含10个unsigned long类型的值。
xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ));
if( xQueue1 ==0 )
{
// 队列创建失败,不可以使用
}
// 创建一个队列,队列能包含10个 Amessage结构体指针类型的值。
// 这样可以通过传递指针变量来包含大量数据。
xQueue2 =xQueueCreate( 10, sizeof( struct AMessage * ) );
if( xQueue2 ==0 )
{
// 队列创建失败,不可以使用
}
// ... 任务的其它代码.
}
2.2 发送数据xQueueSend
队列项入队成功返回pdTRUE,否则返回errQUEUE_FULL。
portBASE_TYPE xQueueSend( xQueueHandle xQueue, //哪个队列
const void * pvItemToQueue, //存放什么数据
portTickType xTicksToWait );//等待时间
用法举例:
struct AMessage
{
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
}xMessage;
unsigned portLONG ulVar = 10UL;
void vATask( void *pvParameters )
{
xQueueHandle xQueue1, xQueue2;
struct AMessage *pxMessage;
/*创建一个队列,队列能包含10个unsigned long类型的值。*/
xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) );
/* 创建一个队列,队列能包含10个 Amessage结构体指针类型的值。
这样可以通过传递指针变量来包含大量数据。*/
xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
// ...
if( xQueue1 != 0 )
{
/*1个unsigned long型数据入队.如果需要等待队列空间变的有效,
会最多等待10个系统节拍周期*/
if( xQueueSend( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) !=pdPASS )
{
/*消息入队失败*/
}
}
if( xQueue2 != 0 )
{
/* 发送一个指向结构体Amessage的对象,如果队列满也不等待 */
pxMessage = & xMessage;
xQueueSend( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 );
}
//... 任务其余代码.
}
2.3 接收数据xQueueReceive
成功接收到列表项返回pdTRUE,否则返回pdFALSE。
portBASE_TYPE xQueueReceive( xQueueHandle xQueue, //要从哪个队列取数据
void *pvBuffer, //取出来放哪
portTickType xTicksToWait );//取数据等待时间
用法举例:
struct AMessage
{
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
} xMessage;
xQueueHandle xQueue;
// 创建一个队列并投递一个值
void vATask( void *pvParameters )
{
struct AMessage *pxMessage;
// 创建一个队列,队列能包含10个 Amessage结构体指针类型的值。
// 这样可以通过传递指针变量来包含大量数据。
xQueue =xQueueCreate( 10, sizeof( struct AMessage * ) );
if( xQueue == 0)
{
// 创建队列失败
}
// ...
// 向队列发送一个指向结构体对象Amessage的指针,如果队列满不等待
pxMessage = & xMessage;
xQueueSend(xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );
// ... 其它代码
}
// 该任务从队列中接收一个队列项
voidvADifferentTask( void *pvParameters )
{
struct AMessage *pxRxedMessage;
if( xQueue != 0)
{
// 从创建的队列中接收一个消息,如果消息无效,最多阻塞10个系统节拍周期
if(xQueueReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) )
{
// 现在pcRxedMessage 指向由vATask任务投递进来的结构体Amessage变量
}
}
// ... 其它代码
}
2.4 读取但不移除队列项[同xQueueReceive]
成功接收到列表项返回pdTRUE,否则返回pdFALSE。
BaseType_t xQueuePeek( QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait);
三、FreeRTOS信号量[semphr. h]
1. 信号量简介
FreeRTOS的信号量包括二进制信号量、计数信号量、互斥信号量(以后简称互斥量)和递归互斥信号量(以后简称递归互斥量)。我们可以把互斥量和递归互斥量看成特殊的信号量。互斥量和信号量在用法上不同:
- 信号量用于同步,任务间或者任务和中断间同步;互斥量用于互锁,用于保护同时只能有一个任务访问的资源,为资源上一把锁。
- 信号量用于同步时,一般是一个任务(或中断)给出信号,另一个任务获取信号;互斥量必须在同一个任务中获取信号、同一个任务给出信号。
- 互斥量具有优先级继承,信号量没有。
- 互斥量不能用在中断服务程序中,信号量可以。
- 创建互斥量和创建信号量的API函数不同,但是共用获取和给出信号API函数。
2. 二进制信号量
二进制信号量既可以用于互斥功能也可以用于同步功能。
3. 计数信号量
二进制信号量可以被认为是长度为1的队列,计数信号量则可以被认为长度大于1的队列。此外,信号量使用者不必关心存储在队列中的数据,只需关心队列是否为空。
通常计数信号量用于下面两种事件:
- 计数事件:在这种场合下,每当事件发生,事件处理程序将给出一个信号(信号量计数值增1),当处理事件时,处理程序会取走信号量(信号量计数值减1)。因此,计数值是事件发生的数量和事件处理的数量差值。在这种情况下,计数信号量在创建时其值为0。
- 资源管理:这种用法下,计数值表示有效的资源数目。任务必须先获取信号量才能获取资源控制权。当计数值减为零时表示没有的资源。当任务完成后,它会返还信号量—信号量计数值增加。在这种情况下,信号量创建时,计数值等于最大资源数目。
4. 互斥量
用于互锁的互斥量可以充当保护资源的令牌。当一个任务希望访问某个资源时,它必须先获取令牌。当任务使用完资源后,必须还回令牌,以便其它任务可以访问同一资源。
互斥量与二进制信号量最大的不同是:互斥量具有优先级继承机制。如果一个互斥量(令牌)正在被一个低优先级任务使用,此时一个高优先级企图获取这个互斥量,高优先级任务会因为得不到互斥量而进入阻塞状态,正在使用互斥量的低优先级任务会临时将自己的优先级提升,提升后的优先级与与进入阻塞状态的高优先级任务相同。这个优先级提升的过程叫做优先级继承。这个机制用于确保高优先级任务进入阻塞状态的时间尽可能短,以及将已经出现的**“优先级翻转”影响降低到最小。
互斥量用来保护资源。为了访问资源,任务必须先获取互斥量。任务A想获取资源,首先它使用API函数xSemaphoreTake()获取信号量,成功获取到信号量后,任务A就持有了互斥量,可以安全的访问资源。期间任务B开始执行,它也想访问资源,任务B也要先获得信号量,但是信号量此时是无效的,任务B进入阻塞状态。当任务A执行完成后,使用API函数xSemaphoreGive()**释放信号量。之后任务B解除阻塞,任务B使用API函数xSemaphoreTake()获取并得到信号量,任务B可以访问资源。
5. 相关API
5.1 信号创建xSemaphoreCreateMutex
使用已存在的队列结构来创建互斥锁信号量的宏。通过此宏创建的互斥锁可以使用**xSemaphoreTake()与xSemaphoreGive()**宏来访问;返回值:信号量句柄。
xSemaphoreHandle xSemaphoreCreateMutex( void );
用法举例:
xSemaphoreHandle xSemaphore;
voidvATask( void * pvParameters )
{
// 互斥量在未创建之前是不可用的
xSemaphore = xSemaphoreCreateMutex();
if( xSemaphore != NULL )
{
// 创建成功
// 在这里可以使用这个互斥量了
}
}
5.2 获取信号量xSemaphoreTake
xSemaphoreTake(SemaphoreHandle_t xSemaphore, //信号量句柄
TickType_t xTicksToWait); //信号量无效时,任务最多等待的时间,单位是系统节拍周期个数。使用宏portTICK_PERIOD_MS可以辅助将系统节拍个数转化为实际时间(以毫秒为单位)。如果设置为0,表示不是设置等待时间。如果INCLUDE_vTaskSuspend设置为1,并且参数xTickToWait为portMAX_DELAY则可以无限等待。
返回值:成功获取到信号量返回pdTRUE,否则返回pdFALSE。用法举例:
SemaphoreHandle_t xSemaphore = NULL;
/*这个任务创建信号量 */
void vATask( void * pvParameters )
{
/*创建互斥型信号量,用于保护共享资源。*/
xSemaphore = xSemaphoreCreateMutex();
}
/* 这个任务使用信号量 */
void vAnotherTask( void * pvParameters )
{
/* ... 做其它事情. */
if( xSemaphore != NULL )
{
/*如果信号量无效,则最多等待10个系统节拍周期。*/
if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
{
/*到这里我们获取到信号量,现在可以访问共享资源了*/
/* ... */
/* 完成访问共享资源后,必须释放信号量*/
xSemaphoreGive( xSemaphore );
}
else
{
/* 没有获取到信号量,这里处理异常情况。*/
}
}
}
5.3 释放信号量xSemaphoreGive
用于释放一个信号量。信号量必须是API函数xSemaphoreCreateBinary()、xSemaphoreCreateCounting()或xSemaphoreCreateMutex() 创建的。必须使用API函数xSemaphoreTake()获取这个信号量。
xSemaphoreGive(SemaphoreHandle_t xSemaphore );// 信号量释放成功返回pdTRUE,否则返回pdFALSE。
用法示例:
SemaphoreHandle_t xSemaphore = NULL;
voidvATask( void * pvParameters )
{
// 创建一个互斥量,用来保护共享资源
xSemaphore = xSemaphoreCreateMutex();
if( xSemaphore != NULL )
{
if( xSemaphoreGive( xSemaphore ) != pdTRUE )
{
//我们希望这个函数调用失败,因为首先要获取互斥量
}
// 获取信号量,不等待
if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
{
// 现在我们拥有互斥量,可以安全的访问共享资源
if( xSemaphoreGive( xSemaphore ) != pdTRUE )
{
//我们不希望这个函数调用失败,因为我们必须
//要释放已获取的互斥量
}
}
}
}
四、软定时器实现
1、基本概念
软件定时器在被创建之后,当经过设定的时钟计数值后会触发用户定义的回调函数。 定时精度与系统时钟的周期有关。一般系统利用 SysTick 作为软件定时器的基础时钟,软件定时器的回调函数类似硬件的中断服务函数,所以,回调函数也要快进快出,而且回调函数中不能有任何阻塞任务运行的情况(软件定时器回调函数的上下文环境是任务),比如 vTaskDelay() 以及其它能阻塞任务运行的函数,两次触发回调函数的时间间隔 xTimerPeriodInTicks 叫定时器的定时周期。
FreeRTOS 提供的软件定时器支持单次模式和周期模式,单次模式和周期模式的定时时间到之后都会调用软件定时器的回调函数,用户可以在回调函数中加入要执行的工程代码。
- 单次模式:当用户创建了定时器并启动了定时器后,定时时间到了,只执行一次回调函数之后就将该定时器删除,不再重新执行。
- 周期模式:这个定时器会按照设置的定时时间循环执行回调函数,直到用户将定时器删除。
2、配置定时器服务任务
程序中需要使用到软件定时器, 需要先在FreeRTOSConfig.h中正确配置如下宏 :
- configUSE_TIMERS
是否编译定时器相关代码, 如需要使用定时器, 设置为 1- configTIMER_TASK_PRIORITY
设置定时器Daemon 任务优先级, 如果优先级太低, 可能导致定时器无法及时执行- configTIMER_QUEUE_LENGTH
设置定时器Daemon 任务的命令队列深度, 设置定时器都是通过发送消息到该队列实现的。- configTIMER_TASK_STACK_DEPTH
设置定时器Daemon 任务的栈大小
3、相关API函数(timers.h)
3.1 定时器创建函数
TimerHandle_t xTimerCreate
( const char * const pcTimerName,//软件定时器名字
const TickType_t xTimerPeriod,//软件定时器的周期
const UBaseType_t uxAutoReload,//设置为pdTRUE,那么软件定时器的工作模式就是周期模式;设为pdFALSE,则为单次模式
void * const pvTimerID,// 软件定时器ID
TimerCallbackFunction_t pxCallbackFunction );//软件定时器的回调函数
3.2 回调函数的写法
#define NUM_TIMERS 5
TimerHandle_t xTimers[ NUM_TIMERS ];
void vTimerCallback( TimerHandle_t xTimer )
{
const uint32_t ulMaxExpiryCountBeforeStopping = 10;
uint32_t ulCount;
configASSERT( xTimer );//断言函数,在使用调试器的情况下,一旦出现断言失败,会关闭中断,程序会死在这个for循环中,此时用户可以很容易就锁定函数出错位置。
ulCount = ( uint32_t ) pvTimerGetTimerID( xTimer );
ulCount++;
if( ulCount >= ulMaxExpiryCountBeforeStopping )
{
xTimerStop( pxTimer, 0 );
}
else
{
vTimerSetTimerID( xTimer, ( void * ) ulCount );
}
}
void main( void )
{
long x;
for( x = 0; x < NUM_TIMERS; x++ )
{
xTimers[ x ] = xTimerCreate("Timer",(1*1000/portTICK_PERIOD_MS),pdTRUE,NULL,vTimerCallback);
if( xTimers[ x ] == NULL )
{
/* The timer was not created. */
}
else{
if( xTimerStart( xTimers[ x ], 0 ) != pdPASS )
{
/* The timer could not be set into the Active state. */
}
}
}
/* ... Create tasks here.... */
vTaskStartScheduler();
for( ;; );
}
3.3 启动软件定时器
BaseType_t xTimerStart( TimerHandle_t xTimer, //要操作的软件定时器句柄
TickType_t xTicksToWait); // 用户指定的超时时间,单位为系统节拍周期(即 tick),如果在FreeRTOS调度器开启之前调用xTimerStart(),该形参将不起作用
返回值:如果启动命令无法成功地发送到定时器命令队列则返回pdFAILE,成功发送则返回pdPASS。软件定时器成功发送的命令是否真正的被执行也还要看定时器守护任务的优先级,其优先级由宏configTIMER_TASK_PRIORITY定义。
3.4 xTimerStop(任务)-停止一个软件定时器,让其进入休眠态。
BaseType_t xTimerStop( TimerHandle_t xTimer, //要操作的软件定时器句柄
TickType_t xBlockTime );//用户指定超时时间,单位为系统节拍周期(即tick)。
3.5使用范例
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
/*
* 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
* 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
* 们就可以通过这个句柄操作这些内核对象。
*
* 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
* 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
* 来完成的
*/
static TimerHandle_t Swtmr1_Handle = NULL; /* 软件定时器句柄 */
static TimerHandle_t Swtmr2_Handle = NULL; /* 软件定时器句柄 */
static uint32_t TmrCb_Count1 = 0; /* 记录软件定时器 1 回调函数执行次数 */
static uint32_t TmrCb_Count2 = 0; /* 记录软件定时器 2 回调函数执行次数 */
static void AppTaskCreate(void);/* 用于创建任务 */
static void Swtmr1_Callback(void* parameter);
static void Swtmr2_Callback(void* parameter);
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */
/* 创建 AppTaskCreate 任务 */
xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,/* 任务入口函数 */
(const char* )"AppTaskCreate",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
(TaskHandle_t*)&AppTaskCreate_Handle);/* 任务控制块指针 */
/* 启动任务调度 */
if (pdPASS == xReturn)
{
vTaskStartScheduler(); /* 启动任务,开启调度 */
}
else
{
return -1;
}
while (1); /* 正常不会执行到这里 */
}
static void AppTaskCreate(void)
{
taskENTER_CRITICAL(); //进入临界区
Swtmr1_Handle=xTimerCreate((const char*)"AutoReloadTimer",
(TickType_t)1000,/*定时器周期 1000(tick) */
(UBaseType_t)pdTRUE,/* 周期模式 */
(void*)1,/*为每个计时器分配一个索引的唯一 ID */
(TimerCallbackFunction_t)Swtmr1_Callback);
if (Swtmr1_Handle != NULL)
{
xTimerStart(Swtmr1_Handle,0); //开启周期定时器
}
Swtmr2_Handle=xTimerCreate((const char* )"OneShotTimer",
(TickType_t)5000,/*定时器周期 5000(tick) */
(UBaseType_t )pdFALSE,/* 单次模式 */
(void*)2,/*为每个计时器分配一个索引的唯一 ID */
(TimerCallbackFunction_t)Swtmr2_Callback);
if (Swtmr2_Handle != NULL)
{
xTimerStart(Swtmr2_Handle,0); //开启周期定时器
}
vTaskDelete(AppTaskCreate_Handle); //删除 AppTaskCreate 任务
taskEXIT_CRITICAL(); //退出临界区
}
/***********************************************************************
* @ 函数名 : Swtmr1_Callback
* @ 功能说明: 软件定时器 1 回调函数,打印回调函数信息&当前系统时间
* 软件定时器请不要调用阻塞函数,也不要进行死循环,应快进快出
* @ 参数 : 无
* @ 返回值 : 无
********************************************************/
static void Swtmr1_Callback(void* parameter)
{
TickType_t tick_num1;
TmrCb_Count1++; /* 每回调一次加一 */
tick_num1 = xTaskGetTickCount(); /* 获取滴答定时器的计数值 */
LED1_TOGGLE;
printf("swtmr1_callback 函数执行 %d 次\n", TmrCb_Count1);
printf("滴答定时器数值=%d\n", tick_num1);
}
/***********************************************************************
* @ 函数名 : Swtmr2_Callback
* @ 功能说明: 软件定时器 2 回调函数,打印回调函数信息&当前系统时间
* 软件定时器请不要调用阻塞函数,也不要进行死循环,应快进快出
* @ 参数 : 无
* @ 返回值 : 无
********************************************************/
static void Swtmr2_Callback(void* parameter)
{
TickType_t tick_num2;
TmrCb_Count2++; /* 每回调一次加一 */
tick_num2 = xTaskGetTickCount(); /* 获取滴答定时器的计数值 */
printf("swtmr2_callback 函数执行 %d 次\n", TmrCb_Count2);
printf("滴答定时器数值=%d\n", tick_num2);
}