参考:
感谢开源项目和其他作者的分享,本文为新手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为例,参数是60000
SysTick_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: // 只支持串口0
break;
default:
break;
}
}
测试效果如下: