Arduino定时计数器 0、1、2的灵活使用

来源:互联网 发布:b1ued同志交友软件 编辑:程序博客网 时间:2024/06/16 05:40

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

 

 

1 0
原创粉丝点击