自认为写过很多MCU程序,但总是回头想想,我所了解的MCU编程思想大体有两种,其中具体的想法我得再找时间写下来。
总想总结出一个可扩展的,易移植的写法,但能力还没到这个层次。但《30天自制操作系统》这本书确实给我了一个思路,就像我已经写过的两篇读书笔记。
将两个独立的内容--FIFO和内存动态管理做到高度模块化,尤其是其中数据结构模型的设计更是我学习的好例子。
今天要学习的设计内容是多定时器处理。原书对这部分的处理讲的很详细,由浅入深,看得我由衷佩服作者,也可能是因为我水平低,稍稍看出点门道来就高兴得不得了。
呵呵,进入正题。
1、数据结构的抽象
#define MAX_TIMER 500 #define TIMER_FLAG_NOUSE 0 //没使用 #define TIMER_FLAG_ALLOC 1 //已配置状态 #define TIMER_FLAG_USING 2 //已使用状态 struct TIMER { unsigned int timeout,flag; //设定的定时时间,定时器的状态 struct FIFO8 *fifo; //定时器关联的fifo unsigned char data; //当定时时间到时,向fifo中写入的数值 } struct TIMERCTL { unsigned int count; //定时器计数基数 struct TIMER timer[MAX_TIMER]; //设定系统最多使用的定时器个数 } TIMERCTL timerctl; //定义一个定时器控制变量
2、定时器控制管理变量的初始化
void TIMERCTL_Initial(void) { char i=0; timer.count = 0; //基数清零 for(i=0;i<MAX_TIMER;i++) { timerctl.timer[i].flag = TIMER_FLAG_NOUSE; /*标记为未使用*/ } }
3、申请一个定时器
struct TIMER* timer_alloc(void) //分配一个可以使用的定时器 { int i=0; for(i=0;i<MAX_TIMER;i++) { if(timerctl.timer[i].flag == TIMER_FLAG_NOUSE) { timerctl.timer[i].flag = TIMER_FLAG_ALLOC; //标记为已分配 return &timer[i]; } } return NULL; //没有可用的了 }
4、释放一个定时器
void timer_free(struct TIMER* timer) //释放一个定时器 { timer.flag = TIMER_FLAG_NOUSE; return ; }
5、要使用一个定时器时,先初始化定时器
//初始化一个定时器,timer应该先去调用timer_alloc申请,fifo也应该先创立,data表示当定时时间到了之后 //向关联fifo中写入的数据 void timer_init(struct TIMER* timer,struct FIFO8* fifo,unsigned char data) { timer.fifo = fifo; timer.data = data; return ; }
6、设定定时器的定时时间,同时也启动了定时器
//设定timer定时器的定时时间为timeout void timer_settime(struct TIMER* timer,unsigned int timeout) { timer.timeout = timeout; timer.flag = TIMER_FLAG_USING; //这条语句相当于启动了定时器 return ; }
7、定时器中断处理函数
//定时器中断函数 void Timer_Interrupt(void) { int i=0; timerctl.count ++; for(i=0;i<MAX_TIMER;i++) //扫描所有的定时器 { if(timerctl.timer[i].flag == TIMER_FLAG_USING) { timerctl.count--; if(timerctl.count == 0) //减得时间到了 { fifo8_put(timerctl.timer[i].fifo,timerctl.timer[i].data); //向关联fifo中写入数据 timerctl.timer[i].flag = TIMER_FLAG_ALLOC; //改变标志位 } } } return ; }
如何使用以上代码:
1、初始化一个fifo,包括以下内容
struct FIFO8 timer0fifo;
char timer0buf[8];
fifo8_init(&timer0fifo,8,timer0buf); //这句话就是初始化一个fifo,8是缓存大小,timer0buf是实际缓存地址。
2、定义一个timer,先申请再初始化它
struct TIMER* timer0;
timer0 = timer_alloc(); //申请一个定时器对象
timer_init(timer0,timer0fifo,1); //这里也就是将timer0fifo与timer0关联,1是定时器到达时,向关联fifo中写入的值
3、设定定时时间,启动定时器
timer_settime(timer0,1000); //这里设定的定时时间t=1000*T T为定时器硬件设定的时间间隔,比如1ms,那么这里就设定了一个1s的定时器
4、通过在主程序中查询定时器关联fifo的状态,判断定时时间是否到了,并处理相关事情
while(1) { ...(其他省略) if(fifo8_status(&timer0fifo) != 0) //定时时间到了,定时器向关联fifo中写入了数据 { i = fifo8_get(&timer0fifo); if(i == 1) { //do_something } } }
这样可以完成类似LED灯闪烁的任务了。当然这个定时器定时时间到了之后,还可以再初始化这个定时器,比如改变定时到时向fifo中写入的数值,然后再启动定时器,这样
判断fifo8_get的值就有意义了。这样可以完成文本输入中光标闪烁的任务(定时亮,定时灭,定时时间一样,处理不一样)。
今天的内容就到此为止了,实际原作者比这写得更好,明天继续深入。