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

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

内容简介:我是潘,曾经是个工程师。这是为 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


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

查看所有标签

猜你喜欢:

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

连线力

连线力

杨国斌 / 邓燕华 / 广西师范大学出版社 / 2013-9 / 39.00

《连线力》,最关切我们未来的“思想@网络.中国”丛书之一,互联网中国传媒参考书。 中国网民在行动。在中国的广大网民中,普遍存在着对正义的渴望和追求,对弱者和小人物的同情, 对贪官污吏的痛恶,对政府的失望, 对权贵的嘲讽,对沟通的渴望,甚至对革命的呼唤。这些因素有着共同的内在逻辑,即情感逻辑。在这个意义上,情感汹涌的网络事件,是整个中国社会情感结构的脉络。 1994年,中国开通了全功能的......一起来看看 《连线力》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器