当我们在使用单片机时,为了方便程序编写中任务的调用,一般会使用一个定时器去产生一个时钟心跳,单片机上电之后运行的程序就会以这个定时为时间基准去产生一系列的时间片,然后使用时间片的思想去编写程序。例如,上电产生一个定时时间基准为1ms的定时时间基准,例如调用按键状态机时需要每5ms的去调用一次,那么便可以使用获取当前时间心跳-上一次记录的时间心跳去产生5ms基准。
uint8_t recode_tick = Get_Systick();//记录当前时间tick值
void Detect_tick_arrive(void)//检测时间片是否到达
{
uint8_t current_tick;
current_tick = Get_Systick();//获取当前时间tick值,系统tick值会在定时器中断中自动累加
if ((current_tick - recode_tick) == 5) Key_StateFlag = 1;//到达5ms,产生时间片标志
}
产生时间片的办法
1.在定时器中断中利用取余的方式获取时间片,以STM32系统滴答定时为例子
void SysTick_Handler(void)//假设1ms进入一次中断
{
Systick++;//uint16_t
if ((Systick % 5) == 0) Flag_5ms = 1;
if ((Systick % 10) == 0) Flag_10ms = 1;
if ((Systick % 100) == 0) Flag_100ms = 1;
if (Systick == 60000) Systick = 0;
}
上述实现方法中:
1.便于产生时间片标志,再编程中只需要判断时间片标志是否到来即可。
2.无法做到在程序中的任何位置做到一个延时,例如在某段程序中需要获取一段任意时间,此种方式无法做到,只能做到固定的时间,在中断函数中多加一个取余操作并增加标志位。
3.由于取余的操作会受到Systick 溢出的影响导致定时不准确,假如去掉if (Systick == 60000) Systick = 0;那么当定时为7ms时,由于65534已经为7ms置位一次,那么下次置位便于要在7的时候才能置位,此时定时时间变为了9ms,多了2ms产生误差。
1.在定时器中断中进行系统时钟心跳累加,利用减法机制获取需要定时的时间,,以STM32系统滴答定时为例子
void SysTick_Handler(void)//假设1ms进入一次中断
{
Systick++;//uint16_t
}
uint16_t Get_Systicks(void)
{
return Systick;
}
//例如延时5ms
void Delay_5ms(void)
{
uin16_t current_systick = Get_Systicks();
while((Get_Systicks() - current_systick) < 5);
}
上述实现方法中:
1.简化定时器中断内部操作。
2.可以做到在程序中任何地方基于时间心跳进行一个精确延时。
3.不会受到时间心跳变量溢出的影响。
4.需要注意的是时钟心跳变量一定要用无符号变量定义,不然溢出会受到有符号数定义的影响。
注(不会受到时间变量溢出影响的解释):
在C语言中是没有减法操作的,进行减法操作是加上一个数的负数,如6-250=6+(-250)
在运算中,6-250=6+(-250)
=(0110)原码 +(- 1111 1010)原码 在有符号中,最高位为符号位
=(0110)原码 +(1 1111 1010)原码
=(0110 反码 +(1 0000 0101)反码
=(0110 补码 +(1 0000 0110)补码
=(1 0000 1100)补码
补码中,正数补码=反码=原码 所以将 (1 0000 1100)补码表示为正数原码为(1100) = 12(十进制)
那么 6-250 = 12,这12个数分别是(251,252,253,254,255,0,1,2,3,4,5 正好为12)
如果上述解释难以理解,那么可以简单粗暴的理解如下:
因为1+255=0(255+1溢出等于0) 所以255 = 0-1,250 = (0-1)-5
那么6-250 = 6 - ((0-1)-5) = 6-(-6)=12
以上纯属个人见解,有误之处还望大家指正。



