FreeRTOS记录(八、用软件定时器?还是硬件定时器?)_freertos的硬件时钟

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

			 TimerCallbackFunction_t pxCallbackFunction ); /\* 定时器回调函数 \*/


void pxCallbackFunction (xTimerHandle pxTimer){…}


在CubeMX中封装后的函数为`osTimerCreate`:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/288916c4b3424908b030f145cf3895e6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


### 2.2 开始定时器(为什么开启定时器还需要等待时间)


* `xTimerStart()` 和`xTimerStartFromISR()`


**xTimerStart():**



BaseType_t xTimerStart( TimerHandle_t xTimer, /* 定时器句柄 */
TickType_t xBlockTime ); /* 成功启动定时器前的最大等待时间设置,单位系统时钟节拍
如果在 FreeRTOS 调度器开启之前调用 xTimerStart(),该形参将不起作用*/


说明:为什么开启定时器还需要等待时间?


前面 运行原理 已经介绍过,FreeRTOS的软件定时器是通过消息队列给定时器任务发消息来实现的,此参数设置的等待时间就是当消息队列已经满的情况下,等待消息队列有空间时的最大等待时间。


定时器任务实际执行消息队列发来的命令依赖于定时器任务的优先级,如果定时器任务是高优先级会及时得到执行,如果是低优先级,就要等待其余高优先级任务释放 CPU 权才可以得到执行。


对于已经被激活的定时器,即调用过函数 `xTimerStart`进行启动,再次调用此函数相当于调用了函数`xTimerReset`对定时器时间进行了复位。


如果在启动 FreeRTOS 调度器前调用了此函数,定时器是不会立即执行的,需要等到启动了 FreeRTOS调度器才会得到执行,即从此刻开始计时,达到`xTimerCreate` 中设置的单次或者周期性延迟时间才 会执行相应的回调函数。


**xTimerStartFromISR():**



/*******************************************************************************************************
*@ 函数功能:在中断中启动一个软件定时器。

*@ 函数参数:xTimer:软件定时器句柄
pxHigherPriorityTaskWoken:定时器守护任务的大部分时间都在阻塞态等待定时器命令队列的命令。
调用函数 xTimerStartFromISR()将会往定时器的命令队列发送一个启动命令,这很有可能会将定时器任务从阻塞除。
如果调用函数xTimerStartFromISR()让定时器任务脱离阻塞态,
且定时器守护任务的优先级大于或者等于当前被中断的任务的优先级,
那么 pxHigherPriorityTaskWoken 的值会在函数xTimerStartFromISR()内部设置为 pdTRUE,
然后在中断退出之前执行一次上下文切换。
pxHigherPriorityTaskWoken:pxHigherPriorityTaskWoken 在使用之前必须初始化成pdFALSE。
调用xEventGroupSetBitsFromISR()会给守护任务发送一个消息,
如果守护任务的优先级高于当前被中断的任务的优先级的话
(一般情况下都需要将守护任务的优先级设置为所有任务中最高优先级),
pxHigherPriorityTaskWoken 会被置为 pdTRUE, 然后在中断退出前执行一次上下文切换。

*@ 返回值:如果启动命令无法成功地发送到定时器命令队列则返回 pdFAILE,成功发送则返回pdPASS。
软件定时器成功发送的命令是否真正的被执行也还要看定时器守护任务的优先级,
其优先级由宏 configTIMER_TASK_PRIORITY 定义。
*******************************************************************************************************/
#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR,( xTaskGetTickCountFromISR() ),( pxHigherPriorityTaskWoken ), 0U )


### 2.3 获取定时器ID


* `pvTimerGetTimerID`



void *pvTimerGetTimerID( const TimerHandle_t xTimer ) /* 定时器句柄 */


### 2.4 停止/删除定时器


* `xTimerStop()`和`xTimerStopFromISR()`


**xTimerStop():**



