前言说明
在我所接触到的很多MCU源代码(不跑RTOS),无论是我们的同事还是其它方案的源代码,为了实现某些不需要太精确固定周期的工作,常见的做法都会是启动一个定时器,按照比较小的间隔来产生中断,如 0.1 毫秒一次,在定时器中断函数里面进行计数,根据计数值来给多个全局变量置1,在需要的源文件里面导出这些全局变量。如要求每 500ms 上报一次 ADC 检测的值,通过判断 500ms 的全局变量是否被置1,如果置位的话就将 ADC 值通过串口发出去,并且将这个全局变量设置为0,实现间隔 500ms 上报一次。
其实这个思想是很不错的,但是经常看到这些全局变量满天飞,真心有些受不了,即使有些源码将所有需要判断这些全局变量的地方集中在一个源文件,N多的 IF 语句,也会让人看的眼花缭乱,特别是与上下文的变量名相似时,特别容易出错,实在是不好维护。碰到这种源码,在项目时间容许的情况下,我大概率会选择重写,不然真的是太痛苦了。(通常这种全局变量满天飞的源代码,其它的功能实现的代码都好不到哪去)
因为这种方法很常用,也很好用,所以我将其作为基础接口进行封装,实测效果不错,核心思想并未改变。
封装源码
time_base.h
/**
******************************************************************************
* @文件 time_base.h
* @版本 V1.0.0
* @日期
* @概要 计时功能接口
* @作者 lmx
* @邮箱 lovemengx@qq.com
******************************************************************************
* @注意
*
******************************************************************************
*/
#ifndef TIME_BASE_H
#define TIME_BASE_H
// 可选的时间点
typedef enum
{
TIME_BASE_FLAGS_01MS = 1 << 0,
TIME_BASE_FLAGS_1MS = 1 << 1,
TIME_BASE_FLAGS_10MS = 1 << 2,
TIME_BASE_FLAGS_100MS = 1 << 3,
TIME_BASE_FLAGS_500MS = 1 << 4,
TIME_BASE_FLAGS_1000MS = 1 << 5,
TIME_BASE_FLAGS_2000MS = 1 << 6,
TIME_BASE_FLAGS_3000MS = 1 << 7,
TIME_BASE_FLAGS_ALL = 0xFF,
}timer_base_flags_e;
// 初始化和释放
void time_base_init();
void time_base_release();
// 主循环里面每完成一个循环都需要调用一次
void time_base_update_flags();
// 各个功能模块需要在某个时间点执行的时候调用, 传入对应的标志即可判断时间点是否达到
unsigned char time_base_is_flags(timer_base_flags_e flags);
#endif
time_base.c
/**
******************************************************************************
* @文件 time_base.h
* @版本 V1.0.0
* @日期
* @概要 计时功能接口
* @作者 lmx
* @邮箱 lovemengx@qq.com
******************************************************************************
* @注意
*
******************************************************************************
*/
#include "gd32e23x.h"
#include "configuration.h"
#include "time_base.h"
// 定义定时器基准结构
typedef struct
{
unsigned int bitmap; // 使用位来记录各个时间点是否到位
unsigned int time[8]; // 每一个数据位置位所需要的计数值
}timer_base_flags_t;
// 这里的值与 timer_base_flags_e 一一对应, 并且与定时器中断时间相关
// 定时器每 0.1ms 会产生一次中断, 这里的值必须是从小到大
static volatile timer_base_flags_t timer_base_flags = {
.bitmap = 0x00,
.time[0] = 1, // 0.1ms
.time[1] = 10, // 1ms
.time[2] = 100, // 10ms
.time[3] = 1000, // 100ms
.time[4] = 5000, // 500ms
.time[5] = 10000, // 1000ms
.time[6] = 20000, // 2000ms
.time[7] = 30000, // 3000ms
};
#define TIMER_BASE_TIME_ARRAR_MAX (sizeof(timer_base_flags.time) / sizeof(timer_base_flags.time[0]))
// 用于被外部接口判断指定时间点是否到达
static volatile unsigned int timer_bitmap = 0x00;
// 定时器中断服务程序
void CFG_TIME_BASE_IRQ_FUNCTION(void)
{
static unsigned int time_01ms_count = 0;
static unsigned int i = 0;
if(RESET != timer_interrupt_flag_get (CFG_TIME_BASE_TIMER, TIMER_INT_FLAG_UP))
{
timer_interrupt_flag_clear(CFG_TIME_BASE_TIMER, TIMER_INT_UP);
// 计数器计数, 当达到最大值时会自动复位为 1
time_01ms_count = time_01ms_count % timer_base_flags.time[TIMER_BASE_TIME_ARRAR_MAX - 1] + 1;
timer_base_flags.bitmap |= 1 << 0;
// 依次各个时间点是否到达
for(i = 1; i < TIMER_BASE_TIME_ARRAR_MAX; i++){
if(0 == time_01ms_count % timer_base_flags.time[i]){
timer_base_flags.bitmap |= 1 << i;
}
}
}
}
// 更新标志位
void time_base_update_flags()
{
static unsigned int i = 0;
// 拷贝当前时间标志位, 便于其他模块判断指定的时间点是否抵达
timer_interrupt_disable(CFG_TIME_BASE_TIMER, TIMER_INT_UP);
timer_bitmap = timer_base_flags.bitmap;
timer_base_flags.bitmap &= ~timer_base_flags.bitmap;
timer_interrupt_enable(CFG_TIME_BASE_TIMER, TIMER_INT_UP);
return ;
}
// 判断标志位
unsigned char time_base_is_flags(timer_base_flags_e flags)
{
return timer_bitmap & flags ? 1 : 0;
}
// 初始化
void time_base_init()
{
timer_parameter_struct para;
rcu_periph_clock_enable(CFG_TIME_BASE_RCU_CLOCK);
timer_deinit(CFG_TIME_BASE_TIMER);
para.prescaler = 720 - 1; // 72M720分频100khz
para.alignedmode = TIMER_COUNTER_EDGE;
para.counterdirection = TIMER_COUNTER_UP; //向上计数
para.period = 10 - 1; // 0.1 ms
para.clockdivision = 0; //不分频
para.repetitioncounter = 0; //计数重复值0
timer_init(CFG_TIME_BASE_TIMER, ¶);
nvic_irq_enable(CFG_TIME_BASE_IRQ_NUMBER, 0U);
timer_interrupt_enable(CFG_TIME_BASE_TIMER, TIMER_INT_UP);
timer_auto_reload_shadow_enable(CFG_TIME_BASE_TIMER);
timer_enable(CFG_TIME_BASE_TIMER);
return ;
}
// 释放
void time_base_release()
{
timer_interrupt_disable(CFG_TIME_BASE_TIMER, TIMER_INT_UP);
nvic_irq_disable(CFG_TIME_BASE_IRQ_NUMBER);
timer_disable(CFG_TIME_BASE_TIMER);
rcu_periph_clock_disable(CFG_TIME_BASE_RCU_CLOCK);
return ;
}
configuration.h
/**
******************************************************************************
* @文件 configuration.h
* @版本 V1.0.0
* @日期
* @概要 配置信息文件
* @作者 lmx
* @邮箱 lovemengx@qq.com
******************************************************************************
* @注意
*
******************************************************************************
*/
#ifndef CONFIGURATION_H
#define CONFIGURATION_H
#include "gd32e23x.h"
#include "gd32e230c_eval.h"
#include "systick.h"
// 时基定时器
#define CFG_TIME_BASE_TIMER TIMER2
#define CFG_TIME_BASE_RCU_CLOCK RCU_TIMER2
#define CFG_TIME_BASE_IRQ_NUMBER TIMER2_IRQn
#define CFG_TIME_BASE_IRQ_FUNCTION TIMER2_IRQHandler
#endif
使用方法
/**
******************************************************************************
* @文件 main.c
* @版本 V1.0.0
* @日期
* @概要 主程序文件
* @作者 lmx
******************************************************************************
* @注意
*
*
******************************************************************************
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "time_base.h"
#include "xxx.h"
#include "ccc.h"
#include "aaa.h"
int main(void)
{
time_base_init();
// 主循环
while(1)
{
time_base_update_flags(); // 更新时间标志
xxx_process();
ccc_process();
aaa_process();
}
time_base_release();
return 0;
}
// xxx.c 里面的实现
void xxx_process()
{
if(time_base_is_flags(TIME_BASE_FLAGS_100MS) == 0){
return ;
}
// 100 ms 做的事情
}
// ccc.c 里面的实现
void ccc_process()
{
if(time_base_is_flags(TIME_BASE_FLAGS_3000MS) == 0){
return ;
}
// 3000 ms 做的事情
}
// aaa.c 里面的实现
void aaa_process()
{
if(time_base_is_flags(TIME_BASE_FLAGS_500MS) == 0){
return ;
}
// 500 ms 做的事情
}