1 引 言
这里的倒计时就是计算出从当前时间点需要经过多长时间才能到达目标时间点。从另一个角度讲, 就是计算出两个时间点之间的时间差。
目前, 倒计时系统正得到越来越广泛的应用。
在体育比赛、公交系统乃至铁路系统中出现了很多倒计时的时间显示。就在前不久, 关于上海世博会的倒计时正式启动, 其精度精确到天。
在类似的应用中, 大多数情况下, 倒计时功能(包括显示功能)是由微控制器实现的。微控制器不同于桌面电脑或笔记本电脑, 其系统资源非常有限, 也不能安装复杂的操作系统, 没有现成的倒计时系统可以应用。下面将讨论适合在微控制器中运行的倒计时算法。
2 倒计时算法的两种常用思路
关于倒计时的计算主要有两种思路: 一是针对时间段倒计时, 二是针对目标时间倒计时。
●针对时间段倒计时
时间段的含义就是两个时间点的时间差。对时间段倒计时就是先获取两个时间点之间的时间差,然后随着运行时间的增加对该时间差按运行时间递减, 直至递减为0, 表示倒计时结束。
例如, 微处理器对100天倒计时或者对100秒倒计时, 都属于对时间段倒计时, 时间段分别是100天和100秒。当然, 在实际计算时, 最好先把天换算为秒, 再按运行时间递减。
●针对目标时间倒计时
该种思路与第一种思路最大的不同就是它获取的不是时间差, 而是目标时间, 微控制器须自行计算出当前时间与目标时间的时间差。在这种情况下,随着当前时间的改变, 微控制器必须反复计算与目标时间的时间差, 直至差值为0。
例如, 当前时间是2009年5月10 日11点27分0秒, 微控制器获取到目标时间是2009年5月12日11点27分0秒, 则计算出当前的时间差为2天。
然后, 当前时间一旦改变, 微控制器就必须重新计算时间差, 直到当前时间到达或超过2009年5月12日11点27分0秒。
●两种思路的比较
( 1)获取的时间参数不同
如前所述, 第一种思路获取的是时间差, 第二种思路获取的是目标时间点。
( 2)采取的算法不同
第一种思路的主要算法是按运行时间对获取的时间差进行递减运算, 这实际是时间计时的逆运算。
该算法牵涉到减法运算与天、小时、分钟、秒的时间规则运算。
第二种思路的主要算法就是计算出两个时间点之间的时间差。该算法不仅牵涉到时间规则运算,还牵涉到闰年的概念与多种算术运算, 比第一种思路的算法复杂许多。此外, 由于该算法需要微控制器能够随时获取当前的准确时间, 因此要求微处理器必须具备实时时钟功能。
( 3)应用范围与灵活性不同
第一种思路的实现依靠对运行时间的准确把握。如果微控制器断电或由于其它原因产生复位导致运行中断, 则从此刻起到下一次稳定运行时所经过的时间无法掌控, 进而导致倒计时运算无法继续运行。因此, 该思路只适用于极短时间段的倒计时计算, 其可靠性与灵活性欠佳。
第二种思路的实现需要两点: 一是断电保护的实时时钟功能, 这是为了微控制器能够随时读取准确时间; 二是非易失数据的存储功能, 这是为了微控制器可以长时间保存目标时间。满足了这两点要求, 微控制器就能够可靠地实现倒计时计算, 即使突然断电或复位也不会受到影响。对于第一点要求,不管使用外置时钟还是内置时钟, 只要配置电池就可以实现。对于第二点要求, 当前的主流微控制器大都配置FLASH存储功能, 也可以轻松满足。
可见, 针对目标时间的倒计时算法在可靠性与灵活性上极具优势, 对微控制器的要求也不苛刻。
下面就阐述该算法的实现环节。
3 针对目标时间点的倒计时算法实现
如前所述, 该算法主要是计算当前时间点与目标时间点的时间差。具体思路就是先选择一个参考时间点, 然后分别计算出这两个时间点与参考时间点之间的时间差, 再把这两个时间差相减就得到这两个时间点之间的时间差。
下面分三部分描述该算法: 时间格式的建立; 计算时间点到参考时间点的时间差; 时间差相减算法。
( 1)时间格式的建立
有两种时间格式, 一是时间点的格式; 一是时间差的格式。时间点的格式按年月日时分秒排列, 其中年份为16位无符号整数, 其余为8 位无符号整数。时间差格式按天时分秒排列, 天数为16位无符号整数, 其余为8位无符号整数。
( 2)到参考时间点的时间差算法
该算法有两个重点, 一是参考时间点的选取, 二是根据闰年规则对时间差中的天数进行补偿。
关于参考时间点的选取, 应符合两个原则: 一是方便闰年的计算, 二是方便时间差的计算。在这里,选取2001年1月1日0时0分0秒为参考时间点。
图1是时间点到该参考时间点算法的示例代码,pT mi e是指向时间点的数据结构指针, pResult是指向时间差的数据结构指针。下面对该段代码逐条说明。
图1 天数时间差参考代码
图1- 1定义了一个数组, 它的12个元素对应1月份到12月份所累积的天数。请注意两点, 一是该天数不包括本月份的天数, 二是二月份的天数按28天计。
图1- 2是计算时间点与参考时间点的年份、月份与日期的差值。
图1- 3 是初步计算天数差。在这里应用到了图1- 1定义的数组与图1- 2的计算结果。首先,按照每年365天来计算天数, 再按照闰年个数补偿天数, 最后按月份日期的差值计算本年度过的天数。
代码中的( Y earId /4) 就是初步的闰年补偿计算, 补偿规则就是把年份差被4整除的值视为经过的闰年个数, 也就是要补偿的天数。
图1- 4是根据世纪年(也就是能被100整除的年份)的闰年判断规则对图1- 3的计算结果进行校正。这是因为图1- 3 进行的闰年补偿计算所依据的是非世纪年的闰年判断规则, 这一规则在判断世纪年是否为闰年时可能会产生误差。在这里, 对该步骤算法采用了条件编译, 这是考虑到该计算牵涉到真正的多字节除法, 比较耗时, 设计者可以根据实际需要决定是否运行该计算。
图1- 5是判断时间点的年份是否闰年, 进而进行最后的天数调整。请注意, 在图1- 3与图1- 4的计算中, 只计算了度过的年份中包含了多少个闰年, 这其中不包含时间点本身的年份。在本计算中,先调用函数判断时间点年份是否闰年, 再根据时间点的月份是否超过2月决定是否对天数进行补偿。
图1 - 6是记录时间差结果。因为参考时间点的时分秒选择的是0时0分0秒, 所以时间差的时分秒也就是时间点的时分秒。
图2是闰年判断函数的参考代码。该函数提供了两种判断计算, 一种是关于闰年规则的完整判断,即当年份不能被100整除时, 能被4整除的是闰年;当年份能被100整除时, 必须能被400 整除才是闰年。另一种是简易判断, 即把能被4整除的年份视为闰年(当然, 该判断只在年份不能被100整除时才正确)。这两种计算的复杂程度与应用条件不同, 设计者应根据实际需要自行选择。
图2 闰年检测函数参考代码
请注意, 如果参考时间点选择的不是2001年1月1日0时0分0秒, 则上述算法需要进行适当调整。
( 3)时间差相减算法时间差相减算法的主要处理方法是按时间规则进行借位相减。
图3 是完整的时间差借位相减的参考代码, 其前提是时间差中的天数差不为0。代码中pT im e0是指向目标时间点与参考时间点的时间差数据结构的指针, pT ime1是指向当前时间点与参考时间点的时间差数据结构的指针。
下面对图3的代码逐条分析。
图3- 1 就是按时间规则进行时间借位。其实质是小时单位向天数单位借1天, 增加24小时; 分钟单位向小时单位借1小时, 增加60分钟; 秒单位向分钟单位借1分钟, 增加60秒。
图3- 2是时间差相减。注意, 因为天数差被借走一天, 所以要减1。
图3- 3 是根据计算结果进行进位补偿。因为经过借位, 相减的结果有可能超过时间单位的上限,此时就要按时间规则进位。
请注意, 图3代码是以天数为最高时间单位的借位计算, 可以根据实际需要把最大的时间单位设为小时或分钟。
在实际的时间差相减计算中, 为了避免负值的出现, 应先从天数开始对两个时间差的时间单位比较数值大小, 其结果按三种情况处理。
( 1)若当前时间点的时间单位超过目标时间点的时间单位, 表明倒计时结束。
( 2)若两个时间单位相同, 当时间单位为秒时,表明倒计时结束, 否则进入次一级时间单位的判断。
( 3)若当前时间点的时间单位小于目标时间点的时间单位, 则视此时间单位为最高时间单位, 进行借位相减计算。
由此, 即可计算出两个时间点的精确时间差。
图3 时间差借位相减参考代码。
4 结束语
在上面的介绍中, 先讨论了倒计时的两种算法,再择优对其中一种算法进行了详细的阐述。该算法已在实际项目中获得应用, 其计时准确, 工作稳定。