魔法书4:Arduino UNO 内部定时器之谜

栏目: 服务器 · 发布时间: 6年前

内容简介:我是潘,曾经是个工程师。这是为 Ardui.Co 制作的 “Arduino 魔法书” 系列的专栏。在基础教程中,已简单介绍过内部定时器的基本概念。定时器就是一个内部闹钟,定时让Arduino 干些事情,比如,关联 PWM 输出,让睡眠的Arduino 醒来。内部定时器独立于CPU之外运行,不受CPU影响。要充分发挥 Arduino 的性能,就离不开定时器的灵活运用。Arduino UNO (ATmega328) 内置了3个独立的定时器,它们分别是Timer0、Timer1、Timer2,虽然规格各不相同,

我是潘,曾经是个工程师。这是为 Ardui.Co 制作的 “Arduino 魔法书” 系列的专栏。在基础教程中,已简单介绍过内部定时器的基本概念。定时器就是一个内部闹钟,定时让Arduino 干些事情,比如,关联 PWM 输出,让睡眠的Arduino 醒来。内部定时器独立于CPU之外运行,不受CPU影响。要充分发挥 Arduino 的性能,就离不开定时器的灵活运用。

Arduino UNO (ATmega328) 内置了3个独立的定时器,它们分别是Timer0、Timer1、Timer2,虽然规格各不相同,但原理一样。我们先看看Timer0 是如何运转的。

Timer0的核心是一个8位定时器,本质上它是一个计数器。这意味着它可以从0数到255,然后可以回归0,也可以倒过来,从255数到0。周而复始,这是它的基本功能,也是它存在的原因。

当然计数器可以被指定一个上限,不一定是255。当它做定时器时,可以由CPU的晶振产生的时钟信号驱动(16MHz),或者由预分频器驱动,分频比可以是 1、8、64、256、1024。当时作为计数器时,可以由外部输入驱动。Timer0 关联了两个PWM引脚:D5和D6。

Timer1的核心是一个16位计数器,它可以从0数到65535,与Timer0一样,可以一直递增,也可以一直递减,或者一会儿递增一会递增,并周而复始。

Timer1 还有一个输入捕获单元,可以将一个时间戳放到输入信号上。Arduino UNO 上,Timer1关联了两路PWM,在Arduino Mege2560 上关联了3路PWM。

Timer2和Timer0 类似,也是8位计数器,关联两路PWM。Timer2 最特别之处在于可以连接到 MCU 内置32KHz晶振上,这个晶振在 TOSC1和 TOSC2 引脚上。

遗憾的是,Arduino UNO 的 ATmega328P 这两个引脚和与 XTAL1和 XTAL2 外部16MHz 晶振的引脚复用,大部分Arduino UNO 就无法使用这个功能了,不过,在不连接外部晶振的最小系统上可以。

对于ATmega2560来说,因为XTAL1和XTAL2有专门的引脚,没有冲突,Timer2 可以由功耗极低的 32KHz 驱动,当MCU  进入深度睡眠模式时,也可以保持计时,定时将MCU唤醒。

深入内部中断

现在通过一个简单的内部中断程序,去了解定时器的运作机制。Arduino 已经将Timer0用于记录时间,其他两个则配备给了analogWrite() 函数。如果要用Timer1做实验,D9、D10的PWM将不能使用。

/*
  作者:Ardui.co
  效果:定时器溢出产生闪烁
  版本:1.0
  更新时间:2018年6月2日
*/
void setup() {
  bitSet(DDRB, 5); //bitSet() 代替pinMode() 函数节省不少空间
  TCCR1A = 0;
  TCCR1B = 1 << CS12;
  bitSet(TIMSK1, TOIE1);
}
 
void loop() {
}
ISR(TIMER1_OVF_vect) {
  bitSet(PINB, 5); // 翻转
}

又一个闪烁程序!这里直接用Timer1来计时。首先,需要把Timer1 的 TCCR1A 寄存器(Timer/Counter1 Control Register A)中所有的位置零,这个寄存器控制着Timer1的工作状态,默认被Arduino 跟PWM关联在一起,置零后,Timer1 就被释放出来了。TCCR1A 的具体用法可以参见ATmega328P的DataSheet 第135 页。

Timer1 第二个寄存器 TCCR1B 可以设置分频比,即以什么速度驱动Timer1。别忘了Timer1本质是个计数器,这是理解的关键,如果不分频,Timer1 将以16Mhz速度运行,从0数到65535,大约需要 0.0041s:

魔法书4:Arduino UNO 内部定时器之谜

系统时钟是16MHz, 这里设置为 16MHz / 256 =  62.5KHz。因为满了256倍,所以从0数到65535 大约需要0.0041s * 256 = 1.0486s 约1s 时间。

下一步使用 bitSet() 设置TIMSK1(时间中断寄存器),TOIE1 即允许中断 (Timer Overflow Interrupt Enable 1 )。

最后就是中断处理函数,非常简单:

ISR(TIMER1_OVF_vect) {
  bitSet(PINB, 5); // 翻转
}

Timer1_OVF_vect 含义如果Timer1 溢出,返回真值。Timer1溢出的含义即在分频器驱动下, 从0数到65535,一旦超过65535,即溢出。分频设定成256,不是随便来的,而是刚好设定为1秒左右,Timer1溢出(1/16Mhz * 65535 * 256 = 1.0486s )。

这只是内部定时器提供多种模式之一。还可以使用CTC(Clear Timer on Compare match,比较匹配时清除定时器),这个模式可以理解为设定一个值,然后跟定时器比较,一旦超过这个值,就溢出。

/*
  作者:Ardui.co
  效果:CTC模式定时器溢出产生闪烁
  版本:1.0
  更新时间:2018年5月29日
*/
void setup() {
  bitSet(DDRB, 5);
  TCCR1A = 0;
  TCCR1B = 1 << WGM13 | 1 << WGM12 | 1 << CS12 | 1 << CS10;
  ICR1 = 15625;
  bitSet(TIMSK1, ICIE1); // 允许输入捕捉中断
}
 
void loop() {
}
ISR(TIMER1_CAPT_vect) {
  bitSet(PINB, 5); // 翻转
}

寄存器TCCR1B 中 WGM13、WGM12 两个位,可以设定Timer1 的模式,而CS1x位则设置分频,前面提到过了。

魔法书4:Arduino UNO 内部定时器之谜

bitSet(TIMSK1, ICIE1) 中,ICIE 即 Input Capture Interrupt Enable 输入捕捉中断。ICR1 即捕捉寄存器,可以存储一个值。当Timer1数到这个值时,立刻重新计数。我们设置了1024分频比,即16Mhz / 1024 = 15.625KHz,ICR1 设置位15625,意思就是让计数器从0数到15625时,定时器溢出,重新计数,刚好1秒:

(1/16MHz)  * 1024 * 15625 = 1s


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Tangled Web

The Tangled Web

Michal Zalewski / No Starch Press / 2011-11-26 / USD 49.95

"Thorough and comprehensive coverage from one of the foremost experts in browser security." -Tavis Ormandy, Google Inc. Modern web applications are built on a tangle of technologies that have been de......一起来看看 《The Tangled Web》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具