参考:
MultiTimer
MultiTimer v2 重构版本 | 一款可无限扩展的软件定时器
感谢开源项目和其他作者的分享,本文为新手CH582F裸机移植MultiTimer(一个软件定时器扩展模块)的过程记录。
0. MultiTimer
MultiTimer 是一个软件定时器扩展模块,可无限扩展你所需的定时器任务,取代传统的标志位判断方式, 更优雅更便捷地管理程序的时间触发时序。
1. 新建工程
添加MultiTimer
源码到新工程中,并添加头文件路径。
2. 使用MultiTimer
- 配置系统时间基准接口,安装定时器驱动;
uint64_t PlatformTicksGetFunc(void)
{/* Platform implementation */
}MultiTimerInstall(PlatformTicksGetFunc);
这里把SysTick设置为了1ms中断一次,将变量tick_1ms_cnt
++,实现了MultiTimer的时间基准接口:
// tick_1ms_cnt在SysTick_Handler()中1ms +1
volatile uint64_t tick_1ms_cnt = 0;/*** @description: 配置MultiTimer时间基准接口,以SysTick(1ms)为基础* @return {uint64_t}*/
uint64_t PlatformTicksGetFunc(void)
{/* Platform implementation */return tick_1ms_cnt;
}// SysTick中断函数
__INTERRUPT
__HIGH_CODE
void SysTick_Handler()
{SysTick->SR = 0; // 清除中断标志tick_1ms_cnt++;
}
注意!
-
定时器的时钟频率直接影响定时器的精确度,尽可能采用1ms/5ms/10ms这几个精度较高的tick;
-
定时器的回调函数内不应执行耗时操作,否则可能因占用过长的时间,导致其他定时器无法正常超时;
-
由于定时器的回调函数是在 MultiTimerYield 内执行的,需要注意栈空间的使用不能过大,否则可能会导致栈溢出。
mian.c
:
这里我创建了两个Timer对象,Timer1
每1s循环触发一次,Timer2
在初始化后的第3s时执行一次,并打印出字符串。
定时器循环触发只需要在对应的超时回调函数中,重启自己就可以了。
/** @Author : stark1898y gg1658608470@gmail.com* @Date : 2022-11-26 15:04:43* @LastEditors : stark1898y gg1658608470@gmail.com* @LastEditTime : 2022-11-26 17:15:53* @FilePath : \CH582F_MultiTimer\src\Main.c* @Description : CH582F_MultiTimer基于SysTick** Copyright (c) 2022 by stark1898y gg1658608470@gmail.com, All Rights Reserved.*/#include "CH58x_common.h"
#include "MultiTimer.h"// 设定嘀嗒时间 1 ms
#define SYSTICK_INTERVAL (1)uint8_t TxBuff[] = "This is a tx exam\r\n";
uint8_t RxBuff[100];
uint8_t trigB;// tick_1ms_cnt在SysTick_Handler()中1ms +1
volatile uint64_t tick_1ms_cnt = 0;uint8_t timer1_flag = 0;// 实例化MultiTimer定时器对象
MultiTimer timer1;
MultiTimer timer2;/*** @description: 配置MultiTimer时间基准接口,以SysTick(1ms)为基础* @return {uint64_t}*/
uint64_t PlatformTicksGetFunc(void)
{/* Platform implementation */return tick_1ms_cnt;
}/*** @description: MultiTimer1的回调函数,1000ms循环触发* @param {MultiTimer*} timer* @param {void*} user_data* @return {void}*/
void Timer1_Callback(MultiTimer* timer, void* user_data)
{timer1_flag = 1;
// PRINT("MultiTimer1_Callback\r\n");// 定时器的回调函数内不应执行耗时操作,否则可能因占用过长的时间,导致其他定时器无法正常超时;// 重启该定时器,实现1000ms周期的循环触发MultiTimerStart(&timer1, 1000, Timer1_Callback, NULL);
}/*** @description: Timer2的回调函数,打印出来user_data的字符串* @param {MultiTimer*} timer* @param {void*} user_data char** @return {*}*/
void Timer2_Callback(MultiTimer* timer, void* user_data)
{PRINT("MultiTimer2_Callback! user_data: %s\r\n", (char*) user_data);
}/********************************************************************** @fn main** @brief 主函数** @return none*/
int main()
{SetSysClock(CLK_SOURCE_PLL_60MHz);// 自动重新加载计数值,计数时钟60M,以1ms为例,参数是60000SysTick_Config( GetSysClock() / 1000 * SYSTICK_INTERVAL); //设定嘀嗒时间1ms// 安装定时器驱动MultiTimerInstall(PlatformTicksGetFunc);/* 配置串口1:先配置IO口模式,再配置串口 */GPIOA_SetBits(GPIO_Pin_9);GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉输入GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平UART1_DefInit();// 中断方式:接收数据后发送出去UART1_ByteTrigCfg(UART_7BYTE_TRIG);trigB = 7;UART1_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT);PFIC_EnableIRQ(UART1_IRQn);PRINT("GetSysClock: %d\n", GetSysClock());PRINT("PlatformTicksGetFunc: %d\n", PlatformTicksGetFunc());// 设置定时时间1000ms,超时回调处理函数, 用户上下指针,启动定时器;MultiTimerStart(&timer1, 1000, Timer1_Callback, NULL);MultiTimerStart(&timer2, 5000, Timer2_Callback, "Timer2_Callback 5000ms once!");while(1){if(timer1_flag){timer1_flag = 0;PRINT("MultiTimer1_Callback (1000ms)\r\n");}// 在主循环中调用Timer对象处理函数,处理函数会判断链表上的每个定时器是否超时,如果超过,则拉起注册的回调函数MultiTimerYield();}
}// SysTick中断函数
__INTERRUPT
__HIGH_CODE
void SysTick_Handler()
{SysTick->SR = 0; // 清除中断标志tick_1ms_cnt++;
}/********************************************************************** @fn UART1_IRQHandler** @brief UART1中断函数** @return none*/
__INTERRUPT
__HIGH_CODE
void UART1_IRQHandler(void)
{volatile uint8_t i;switch(UART1_GetITFlag()){case UART_II_LINE_STAT: // 线路状态错误{
// UART1_GetLinSTA();break;}case UART_II_RECV_RDY: // 数据达到设置触发点for(i = 0; i != trigB; i++){RxBuff[i] = UART1_RecvByte();UART1_SendByte(RxBuff[i]);}break;case UART_II_RECV_TOUT: // 接收超时,暂时一帧数据接收完成i = UART1_RecvString(RxBuff);UART1_SendString(RxBuff, i);break;case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送break;case UART_II_MODEM_CHG: // 只支持串口0break;default:break;}
}
测试效果如下: