Arduino定时计数器 0、1、2的灵活使用
本人刚接触Arduino不久,由于ArduinoIDE封装太多,相信不少初学者对Arduino定时计数器的概念和使用比较模糊,比如定时器0的用法。下面我就结合AVR GCC介绍一下如何在Arduino中灵活使用这三个定时计数器。
就最经典的UNO板而言,它的控制芯片是atmega328,它有0、1、2三个定时计数器,除了定时中断,它们还可以控制引脚pwm输出,通过查询芯片手册和引脚分布可知这三个定时器分别控制的引脚。
T/C0: Pin6(OC0A)和Pin5(OC0B)
T/C1: Pin9(OC1A)和Pin10(OC1B)
T/C3: Pin11(OC2A)和Pin3(OC2B)
但是实际上Arduino已经将T/C0的溢出中断运用到了delay()、delayMicroseconds、millis()、micros()中,这些函数都写在了Arduino核心代码wiring.c文件中,请参考:http://wiki.geek-workshop.com/doku.php?id=arduino:cores:wiring.c,或者直接在arduino-1.0.5-r2\hardware\arduino\cores\arduino文件夹中找到这个C文件。
假如有个问题摆在你面前:你的项目需要用一个UNO控制4个直流电机始终运转,但同时又要定时接收外来信号,那么必然需要全部用上这三个定时器,其中两个用来输出4路PWM波,另一个定时器用来定时中断。那问题来了,T/C0已经用在了像delay()这些可恶的不可与程序并行的函数中,并且wiring.c中已经声明了T/C0的溢出中断函数ISR(TIMER0_OVF_vect),也就是说我们在主程序中将不能再写一个ISR(TIMER0_OVF_vect)用来写定时接受外来信号的中断程序,那怎么办呢?
最大胆的方法就是直接改那些老外写的库函数,没错,直接修改wiring.c。这个时候你就需要仔细阅读一下wiring.c的代码了,其实也不难,里面有一个T0的溢出中断函数,然后是delay()、millis()之类的函数,这些函数都利用了T0的溢出中断,然后就是init()函数,这个函数是Arduino编译器首先编译的程序,在setup()之前首先编译的就是它,里面写的大多都是timer0、1、2或者3、4、5寄存器的初始化,学过AVR GCC的伙伴们应该可以看懂吧,实际上就是配置预分频和工作模式,比如快速PWM啦,相位修正PWM啦,(其实我也不怎么懂定时器工作原理啥的,才接触Arduino三周)。
好吧,为了达到我们使用T0去定时中断的目的,我们开始修改吧!为了可以调用T0的溢出中断,直接把ISR(TIM0_OVF_vect)那段程序注释掉,后面的millis()等函数可以不用注释掉,但是这些函数已经没有用了,你一定会觉得可惜,但是我觉得一点也不可惜,你可以有别的简单方法创造它们。
当我做完上述这些后,我写了一个T/C0的定时1秒溢出中断,结合AVR GCC的编程语法编写的测试程序,成功!串口调试助手每隔1秒输出1,程序代码如下:
// timer0_ov_interrupt.ino
//用Timer0定时中断,每隔1s输出1。
int count = 0;
void setup()
{
Serial.begin(9600);
init_time();
}
void loop( void )
{
while(1);
}
void init_time() //配置寄存器,普通模式,64分频,定时器初值为6
{
TCCR0A=0;
TCCR0B=_BV(CS01)|_BV(CS00);
TIMSK0=_BV(TOIE0);
TCNT0=6;
sei();
}
ISR(TIMER0_OVF_vect) //T0溢出中断函数
{
TCNT0=6;
++count;
if(count == 1000 )
{
Serial.write(1);
count=0;
}
}
但是尼玛当我用同样的语法测试了一下T/C2,发现始终不输出1,我也始终没有找到原因,一怒之下,我把init()函数中#if defined(ADCSRA)(这个有关ADC,不敢注释!)语句之前的所有关于定时器寄存器配置的语句都注释了。再次测试了一下T/C2,成功了!T/C1测试也同样成功了!当然测试成功还取决于我在主程序中寄存器的准确配置,这个就得多看看芯片手册了。
如此一来,三个定时器都解放了,我可以任意操作他们,我可以改变pwm的频率,而不是是使用analogwrite()函数输出一个品质很差的pwm波了。
有人会问那些delay()、delayMicroseconds函数不能用了怎么办?我之所以讨厌这写函数是因为它们不但不能与程序并行,而且还浪费了T0这个定时器,所以直接采用软件延时来代替上面的delay()函数,学过单片机的孩纸都懂的。可以参考我的另一个帖子:http://www.arduino.cn/thread-16784-1-1.html