/*******************************************************************************************************
*@ 函数功能:停止一个软件定时器, 让其进入休眠态。
*@ 函数参数:xTimer:软件定时器句柄
xBlockTime:用户指定超时时间, 单位为系统节拍周期(即 tick)。
如果在 FreeRTOS 调度器开启之前调用 xTimerStart(),形参将不起作用。
*@ 返回值:
如果启动命令在超时时间之前无法成功地发送到定时器命令队列则返回 pdFAILE,成功发送则返回 pdPASS。
软件定时器成功发送的命令是否真正的被执行也还要看定时器守护任务的优先级,
其优先级由宏 configTIMER_TASK_PRIORITY 定义。
*******************************************************************************************************/
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xBlockTime );


**xTimerStopFromISR():**



/*******************************************************************************************************
*@ 函数功能:在中断中停止一个软件定时器, 让其进入休眠态。
*@ 函数参数:xTimer:软件定时器句柄
pxHigherPriorityTaskWoken:定时器守护任务的大部分时间都在阻塞态等待定时器命令队列的命令。
调用函数 xTimerStopFromISR()将会往定时器的命令队列发送一个停止命令,
这很有可能会将定时器任务从阻塞态移除 。
如果调用函数xTimerStopFromISR()让定时器任务脱离阻塞态,
且定时器守护任务的优先级大于或者等于当前被中断的任务的优先级,
那么 pxHigherPriorityTaskWoken 的值会在函数xTimerStopFromISR()内部设置为 pdTRUE,
然后在中断退出之前执行一次上下文切换。
*@ 返回值:
如果停止命令在超时时间之前无法成功地发送到定时器命令队列则返回pdFAILE,成功发送则返回 pdPASS。
软件定时器成功发送的命令是否真正的被执行也还要看定时器守护任务的优先级,
其优先级由宏 configTIMER_TASK_PRIORITY 定义。
*******************************************************************************************************/
BaseType_t xTimerStopFromISR(TimerHandle_t xTimer,BaseType_t *pxHigherPriorityTaskWoken);


* `xTimerDelete()`



/*******************************************************************************************************
*@ 函数功能:删除一个已经被创建成功的软件定时器
*@ 函数参数:xTimer:软件定时器句柄
xBlockTime:用户指定的超时时间, 单位为系统节拍周期(即 tick),
如果在 FreeRTOS调度器开启之前调用 xTimerStart(), 该形参将不起作用。
*@ 返回值:如果删除命令在超时时间之前无法成功地发送到定时器命令队列则返回 pdFAILE, 成功发送则返回 pdPASS。
*******************************************************************************************************/
#define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ),tmrCOMMAND_DELETE,0U, NULL, ( xTicksToWait ) )


## 三、测试Demo


### 3.1 简单测试


在CubeMX中定时器配置:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2bd616bed4d14a64968ab854249e30f4.png)


添加软件定时器:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/79f9c4aaaad14850b2bbe4513499e72a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)


程序设计:


1. 生成的代码中需要定义一下 ID(上图的Parameter是NULL的,后来我改成了no1 和 no2,需要对no1 和 no2定义一下);
2. 不能在调度前使用`xTimerStart()` 函数,所以单独创建了一个任务启动定时器,然后删除;
3. 回调函数中发送任务通知给温湿度读取任务;
4. 温湿度读取任务接收任务通知执行任务;



/*1.定义一下 ID*/
#define no1 1
#define no2 2

/* Create the timer(s) */
/* definition and creation of myTimer01 */
osTimerDef(myTimer01, myTimeCallback);
myTimer01Handle = osTimerCreate(osTimer(myTimer01), osTimerOnce, (void*) no1);

/* definition and creation of myTimer02 */
osTimerDef(myTimer02, myTimeCallback);
myTimer02Handle = osTimerCreate(osTimer(myTimer02), osTimerPeriodic, (void*) no2);

