本帖最后由 lilijin1995 于 2022-12-13 13:09 编辑
序:MultiTimer这样一个软件定时器拓展模块真的很方便,但是如果如果任务回调函数中执行了太耗时的任务的话,可能会导致其他任务来不及执行,相关的tick没法实现自加,现在就我们使用过程遇到的一些问题与大家分享一下,由于楼主水平有限,文档和视频中难免有出错和讲得不好的地方,欢迎各位读者和观众善意地提出意见和建议,谢谢!
关于MultiTimer
MultiTimer 是一个软件定时器扩展模块,作者是0x1abin,可无限扩展你所需的定时器任务,取代传统的标志位判断方式, 更优雅更便捷地管理程序的时间触发时序。
功能限制,这里给出MultiTimer GitHub上的开源链接,:
https://github.com/0x1abin/MultiTimer
1.定时器的时钟频率直接影响定时器的精确度,尽可能采用1ms/5ms/10ms这几个精度较高的tick; 2.定时器的回调函数内不应执行耗时操作,否则可能因占用过长的时间,导致其他定时器无法正常超时; 3.由于定时器的回调函数是在 MultiTimerYield 内执行的,需要注意栈空间的使用不能过大,否则可能会导致栈溢出。
诚如以上功能限制中的描述,第一个点要求我们是满足了,在CH32V003中移植了,使用的是Systick配置的1ms作为时钟基准。
由于项目保密性的原因,部分代码删减,但不会影响大家理解的。
首先是配置Systick 1Ms进入中断,产生1ms的tick。
void SysTick_Handler(void) {
uwTick += 1;
SysTick->SR = 0;
}
uint64_t PlatformTicksGetFunc(void) {
return uwTick;
}
/*********************************************************************
* @fn main
*
* [url=home.php?mod=space&uid=247401]@brief[/url] Main program.
*
* [url=home.php?mod=space&uid=266161]@return[/url] none
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("Demo:TM1650 Test!!!\r\n");
printf("SystemClk:%d\r\n",SystemCoreClock);
//systick
NVIC_EnableIRQ(SysTicK_IRQn);
SysTick->SR &= ~(1 << 0);
SysTick->CMP = (SystemCoreClock/1000)-1;//1ms
SysTick->CNT = 0;
SysTick->CTLR = 0xF;
PollSystemInit();
while(1)
{
MultiTimerYield();
}
}
我们创建了一个定时任务,用于执行按键数码管驱动的扫描,这是一个专门的驱动IC方案,使用软件I2C方案,从数码管数据的更新,再加上按键的扫描,其中有多个软件us延迟:
/*******************读按键命令************************/
u8 TM1650_read(void) {
u8 key;
IIC_Start();
IIC_Send_Byte(0x49); //读按键指令
IIC_Wait_Ack();
key = IIC_Read_Byte(0);
IIC_Wait_Ack();
IIC_Stop();
return key;
}
/***显示1-99数据***/
void TM1650Disp2Num(unsigned char snum) {
Dig1_Data = Digital_Table[snum / 10 % 10];
Dig2_Data = Digital_Table[snum % 10];
DigitalScan();
}
这样的话就违反了第二个限制,不能执行太耗时的操作。因为我们在第一个定时器任务里面执行了启动第二个定时器。如下:void TM1650_Timer1Callback(MultiTimer* timer, void *userData) {
switch (TM1650ReadKey(0)) {
case ONOFF_KEY: //KI6+DIG1 21
{
PowerSta = !PowerSta;
if (PowerSta == PowerON) {
WorkingTime=15;
TM1650Disp2Num(WorkingTime);
MultiTimerStart(&timer2, 1000, Remaining_WorkingPeriodTimer2Callback, NULL);
break;
} else {
Display_0F();
}
}
break;
default:
break;
}
MultiTimerStart(timer, 50, TM1650_Timer1Callback, userData);
}
在定时器1任务中,按下按键ON开启定时器2任务:
void Remaining_WorkingPeriodTimer2Callback(MultiTimer* timer, void *userData) {
if (PowerSta == PowerON)
{
if(++RunTick1S==60)
{
RunTick1S=0;
if(++RunTick1m==15)
{
TM1650Disp2Num(0);
}else{
TM1650Disp2Num(WorkingTime-RunTick1m);
}
}
printf("RunTick1S=%d,RunTick1m=%d\r\n",RunTick1S,RunTick1m);
MultiTimerStart(timer, 60000, Remaining_WorkingPeriodTimer1Callback, userData);
}
}
在定时2中通过RunTick1S和RunTick1m计算1分钟,这是一个分钟计时器,然后我又想,Systick中断是不会被打断的,我可以1分钟执行一次定时器2的任务,如下:
void Remaining_WorkingPeriodTimer2Callback(MultiTimer* timer, void *userData) {
if (PowerSta == PowerON)
{
if(++RunTick1m==15)
{
TM1650Disp2Num(0);
}else{
TM1650Disp2Num(WorkingTime-RunTick1m);
}
printf("RunTick1S=%d,RunTick1m=%d\r\n",RunTick1S,RunTick1m);
MultiTimerStart(timer, 60000, Remaining_WorkingPeriodTimer2Callback, userData);
}
}
现在是1min中进入一次定时器2回调,然后我们实测,2min,离晒大谱了。
最后猜测Systick的中断都不准,后来发现,我们用了HSE,而我们并没有外部晶振,这一切都说得通了
后来配置为HSI,完美,相差无几
总结
这是完全是粗心惹的祸,作为一名工程师,这种错误还是少犯才行,为避免这种错误,我觉得还是需要给自己安排一个软件设计流程,按流程来走,不然以后又出现这种笑掉诸位大牙的事件。
|