【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
在mcu开发过程当中,有一种开发模式用的比较多,那就是中断+while(1)。这里面的中断,又是以定时器中断最为普遍。很多时候我们使用mcu,恰恰是看中了它的实时性。比如说,在运动控制中,如果以24ms作为一个周期,那么第一个6ms、第二个6ms、一直到最后一个6ms,每一个6ms做的事情都是不一样的。
所以,对于实时性很强的操作,我们就直接在中断里面完成对应的工作。那些实时性一般的代码就放到后台来做。以前、后台的角度来说,中断处理类似于前台,while(1)类似于后台。前台如果说是处理业务,那么后台处理什么呢,通常就是日志输出、看门狗检测、数据统计等等,这都是可以放在后台来做的。今天,我们就来看看在mcu下面,定时器是怎么做的。
1、查看main函数
首先还是查看main函数,看看如果要使用定时器,需要做些什么,
int main(void)
{SystemClock_Config();LED_GPIO_Config();SysTick_Init();while(1) {LED1_ON; Delay_us(1000);LED1_OFF;Delay_us(1000);}
}
main函数比较简单,除了mcu设置、gpio设置之外,看到一个新的配置,那就是SysTick_Init。剩下来的代码就比较简单了,从名字上就可以看出,这是一个LED1点亮和熄灭的功能。代码过程当中采用了延时函数,并且这个延时函数应该和今天谈到的定时器有关。
2、定时器初始化
前面看到了定时器初始化函数,下面就看看SysTick_Init是怎么实现的。
void SysTick_Init(void)
{if (HAL_SYSTICK_Config(SystemCoreClock / 1000)){ /* Capture error */ while (1);}
}
代码还是比较简单的,主要就是设置一下定时周期。后面的1000除数,表示设置的定时器周期是多少,假设1s的频率是SystemCoreClock,那么除以1000,代表定时中断的周期是1ms。
3、延时判断
LED1点亮和熄灭的时候,使用到了函数Delay_us。那我们看下,这个函数是怎么实现的,
void Delay_us(__IO u32 nTime)
{ TimingDelay = nTime; while(TimingDelay != 0);
}
代码不复杂,就是把参数nTime赋值给TimingDelay,接下来看TimingDelay什么时候变成0。因为没有看到其他地方对TimingDelay进行处理,所以应该是中断代码对TimingDelay进行了修改。
4、定时器中断
直接到stm32f1xx_it.c下面寻找对应的定时器中断函数,
void SysTick_Handler(void)
{TimingDelay_Decrement();
}
通过内容,发现定时器中断函数里面,直接调用了TimingDelay_Decrement子函数,继续分析,
void TimingDelay_Decrement(void)
{if (TimingDelay != 0x00){ TimingDelay--;}
}
看到这个函数之后,其实就大致明白怎么回事了。原来在TimingDealy_Decrement进行了设置,只要TimeingDelay不等于0,那么每次中断的时候就会进行递减1的处理。不过因为我们定时器的精度是1ms,所以如果延时只有1ms,那么其实是不准的。
但是如果延时的精度要求不高,比如今天使用的1s延时,那就没啥问题。
5、不变的LED1修改
为了验证实验效果,这里通过LED1闪烁的方法进行验证,所以第一步就是需要把pin的位置修改为pc13,接下来就是烧录版本、按下复位键,如果看到led发生了闪烁,那代表一切ok;否则就要好好检查下硬件接线和软件代码了。
6、其他定时器
除了SysTick系统定时器之外,mcu还有很多的定时器,比如说Tim系列的定时器。广义一点来说,看门狗也属于定时器。当然和SysTick相比,Tim定时器一般精度更高,用途更广。以Tim为例,它除了有定时器的功能之外,还可以用作电机编码器的一部分配置来使用,这都是很常见的做法。