/*2.创建了一个任务启动定时器*/
/* USER CODE END Header_StartTimerTask */
void StartTimerTask(void const * argument)
{
/* USER CODE BEGIN StartTimerTask */
/* Infinite loop */
for(;😉
{
osTimerStart(myTimer01Handle,5000);
osTimerStart(myTimer02Handle,3000);
vTaskDelete(NULL);
osDelay(1);
}
/* USER CODE END StartTimerTask */
}

/*
myTimeCallback function
3.回调函数中发送任务通知给温湿度读取任务
测试用,实际使用不能加printf在回调函数
*/
void myTimeCallback(void const * argument)
{
/* USER CODE BEGIN myTimeCallback */
uint8_t myTimerID;
myTimerID = (uint8_t)pvTimerGetTimerID(argument);

if (myTimerID == no1)
{
osSignalSet(THreadHandle,test_signal1);
printf(“ulTimerID = %d,send a Signal_1 to threadtask!\r\n”,myTimerID);
}
else if(myTimerID == no2){
osSignalSet(THreadHandle,test_signal2);
printf(“ulTimerID = %d,send a Signal_2 to threadtask!\r\n”,myTimerID);
}
/* USER CODE END myTimeCallback */
}

/*4、温湿度读取函数,还是老样子,和第FreeRTOS记录(5、任务通知)中的函数一样*/


#### 3.1.1 再遇溢出问题


本来是个简单的测试,测试结束完结散花,没想到又遇到了溢出问题:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/d7fa5c7f1ebe4440a7ccf59855b94c53.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_16,color_FFFFFF,t_70,g_se,x_16)  
 遇到这个问题那可就不能不管了,为了项目中能够更加合理的分配RAM空间,问题必须深究到底!!!  
 问题一点一点剥开来测试!!!


我们看一下溢出情况下 定时器控制任务的 情况:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2e87ef53aad542698bf1b729f399f9d3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_17,color_FFFFFF,t_70,g_se,x_16)  
 在CubeMX中能看到2个定时器需要的内存大小(目前的设置的configTIMER\_TASK\_STACK\_DEPTH 的大小为 128 字,512Bytes)这样子看的话足够用的啊?:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/a74677f9b0bd40279b8965a2f92c62a6.png)  
 接下来测试,把 configTIMER\_TASK\_STACK\_DEPTH 改成 256 字后,运行起来是正常的:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/3b8e0113ee8741df9a0e2ae7b91cf886.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_17,color_FFFFFF,t_70,g_se,x_16)  
 此时再来看一下任务栈剩余情况(开始剩余2字,多了128字,剩余130,这点OK!):  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2245a9f1377d4e3f982b54a573f7946e.png)


那我始终觉得128字,应该是足够了,我把 configTIMER\_TASK\_STACK\_DEPTH 改回128字,把`myTimeCallback`函数中的 printf 语句去掉,因为正常使用肯定不能带的(快进快出):  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/e6d9264089024bf1a195de54dbd00e47.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)  
 测试是能够正常工作的(128字去掉printf的情况下)看任务栈剩余结果:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/391c80398b234152a8d7eb6d8b6e87e8.png)


还有测试Demo中我们创建了2个定时器,定时器创建完了就占用了内存空间,其中有一个单次的定时器,虽然只运行一次,但是他只是出于休眠状态,随时可以启动运行(虽然每次都是运行一次),如果我们只想他开机运行一次,那么我们可以在回调函数中删除此定时器,那么其所占用的内存就会释放:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2a65dd6696d94de7bfd87b59da5ad017.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)  
 测试结果:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/37e06c9801db45f6a39859510c6223a2.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_16,color_FFFFFF,t_70,g_se,x_16)  
 通过上面我们也可以算出,定时器1占用了 78-26=52 字的空间。


#### 3.1.2 定时器数量问题



**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
![img](https://img-blog.csdnimg.cn/img_convert/fc2c28b008e007f3f8fff2f095951419.png)
![img](https://img-blog.csdnimg.cn/img_convert/da6a6453273a589155c685a58657dd1c.png)

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

mg-6XJ8NqsF-1715874586975)]
[外链图片转存中...(img-AmYmNGYt-1715874586976)]

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 52
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值