wordpress怎么制作网站主页,桂林做手机网站建设,湖南住房和城乡建设厅网站,东莞网站建设 模具STM32F1HAL库FreeTOTS学习19——软件定时器 1 软件定时器1.1 FreeRTOS软件定时器简介1.2 FreeRTOS软件定时器服务任务1.3 FreeRTOS软件定时器服命令队列。1.4 软件定时器的状态1.5 复位定时器1.6 软件定时器结构体 2 软件定时器配置3 软件定时器API函数3.1 xTimerCreate()和xTi… STM32F1HAL库FreeTOTS学习19——软件定时器 1 软件定时器1.1 FreeRTOS软件定时器简介1.2 FreeRTOS软件定时器服务任务1.3 FreeRTOS软件定时器服命令队列。1.4 软件定时器的状态1.5 复位定时器1.6 软件定时器结构体 2 软件定时器配置3 软件定时器API函数3.1 xTimerCreate()和xTimerCreateStatic()3.2 xTimerStart()和xTimerStartFromISR()3.3 xTimerStop()和xTimerStopFromISR()3.4 xTimerReset()和xTimerResetFromISR()3.5 xTimerChangePeriod()和xTimerChangePeriodFromISR()3.6 xTimerDelete()3.7 xTimerIsTimerActive()3.8 pvTimerGetTimerID()3.9 vTimerSetReloadMode()3.10 vTimerSetTimerID()3.11 xTimerPendFunctionCall()和xTimerPendFunctionCallFromISR()3.12 pcTimerGetName()3.13 xTimerGetPeriod()3.14 xTimerGetExpiryTime()3.15 xTimerGetReloadMode() 和uxTimerGetReloadMode() 4 软件定时器操作实验4.1. 实验内容4.2 代码实现4.3 运行结果 上期内容中我们学习了任务通知的相关内容本期我们开始介绍FreeRTOS中的软件定时器。 1 软件定时器
我们在学习32的时候或多或少都接触过定时器在我们学习32的时候我们从最简单基本定时器到带有输入捕获比较输出的通用定时器、再到互补输出、死区控制的高级定时从看门狗到RTC还有sysTick定时器了解了各种各样的定时器学习了各种各样的配置但这些定时器都有一个共同的特点都是单片机芯片的片上外设是在硬件层面的硬件定时器。
而我们今天要学习的是FreeRTOS中的软件定时器是基于FreeRTOS的系统节拍实现的属于软件层面它不像硬件定时器那样可以实现精确的定时、也没有丰富的功能只能适用于一些对定时器任务精度不高的场景。
同时软件定时还具有使用简单不需要考虑中断与任务之间的交互处理成本低等优点让我们在茫茫人海中一眼看中了它。下面我们来简单的介绍一些软件定时器
1.1 FreeRTOS软件定时器简介
在FreeRTOS中提供了用户创建软件定时器的机会在创建的时候可以设置对应的超时时间在软件定时器创建成功并启动之后开始计时达到超时时间之后就会调用相应的回调函数进行任务的处理。
用户还可以根据需要选择定时器是周期的还是单次的类似于硬件定时器里面的是否重装载以满足不同的使用场景软件定时器的数量是可以任意的前提是你的堆栈空间足够大在FreeRTOS中软件定时器不是必选项是支持裁剪的需要使用的话需要在FreeRTOSConfig.h 中将 configUSE_TIMERS配置为1这个在后面会介绍。
1.2 FreeRTOS软件定时器服务任务
我们在前面学习STM32F1HAL库FreeTOTS学习8——第一个任务启动的时候有介绍到在调用vTaskStartScheduler()函数开启任务调度的时候分别创建了空闲任务和软件定时器服务任务也就是我们这里提到的软件定时器服务任务。
#endif /* ( ( INCLUDE_xTaskResumeFromISR 1 ) ( INCLUDE_vTaskSuspend 1 ) ) */
/*-----------------------------------------------------------*/void vTaskStartScheduler( void )
{/*、、上面代码省略、、*//* 判断是否使用软件定时器 */#if ( configUSE_TIMERS 1 ){if( xReturn pdPASS ){/* 调用这个函数里面会创建软件定时器服务任务 */xReturn xTimerCreateTimerTask();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_TIMERS *//*、、下面代码省略、、*/}实际上不管我们创建了多少了软件定时器最后都是跑到这个软件定时器服务任务中进行逻辑判断调用对应的超时回调函数通知这个软件定时器服务任务还需要负责处理软件定时器命令队列的消息。
1.3 FreeRTOS软件定时器服命令队列。
在FreeRTOS中提供了很多对应的API函数这些API函数大部分都是往定时器命令队列中写入消息发送命令这个队列是FreeRTOS源码中提供个软件定时器使用的。不支持用户直接访问只能通过这些API函数来写入消息发送命令。操作过程如下
1.4 软件定时器的状态
休眠态刚刚创建的软件定时器处于休眠态可以通过其句柄被引用但是其超时回调函数不会被执行简单来说就是存在但是没有开始计时。运行态处于运行态的定时器可以通过其句柄进行引用同时其超时回调函数也会被执行简单来说就是存在且开始计时了。
下面是软件定时器状态的转换方式
单次定时器只会执行一次超时回调函数不会重装载 周期定时器每到一次超时时间就会执行一次超时回调函数会重装载 可以看到在定时器状态转换上单次定时器执行回调函数后会从运行态转换为休眠态而周期定时器则会保持在运行态除此之外欸有任何区别。 1.5 复位定时器
如下图所示在超时时间到来之前重复的执行复位定时器操作就可以导致超时回调函数一直不发生不管单次定时器还是周期定时器都一样这样的操作会导致定时器的周期不可控应该避免这种操作当然如果你是故意这样做的喂狗当我没说。 以上就是关于软件定时器的所有简介部分下面我们来看一些使用软件定时器需要做的一些配置。 1.6 软件定时器结构体 下面是软件定时器的结构体成员变量 typedef struct{const char * pcTimerName /* 软件定时器名字 */ListItem_t xTimerListItem /* 软件定时器列表项 */TickType_t xTimerPeriodInTicks; /* 软件定时器的周期 */ void * pvTimerID /* 软件定时器的ID */TimerCallbackFunction_t pxCallbackFunction; /* 软件定时器的回调函数 */#if ( configUSE_TRACE_FACILITY 1 )UBaseType_t uxTimerNumber /* 软件定时器的编号调试用 */#endifuint8_t ucStatus; /* 软件定时器的状态 */} xTIMER;2 软件定时器配置
前面我们提到软件定时器在FreeRTOS中是可以裁剪的他并不是FreeRTOS的必需品所以我们在使用软件定时器的时候就需要做以下配置。 1. configUSE_TIMERS
次宏用来开启软件定时器需要将此宏定义为1才会在开启任务调度的过程中创建软件定时器服务任务。
2. configTIMER_TASK_PRIORITY
此宏用于配置软件定时器服务任务的优先级当使能了软件定时器功能之后会根据此宏来配置软件定时器服务任务的优先级取值区间为0 ~ (configMAX_PRIORITY-1)
3. configTIMER_QUEUE_LENGTH
此宏用于配置软件定时器命令队列的长度此宏的长度必须大于0不然无法正常使用软件定时器功能。
4. configTIMER_TASK_STACK_DEPTH
此宏用于配置软件定时器服务任务的栈空间大小由于软件定时器的所有超时回调函数都是由软件定时器服务任务调用因此在创建的定时器的时候尤其要考虑栈溢出的风险需要适当增大软件定时器服务任务的栈空间大小。
3 软件定时器API函数
FreeRTOS中提供了一些软件定时器的操作函数在源码中的timers.c / timers.h 文件中有定义。如下表 表1包含软件定时器的一些基本操作创建、开启、停止、复位、删除 函数描述xTimerCreate()动态方式创建软件定时器xTimerCreateStatic()静态方式创建软件定时器xTimerStart()开启软件定时器定时xTimerStartFromISR()在中断中开启软件定时器定时xTimerStop()停止软件定时器定时xTimerStopFromISR()在中断中停止软件定时器定时xTimerReset()复位软件定时器定时xTimerResetFromISR()在中断中复位软件定时器定时xTimerChangePeriod()更改软件定时器的定时超时时间xTimerChangePeriodFromISR()在中断中更改软件定时器的定时超时时间xTimerDelete()删除软件定时器 表2包含软件定时器的一些查询操作查看一些状态、以及两个特殊使用场景的请求调用函数。 函数描述xTimerIsTimerActive()查询软件定时器是否处于活动或休眠状态pvTimerGetTimerID()返回分配给软件定时器的 IDvTimerSetReloadMode()将软件定时器的“模式”更新为自动重新加载定时器或一次性 定时器vTimerSetTimerID()更改定时器IDxTimerPendFunctionCall()用于请求一个函数这个函数叫做xTimerPendFunctionCall(),会在软件定时器服务任务中执行xTimerPendFunctionCallFromISR()在中断中请求一个函数这个函数叫做xTimerPendFunctionCall(),会在软件定时器服务任务中执行pcTimerGetName()获取软件定时器的名字xTimerGetPeriod()返回软件计时器的周期。周期以滴答为单位xTimerGetExpiryTime()获取软件定时器下一次的超时时间xTimerGetReloadMode ()查询软件定时器的模式uxTimerGetReloadMode()查询软件定时器的模式
下面我们来简单的了解一些这些个函数
3.1 xTimerCreate()和xTimerCreateStatic()
此函数用于创建软件定时器并返回软件定时器的句柄。其中创建方式分为动态和静态的方式动态方式由FreeRTOS动态分配内存而静态方式则是自己分配内存。其他部分上使用没有什么区别所以我们这里重要介绍动态的方式。下面是函数的原型
/*** brief xTimerCreate动态方式创建软件定时器* param pcTimerName: 软件定时器的名字* param xTimerPeriod: 软件定时器的周期默认系统的滴答值* param uxAutoReload: 是否为周期定时器为pdTRUE是周期定时器为pdFLASE是单次定时器* param pvTimerID: 定时器的ID值由于判断软件定时器服务任务具体调用哪一个回调函数* param pxCallbackFunction : 定时器超时时间到达的回调函数* retval 返回值为NULL表示创建失败。否则表示创建完成之后的句柄*/TimerHandle_t xTimerCreate( const char * const pcTimerName,const TickType_t xTimerPeriod,const UBaseType_t uxAutoReload,void * const pvTimerID,TimerCallbackFunction_t pxCallbackFunction );/*** brief xTimerCreateStatic动态方式创建软件定时器* param pcTimerName: 软件定时器的名字* param xTimerPeriod: 软件定时器的周期默认系统的滴答值* param uxAutoReload: 是否为周期定时器为pdTRUE是周期定时器为pdFLASE是单次定时器* param pvTimerID: 定时器的ID值由于判断软件定时器服务任务具体调用哪一个回调函数* param pxCallbackFunction : 定时器超时时间到达的回调函数* param pxTimerBuffer : 用户自己创建的堆栈用来存放软件定时器相关的结构体成员* retval 返回值为NULL表示创建失败。否则表示创建完成之后的句柄*/
TimerHandle_t xTimerCreateStatic( const char * const pcTimerName,const TickType_t xTimerPeriod,const UBaseType_t uxAutoReload,void * const pvTimerID,TimerCallbackFunction_t pxCallbackFunctionStaticTimer_t *pxTimerBuffer );
【注】软件定时器超时回调函数的原型如下
void vCallbackFunction( TimerHandle_t xTimer );3.2 xTimerStart()和xTimerStartFromISR()
此函数用于开启软件定时器前面我们说过刚刚创建的软件定时器是处于休眠态的所以在使用之前需先开启定时使其从休眠态转换为运行态下面是函数原型
/*** brief xTimerStart开启软件定时器* param xTimer: 需要开启的软件定时器句柄* param xBlockTime : 阻塞超时时间* retval 返回pdPASS表示成功发送命令到软件定时器服务命令队列返回值为pdFALL表示超时发送失败*/BaseType_t xTimerStart( TimerHandle_t xTimer,TickType_t xBlockTime );
/*** brief xTimerStartFromISR在中断中开启定时器* param xTimer: 需要开启的软件定时器句柄* param pxHigherPriorityTaskWoken: 是否需要进行任务交换如果为pdTRUE表示开启软件定时器之后软件定时器服务任务被唤醒* 且优先级大于当前正在执行的任务需要进行任务切换需要在中断退出之前进行任务切换否则不需要* retval 返回pdPASS表示成功发送命令到软件定时器服务命令队列返回值为pdFALL表示发送失败*/BaseType_t xTimerStartFromISR(TimerHandle_t xTimer,BaseType_t *pxHigherPriorityTaskWoken);3.3 xTimerStop()和xTimerStopFromISR()
此函数用于停止定时器使得软件定时器由运行态转换为休眠态下面是函数原型 /*** brief xTimerStop停止软件定时器* param xTimer: 需要停止的软件定时器句柄* param xBlockTime : 阻塞超时时间* retval 返回pdPASS表示成功发送命令到软件定时器服务命令队列返回值为pdFALL表示超时发送失败*/BaseType_t xTimerStop( TimerHandle_t xTimer,TickType_t xBlockTime );
/*** brief xTimerStopFromISR在中断中停止定时器* param xTimer: 需要停止的软件定时器句柄* param pxHigherPriorityTaskWoken: 是否需要进行任务交换如果为pdTRUE表示开启软件定时器之后软件定时器服务任务被唤醒* 且优先级大于当前正在执行的任务需要进行任务切换需要在中断退出之前进行任务切换否则不需要* retval 返回pdPASS表示成功发送命令到软件定时器服务命令队列返回值为pdFALL表示发送失败*/BaseType_t xTimerStopFromISR(TimerHandle_t xTimer,BaseType_t *pxHigherPriorityTaskWoken);3.4 xTimerReset()和xTimerResetFromISR()
此函数用于复位软件定时器将软件定时器的值清零但是对于处于休眠态的软件定时器作用和xTimerStart()相同下面是函数原型
/*** brief xTimerReset复位软件定时器* param xTimer: 需要复位的软件定时器句柄* param xBlockTime : 阻塞超时时间* retval 返回pdPASS表示成功发送命令到软件定时器服务命令队列返回值为pdFALL表示超时发送失败*/BaseType_t xTimerReset( TimerHandle_t xTimer,TickType_t xBlockTime );
/*** brief xTimerResetFromISR在中断中复位定时器* param xTimer: 需要复位的软件定时器句柄* param pxHigherPriorityTaskWoken: 是否需要进行任务交换如果为pdTRUE表示开启软件定时器之后软件定时器服务任务被唤醒* 且优先级大于当前正在执行的任务需要进行任务切换需要在中断退出之前进行任务切换否则不需要* retval 返回pdPASS表示成功发送命令到软件定时器服务命令队列返回值为pdFALL表示发送失败*/BaseType_t xTimerResetFromISR(TimerHandle_t xTimer,BaseType_t *pxHigherPriorityTaskWoken);3.5 xTimerChangePeriod()和xTimerChangePeriodFromISR()
此函数用于改变软件定时器的周期不管定时器处于什么状态都可以改变定时器周期并且更改后会进入运行态下面是函数原型
/*** brief xTimerChangePeriod改变定时器的周期* param xTimer: 需要改变周期的软件定时器句柄* param xNewPeriod: 需要改变的周期单位为滴答周期* param xBlockTime : 阻塞超时时间* retval 返回pdPASS表示成功发送命令到软件定时器服务命令队列返回值为pdFALL表示超时发送失败*/BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,TickType_t xNewPeriod,TickType_t xBlockTime );
/*** brief xTimerChangePeriod改变定时器的周期* param xTimer: 需要改变周期的软件定时器句柄* param xNewPeriod: 需要改变的周期单位为滴答周期* param xBlockTime : 阻塞超时时间* param pxHigherPriorityTaskWoken: 是否需要进行任务交换如果为pdTRUE表示开启软件定时器之后软件定时器服务任务被唤醒* 且优先级大于当前正在执行的任务需要进行任务切换需要在中断退出之前进行任务切换否则不需要* retval 返回pdPASS表示成功发送命令到软件定时器服务命令队列返回值为pdFALL表示超时发送失败*/BaseType_t xTimerChangePeriodFromISR(TimerHandle_t xTimer,TickType_t xNewPeriod,BaseType_t *pxHigherPriorityTaskWoken);
3.6 xTimerDelete()
此函数用于删除软件定时器下面是函数原型
/*** brief xTimerDelete删除软件定时器* param xTimer: 需要改变周期的软件定时器句柄* param xBlockTime : 阻塞超时时间* retval 返回pdPASS表示成功发送命令到软件定时器服务命令队列返回值为pdFALL表示超时发送失败*/BaseType_t xTimerDelete( TimerHandle_t xTimer,TickType_t xBlockTime );
3.7 xTimerIsTimerActive()
此函数用于获取定时器的状态是否处于休眠态或者运行态。下面是函数原型
/*** brief xTimerDelete删除软件定时器* param xTimer: 需要改变周期的软件定时器句柄* retval 返回pdPASS表示成功发送命令到软件定时器服务命令队列返回值为pdFALL表示超时发送失败*/BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer );3.8 pvTimerGetTimerID()
此函数用于获取软件定时器的ID下面是函数原型 /*** brief pvTimerGetTimerID获取软件定时器ID* param xTimer: 需要获取ID的软件定时器句柄* retval 分配给被查询的定时器的 ID*/void *pvTimerGetTimerID( TimerHandle_t xTimer );3.9 vTimerSetReloadMode()
此函数用于修改软件定时器的模式下面是函数原型 /*** brief vTimerSetReloadMode设置软件定时器的模式* param xTimer: 需要修改模式的软件定时器句柄* param uxAutoReload : 传入参数为pdTRUE表示设置周期定时器为odFALSE表示设置为单次定时器。* retval void*/void vTimerSetReloadMode( TimerHandle_t xTimer,const UBaseType_t uxAutoReload );3.10 vTimerSetTimerID()
此函数用来设置定时器的ID值函数原型如下 /*** brief vTimerSetTimerID设置软件定时器的ID* param xTimer: 需要设置ID的软件定时器句柄* param pvNewID : 新的ID* retval void*/void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID );3.11 xTimerPendFunctionCall()和xTimerPendFunctionCallFromISR()
此函数用于请求执行一个另一个函数不过被请求的函数调用不是当下立即完成而是被挂起(延时)到软件定时器服务任务也叫RTOS守护进程任务中执行下面是函数原型
/*** brief xTimerPendFunctionCall请求执行一个函数xFunctionToPend()* param xFunctionToPend: 请求被执行的函数必须为PendedFunction_t 类型* param pvParameter1: 被请求的函数的第一个参数* param ulParameter2: 被请求的函数的第二个参数* param xTicksToWait : 阻塞超时时间* retval 返回值为pdPASS表示请求成功为pdFALSE表示请示失败*/BaseType_t xTimerPendFunctionCall(PendedFunction_t xFunctionToPend,void *pvParameter1,uint32_t ulParameter2,TickType_t xTicksToWait );/*** brief xTimerPendFunctionCallFromISR在中断中请求执行一个函数xFunctionToPend()* param xFunctionToPend: 请求被执行的函数必须为PendedFunction_t 类型* param pvParameter1: 被请求的函数的第一个参数* param ulParameter2: 被请求的函数的第二个参数* param pxHigherPriorityTaskWoken : 是否需要进行任务交换如果为pdTRUE表示开启软件定时器之后软件定时器服务任务被唤醒* 且优先级大于当前正在执行的任务需要进行任务切换需要在中断退出之前进行任务切换否则不需要* retval 返回值为pdPASS表示请求成功为pdFALSE表示请示失败*/BaseType_t xTimerPendFunctionCallFromISR(PendedFunction_t xFunctionToPend,void *pvParameter1,uint32_t ulParameter2,BaseType_t *pxHigherPriorityTaskWoken );下面是请求被执行的函数原型必须满足PendedFunction_t 类型否则无法成功使用 void vPendableFunction( void * pvParameter1, uint32_t ulParameter2 );
【注】请求执行另一个函数成功会往软件定时器服务任务命令队列中写入消息使得软件定时器服务任务处于就绪态且默认配置下软件定时服务任务的优先级为最大所以会立即执行上下文切换。因此xTimerPendFunctionCall()函数在实际上没有很大的意义拙见但是在中断中为了满足快进快出的要求可以使用xTimerPendFunctionCallFromISR()函数将一些比较耗时、非确定性的操作推迟到软件定时器服务任务RTOS守护进程程序中执行以增加中断响应的灵活性。
最后是一个简单的使用示例 /* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 */
#define TASK1_PRIO 1 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /*任务函数*/void myCallbackFunction(void * pvParameters,uint32_t ulParameter2); /* 被请求的函数声明 */void freertos_demo(void)
{taskENTER_CRITICAL(); /* 进入临界区关闭中断此时停止任务调度*/ /* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(const char* )task1,(uint16_t )TASK1_STK_SIZE,(void* )NULL,(UBaseType_t )TASK1_PRIO,(TaskHandle_t* )Task1Task_Handler);taskEXIT_CRITICAL(); /* 退出临界区重新开启中断开启任务调度 */vTaskStartScheduler(); //开启任务调度
}
/* 任务1 */
void task1(void *pvParameters)
{while(1){/* 请求执行myCallbackFunction函数 */xTimerPendFunctionCall(myCallbackFunction, (void *) 123, 0, pdTRUE);/* xTimerPendFunctionCall()执行完之后会发生上下文切换到软件定时器服务任务然后回调myCallbackFunction()函数 */HAL_Delay(1000);}
}
/* 被请求执行的函数 */
void myCallbackFunction(void * pvParameters,uint32_t ulParameter2) {// 可以在这里处理传递的参数uint32_t param (uint32_t) pvParameters;// 例如打印参数printf(Callback executed with parameter: %d\n, param);
}/* 最后的现象就是串口每1打印一次“Callback executed with parameter: 123” */
3.12 pcTimerGetName()
此函数用于获取软件定时器的名字函数原型如下 /*** brief pcTimerGetName获取软件定时器的名字* param xTimer: 需要获取名字的软件定时器句柄* retval 软件定时器的名字*/const char * pcTimerGetName( TimerHandle_t xTimer );3.13 xTimerGetPeriod()
此函数用于获取软件定时的周期函数原型如下 /*** brief xTimerGetPeriod获取软件定时器的周期* param xTimer: 需要获取周期的软件定时器句柄* retval 软件定时器的周期*/TickType_t xTimerGetPeriod( TimerHandle_t xTimer );
3.14 xTimerGetExpiryTime()
此函数用于获取下一次超时的时间函数原型如下 /*** brief xTimerGetExpiryTime获取下一次超时时间* param xTimer: 需要获取下一次超时时间的软件定时器句柄* retval 下一次的超时时间也可以说是滴答数。*/TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer );下面是一个使用示例
static void prvAFunction( TimerHandle_t xTimer )
{
TickType_t xRemainingTime;/* Calculate the time that remains before the timer referenced by xTimerexpires. TickType_t is an unsigned type, so the subtraction will result inthe correct answer even if the timer will not expire until after the tickcount has overflowed. */xRemainingTime xTimerGetExpiryTime( xTimer ) - xTaskGetTickCount();
}
3.15 xTimerGetReloadMode() 和uxTimerGetReloadMode()
此函数用于获取软件定时器的模式下面是函数原型 /*** brief xTimerGetReloadMode获取定时器的模式* param xTimer: 需要获取下一次超时时间的软件定时器句柄* retval 返回值为pdTRUE表示为周期定时器为pdFALSE表示为单次定时器。*/
BaseType_t xTimerGetReloadMode( TimerHandle_t xTimer );
/* uxTimerGetReloadMode()函数和上面一样但属于老版本中存在保证向后兼容使用的新的应用程序中应该避免使用此函数 */
UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ); 以上就是所有FreeRTOS提供的软件定时器相关的API函数最后我们来完成一个简单的操作实验结束今天的学习 4 软件定时器操作实验
4.1. 实验内容
在STM32F103RCT6上运行FreeRTOS通过按键控制完成对应的队列集操作具体要求如下
创建两个软件定时器一个单次定时器、一个周期定时器定义任务1用于LED闪烁执行系统正常运行定义任务2用于按键扫描按键按下控制定时器的启动、停止和复位每完成一个步骤通过串口打印相关信息。
4.2 代码实现
由于本期内容涉及到按键扫描会用到 STM32框架之按键扫描新思路 这里不做过多介绍。我们直接来看代码
freertos_demo.c
#include freertos_demo.h
#include gpio.h
#include queue.h //需要包含队列和任务相关的头文件
#include key.h //包含按键相关头文件
#include timers.h
/*FreeRTOS*********************************************************************************************//******************************************************************************************************/
/*FreeRTOS配置*//* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 */
#define TASK1_PRIO 1 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /*任务函数*//* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 */
#define TASK2_PRIO 2 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /*任务函数*/#define Timer1ID 0x01
#define Timer2ID 0x02TimerHandle_t Timer1Handle;
TimerHandle_t Timer2Handle;void Timer1CallbackFunction( TimerHandle_t xTimer );
void Timer2CallbackFunction( TimerHandle_t xTimer );
/******************************************************************************************************//*** brief FreeRTOS例程入口函数* param 无* retval 无*/
void freertos_demo(void)
{taskENTER_CRITICAL(); /* 进入临界区关闭中断此时停止任务调度*//* 创建单次定时器 */Timer1Handle xTimerCreate( timer1,1000,pdFALSE,(void *)Timer1ID,Timer1CallbackFunction );/* 创建周期定时器 */Timer2Handle xTimerCreate( timer2,1000,pdTRUE,(void *)Timer2ID,Timer2CallbackFunction );/* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(const char* )task1,(uint16_t )TASK1_STK_SIZE,(void* )NULL,(UBaseType_t )TASK1_PRIO,(TaskHandle_t* )Task1Task_Handler);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(const char* )task2,(uint16_t )TASK2_STK_SIZE,(void* )NULL,(UBaseType_t )TASK2_PRIO,(TaskHandle_t* )Task2Task_Handler);taskEXIT_CRITICAL(); /* 退出临界区重新开启中断开启任务调度 */vTaskStartScheduler(); //开启任务调度
}/**
* brief task1:LED0状态翻转指示系统正常运行。* param pvParameters : 传入参数(未用到)* retval 无*/
void task1(void *pvParameters)
{while(1){HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);vTaskDelay(1000);}
}
/**
* brief task2:按键扫描按键0开启定时器按键1停止定时器按键2复位定时器* param pvParameters : 传入参数(未用到)* retval 无*/
void task2(void *pvParameters)
{ while(1){ Key_One_Scan(Key_Name_Key0,Key0_Up_Task,Key0_Down_Task);Key_One_Scan(Key_Name_Key1,Key1_Up_Task,Key1_Down_Task);Key_One_Scan(Key_Name_Key2,Key2_Up_Task,Key2_Down_Task);vTaskDelay(10);}}void Timer1CallbackFunction( TimerHandle_t xTimer )
{static uint16_t Timer1_Count 0;Timer1_Count;printf(软件定时器1任务执行次数: %d\r\n,Timer1_Count);
}void Timer2CallbackFunction( TimerHandle_t xTimer )
{static uint16_t Timer2_Count 0;Timer2_Count;printf(软件定时器2任务执行次数: %d\r\n,Timer2_Count);
}
key.c
/* USER CODE BEGIN 2 */#include key.h
#include freertos_demo.h
#include usart.h
#include event_groups.h //包含事件标志组头文件
#include gpio.h
#include task.h
#include timers.hvoid Key0_Down_Task(void)
{printf(\r\n按键0按下\r\n\r\n);printf(启动定时器\r\n);/* 启动定时器1和定时器2 */xTimerStart(Timer1Handle,portMAX_DELAY);xTimerStart(Timer2Handle,portMAX_DELAY);}
void Key0_Up_Task(void)
{}
void Key1_Down_Task(void)
{printf(\r\n按键1按下\r\n\r\n);printf(停止定时器\r\n);/* 停止定时器1和定时器2 */xTimerStop(Timer1Handle,portMAX_DELAY);xTimerStop(Timer2Handle,portMAX_DELAY);}
void Key1_Up_Task(void)
{}
void Key2_Down_Task(void)
{printf(\r\n按键2按下\r\n\r\n);printf(复位定时器\r\n);/* 定时器1和定时器2 */xTimerReset(Timer1Handle,portMAX_DELAY);xTimerReset(Timer2Handle,portMAX_DELAY);
}
void Key2_Up_Task(void)
{}
void WKUP_Down_Task(void)
{}
void WWKUP_Up_Task(void)
{}void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void))
{static uint8_t Key_Val[Key_Name_Max]; //按键值的存放位置static uint8_t Key_Flag[Key_Name_Max]; //KEY0~2为0时表示按下为1表示松开WKUP反之Key_Val[KeyName] Key_Val[KeyName] 1; //每次扫描完将上一次扫描的结果左移保存switch(KeyName){case Key_Name_Key0: Key_Val[KeyName] Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)); //读取Key0按键值break;case Key_Name_Key1: Key_Val[KeyName] Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)); //读取Key1按键值break;case Key_Name_Key2: Key_Val[KeyName] Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin)); //读取Key2按键值break;
// case Key_Name_WKUP: Key_Val[KeyName] Key_Val[KeyName] | (HAL_GPIO_ReadPin(WKUP_GPIO_Port, WKUP_Pin)); //读取WKUP按键值
// break; default:break;}
// if(KeyName Key_Name_WKUP) //WKUP的电路图与其他按键不同所以需要特殊处理
// {
// //WKUP特殊情况
// //当按键标志为1(松开)是判断是否按下WKUP按下时为0xff
// if(Key_Val[KeyName] 0xff Key_Flag[KeyName] 1)
// {
// (*OnKeyOneDown)();
// Key_Flag[KeyName] 0;
// }
// //当按键标志位为0按下判断按键是否松开,WKUP松开时为0x00
// if(Key_Val[KeyName] 0x00 Key_Flag[KeyName] 0)
// {
// (*OnKeyOneUp)();
// Key_Flag[KeyName] 1;
// }
// }
// else //Key0~2按键逻辑判断
// {//Key0~2常规判断//当按键标志为1(松开)是判断是否按下if(Key_Val[KeyName] 0x00 Key_Flag[KeyName] 1){(*OnKeyOneDown)();Key_Flag[KeyName] 0;}//当按键标志位为0按下判断按键是否松开if(Key_Val[KeyName] 0xff Key_Flag[KeyName] 0){(*OnKeyOneUp)();Key_Flag[KeyName] 1;} }//}
/* USER CODE END 2 */
4.3 运行结果 以上就是本期的所有内容创造不易点个关注再走呗。