arduino 计时器和中断

本教程说明了Arduino板的计时器和中断的用法。作为Arduino程序员,您将在没有知识的情况下使用计时器和中断,因为Arduino API隐藏了所有底层硬件。许多Arduino函数使用计时器,例如时间函数:delay( ),millis( )和micros( )和delayMicroseconds( )。PWM函数AnalogWrite( )使用计时器,就像tone( )和noTone( )函数一样。甚至伺服库也使用定时器和中断。

什么是计时器?

计时器或更准确地说是计时器/计数器是Arduino控制器内置的一块硬件 (其他控制器也具有计时器硬件)。它就像一个时钟,可用于测量时间事件。定时器可以通过一些特殊的寄存器进行编程。您可以为计时器配置预分频器,或者为操作模式配置其他功能。

Arduino的控制器是Atmel AVR ATmega168或ATmega328。这些芯片是引脚兼容的,只是内部存储器的大小不同。两者都有3个定时器,分别称为timer0,timer1和timer2。Timer0和Timer2是8位定时器,其中timer1是16位定时器。8位和16位定时器之间最重要的区别是定时器分辨率。8位表示256个值,而16位表示65536个值,以实现更高的分辨率。
Arduino Mega系列的控制器是Atmel AVR ATmega1280或ATmega2560。同样相同的只是存储器大小不同。这些控制器具有6个计时器。定时器0,定时器1和定时器2与ATmega168 / 328相同。定时器3,定时器4和定时器5均为16位定时器,类似于定时器1。
所有计时器均取决于Arduino系统的系统时钟。通常系统时钟为16MHz,但Arduino Pro 3,3V为8Mhz。因此,编写自己的计时器函数时要小心。
定时器硬件可以配置一些特殊的定时器寄存器。在Arduino固件中,所有计时器均配置为1kHz频率,并且中断均已启用。

定时器0:

定时器0是一个8位定时器。
在Arduino世界中,timer0用于计时器功能,例如delay( ) 364,millis( )861和micros( )380。如果更改timer0寄存器,则可能会影响Arduino定时器功能。因此,您应该知道自己在做什么。

计时器1:

Timer1是一个16位定时器。
在Arduino世界中,Servo库 642在Arduino Uno上使用timer1(在Arduino Mega上为timer5)。

定时器2:

定时器2是8位定时器,类似于定时器0。
在Arduino工作中,tone( ) 493函数使用timer2。

Timer3,Timer4,Timer5:

计时器3、4、5仅在Arduino Mega板上可用。这些定时器都是16位定时器。

计时器寄存器

您可以通过计时器寄存器更改计时器行为。最重要的定时器寄存器是:
TCCRx-定时器/计数器控制寄存器。可以在此处配置预分频器。

在这里插入图片描述

在这里插入图片描述

  • TCNTx-定时器/计数器寄存器。实际计时器值存储在此处。

  • OCRx-输出比较寄存器

  • ICRx-输入捕捉寄存器(仅适用于16位定时器)

  • TIMSKx-定时器/计数器中断屏蔽寄存器。启用/禁用定时器中断。

  • TIFRx-定时器/计数器中断标志寄存器。指示未决的定时器中断。

时钟选择和定时器频率

可以为每个定时器独立选择不同的时钟源。要计算计时器频率(例如使用timer1的2Hz),您需要:

  1. Arduino的CPU频率16Mhz
  2. 最大计时器计数器值(8位为256,16位为65536)
  3. 将CPU频率除以所选的预分频器(16000000/256 = 62500)
  4. 将结果除以所需的频率(62500 / 2Hz = 31250)
  5. 如果失败,请对照最大计时器计数器值(31250 <65536成功)验证结果,然后选择更大的预分频器。
    在这里插入图片描述

计时器模式

定时器可以配置为不同的模式。

  • PWM模式。脉宽调制模式。OCxy输出用于生成PWM信号
  • CTC模式。比较匹配时清除计时器。当定时器计数器到达比较匹配寄存器时,定时器将被清除

在这里插入图片描述

什么是中断?

控制器上运行的程序通常按指令顺序运行。中断是一个外部事件,它会中断正在运行的程序并运行特殊的中断服务程序(ISR)。ISR完成后,正在运行的程序将继续执行下一条指令。指令是指单条机器指令,而不是C或C ++代码行。
在未决中断能够调用ISR之前,必须满足以下条件:

  • 通常必须启用中断
  • 必须启用相应的中断屏蔽

通常可以使用函数interrupts( ) 154 / noInterrupts( ) 40启用/禁用中断。默认情况下,在Arduino固件中启用中断。通过设置/清除中断屏蔽寄存器(TIMSKx)中的位,可以启用/禁用中断屏蔽。
发生中断时,将设置中断标志寄存器(TIFRx)中的标志。进入ISR或手动清除中断标志寄存器中的位时,该中断将自动清除。

Arduino函数attachInterrupt( )191和detachInterrupt( )37仅可用于外部中断引脚。这些是不同的中断源,这里不再讨论。

定时器中断

定时器可以产生不同类型的中断。寄存器和位的定义可以在处理器数据表(Atmega328 185或Atmega2560 175)和I / O定义头文件中找到(对于Arduino为iomx8.h,对于Arduino Mega为iomxx0_1.h在硬件/工具/ avr / include / avr文件夹)。后缀x代表定时器编号(0…5),后缀y代表输出编号(A,B,C),例如TIMSK1(定时器1中断屏蔽寄存器)或OCR2A(定时器2输出比较寄存器A)。

计时器溢出:

计时器溢出表示计时器已达到极限值。当发生定时器溢出中断时,中断标志寄存器TIFRx中的定时器溢出位TOVx将置1。当中断屏蔽寄存器TIMSKx中的定时器溢出中断使能位TOIEx置1时,将调用定时器溢出中断服务程序ISR(TIMERx_OVF_vect)。

输出比较匹配:

当发生输出比较匹配中断时,OCFxy标志将在中断标志寄存器TIFRx中置1。当中断屏蔽寄存器TIMSKx中的输出比较中断使能位OCIExy置位时,将调用输出比较匹配中断服务ISR(TIMERx_COMPy_vect)例程。

定时器输入捕捉:

当定时器输入捕捉中断发生时,输入捕捉标志位ICFx将在中断标志寄存器TIFRx中置1。当中断屏蔽寄存器TIMSKx中的输入捕捉中断使能位ICIEx置1时,将调用定时器输入捕捉中断服务程序ISR(TIMERx_CAPT_vect)。

PWM和计时器

计时器和具有PWM功能的输出之间存在固定关系。当您查阅数据手册或处理器的引脚排列时,这些具有PWM功能的引脚的名称如OCRxA,OCRxB或OCRxC(其中x表示定时器编号0…5)。PWM功能通常与其他引脚功能共享。
Arduino具有3个Timers和6个PWM输出引脚。计时器和PWM输出之间的关系是:

  • 引脚5和6:由计时器0 控制
  • 引脚9和10:由计时器1 控制。
  • 引脚11和3:由计时器2 控制。

在Arduino Mega上,我们有6个计时器和15个PWM输出:

  • 引脚4和13 :由定时器0控制
  • 引脚11和12:由定时器1控制
  • 引脚9 和10:由定时器2 控制
  • 引脚2、3和5:由定时器3控制
  • 引脚6、7和8:由计时器4控制
  • 引脚46、45和44 :由计时器5控制

有用的第三方库存在一些第三方库,它们使用计时器:

  • Ken Shirrifs IR库。使用定时器2。发送和接收任何种类的IR远程信号,例如RC5,RC6,Sony
  • Timer1和Timer3库 。使用timer1或tiner3。编写自己的计时器中断服务例程的简单方法。

我已经将这些库移植到了Arduino Mega的不同计时器上。所有移植的库都可以在附件中找到。

陷阱
当对Arduino进行编程并使用使用计时器的函数或库时,可能会遇到一些陷阱。

  • 伺服库使用Timer1。当您在Arduino上使用伺服库时,不能在引脚9、10上使用PWM。对于Arduino Mega来说要困难一些。所需的计时器取决于伺服器的数量。每个计时器可处理12个伺服器。对于前12个伺服器,将使用计时器5(在引脚44,45,46上释放PWM)。对于24个Servos,将使用计时器5和1(在引脚11,12,44,45,46上松开PWM)。对于36个伺服器,将使用计时器5、1和3(在引脚2,3,5上松开PWM, 11,12,44,45,46)。对于48个伺服器,将使用所有16​​位定时器5、1、3和4(失去所有PWM引脚)。
  • 引脚11具有共享功能PWM和MOSI。SPI接口需要MOSI。您不能在Arduino上同时在引脚11和SPI接口上使用PWM。在Arduino Mega上,SPI引脚位于不同的引脚上。
  • tone( )函数至少使用timer2。当您使用Arduino的tone( )函数和Arduino Mega上的9,10引脚时,不能在引脚3,11上使用PWM。

例子

带比较匹配中断的指示灯闪烁

第一个示例使用CTC模式下的timer1和比较匹配中断来切换LED。计时器配置为2Hz的频率。LED在中断服务程序中切换。

在这里插入图片描述
参考网址:http://www.letmakerobots.com/node/28278

/* Arduino 101: timer and interrupts
   1: Timer1 compare match interrupt example 
   more infos: http://www.letmakerobots.com/node/28278
   created by RobotFreak 
*/
#define ledPin 13
void setup()
{
  pinMode(ledPin, OUTPUT);
  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;
  OCR1A = 31250;            // compare match register 16MHz/256/2Hz
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS12);    // 256 prescaler 
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // enable all interrupts
}


ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{
  digitalWrite(ledPin, digitalRead(ledPin) ^ 1);   // toggle LED pin
}

void loop()
{
  // your program here…
}

Blinking LED with timer overflow interrupt

same example like before but now we use the timer overflow interrupt. In this case timer1 is running in normal mode.
The timer must be preloaded every time in the interrupt service routine.

/* 
 * Arduino 101: timer and interrupts
 * 2: Timer1 overflow interrupt example 
 * more infos: http://www.letmakerobots.com/node/28278
 * created by RobotFreak 
 */

#define ledPin 13
void setup()
{
  pinMode(ledPin, OUTPUT);
  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 34286;            // preload timer 65536-16MHz/256/2Hz
  TCCR1B |= (1 << CS12);    // 256 prescaler 
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();             // enable all interrupts
}

ISR(TIMER1_OVF_vect)        // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
  TCNT1 = 34286;            // preload timer
  digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
}

void loop()
{
  // your program here...
}

下面的例子是我的一Ardubot项目的一部分 。它使用Timer2和比较匹配中断来读取编码器输入。默认情况下,Timer2初始化为1kHz(1ms周期)的频率。在中断服务程序中,读取所有编码器引脚的状态,并使用状态机消除错误的读取。使用定时器中断比使用4个输入更改中断要容易得多。

正交编码器的信号是2位格雷码 。每个状态仅改变一位。状态机非常适合检查信号并计算编码器滴答声。计时器必须足够快,才能识别每个状态变化。对于Pololu车轮编码器 本文所用,1ms的定时器是速度不够快。
在这里插入图片描述

The following example has been modified to work with Arduino V1.x

/*
/* 
 * Arduino 101: timer and interrupts
 * 3: Timer2 compare interrupt example. Quadrature Encoder
 * more infos: http://www.letmakerobots.com/node/28278
 * created by RobotFreak 
 *
 * Credits:
 * based on code from Peter Dannegger
 * http://www.mikrocontroller.net/articles/Drehgeber
 */
#if ARDUINO >= 100
  #include “Arduino.h”
#else
  #include “WConstants.h”
#endif

// Encoder Pins

#define encLtA 2
#define encLtB 3
#define encRtA 11 
#define encRtB 12
#define ledPin 13

#define LT_PHASE_A		digitalRead(encLtA)
#define LT_PHASE_B		digitalRead(encLtB)
#define RT_PHASE_A		digitalRead(encRtA)
#define RT_PHASE_B		digitalRead(encRtB)

static volatile int8_t encDeltaLt, encDeltaRt;
static int8_t lastLt, lastRt;
int encLt, encRt;

ISR( TIMER2_COMPA_vect )
{
  int8_t val, diff;
  digitalWrite(ledPin, HIGH);   // toggle LED pin
  val = 0;
  if( LT_PHASE_A )
    val = 3;
  if( LT_PHASE_B )
    val ^= 1;					// convert gray to binary
  diff = lastLt - val;				// difference last - new

  if( diff & 1 ){				// bit 0 = value (1)
    lastLt = val;				// store new as next last
    encDeltaLt += (diff & 2) - 1;		// bit 1 = direction (+/-)
  }

  val = 0;
  if( RT_PHASE_A )
    val = 3;
  if( RT_PHASE_B )
    val ^= 1;				// convert gray to binary
  diff = lastRt - val;				// difference last - new

  if( diff & 1 ){				// bit 0 = value (1)
    lastRt = val;				// store new as next last
    encDeltaRt += (diff & 2) - 1;		// bit 1 = direction (+/-)
  }
  digitalWrite(ledPin, LOW);   // toggle LED pin
}

void QuadratureEncoderInit(void)
{
  int8_t val;
  cli();
  TIMSK2 |= (1<<OCIE2A);
  sei();
  pinMode(encLtA, INPUT);
  pinMode(encRtA, INPUT);
  pinMode(encLtB, INPUT);
  pinMode(encRtB, INPUT);

  val=0;
  if (LT_PHASE_A)
    val = 3;
  if (LT_PHASE_B)
    val ^= 1;
  lastLt = val;
  encDeltaLt = 0;
  val=0;

  if (RT_PHASE_A)
    val = 3;
  if (RT_PHASE_B)
    val ^= 1;
  lastRt = val;
  encDeltaRt = 0;
  encLt = 0;
  encRt = 0;
}

int8_t QuadratureEncoderReadLt( void )			// read single step encoders
{
  int8_t val;
  cli();
  val = encDeltaLt;
  encDeltaLt = 0;
  sei();
  return val;					// counts since last call
}

int8_t QuadratureEncoderReadRt( void )			// read single step encoders
{
  int8_t val;
  cli();
  val = encDeltaRt;
  encDeltaRt = 0;
  sei();
  return val;					// counts since last call
}

void setup()
{
  Serial.begin(38400);
  pinMode(ledPin, OUTPUT);
  QuadratureEncoderInit();
}

void loop()
{
  encLt += QuadratureEncoderReadLt();
  encRt += QuadratureEncoderReadRt();
  Serial.print("Lt: “);
  Serial.print(encLt, DEC);
  Serial.print(” Rt: ");
  Serial.println(encRt, DEC);
  delay(1000);
}

Popular Links
2150 http://arduino.cc/playground/Code/Timer1
1699 http://arduino.cc/en/Reference/Millis
1204 http://arduino.cc/en/Reference/Servo
1049 http://arduino.cc/en/Reference/Tone
975 //www.robotshop.com/community/forum/uploads/default/original/2X/4/460915d584caebf2be9…
763 http://arduino.cc/en/Reference/Micros
760 http://arduino.cc/en/Reference/Delay
724 //www.robotshop.com/community/forum/uploads/default/original/2X/5/53d05d20393aaf1f688…
466 http://arduino.cc/en/Reference/Volatile
374 http://arduino.cc/en/Reference/AttachInterrupt
373 http://www.atmel.com/dyn/resources/prod_documents/doc8271.pdf
346 http://www.atmel.com/dyn/resources/prod_documents/doc2549.pdf
308 http://arduino.cc/en/Reference/Interrupts
279 http://www.arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
261 http://arduino.cc/en/Reference/DelayMicroseconds
200 http://arduino.cc/en/Reference/AnalogWrite
110 http://www.pololu.com/catalog/product/1217
82 http://ArduinoInfo.Info
82 /letsmakerobots/node/11987
79 http://arduino.cc/en/Reference/NoInterrupts
67 http://arduino.cc/forum/index.php?topic=130423.0
66 http://arduino.cc/en/Reference/DetachInterrupt
60 http://en.wikipedia.org/wiki/Gray_code
53 http://arduino.cc/en/Reference/NoTone
16 https://code.google.com/p/arduino/source/browse/trunk/libraries/Servo/Servo.cpp
11 volatile - Arduino Reference arduino.cc
7 GitHub - adafruit/Adafruit_NeoPixel: Neo Pixels! github.com
5 https://www.arduino.cc/reference/en/language/variables/variable-scope–qualifiers/vol…
There are 33 replies with an estimated read time of 19 minutes.

版权声明:本文为CSDN博主「acktomas」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/acktomas/article/details/102881313

生成海报
点赞 0

acktomas

我还没有学会写个人说明!

暂无评论

相关推荐

【翻译】arduino 内置示例

内置示例{#top} https://www.arduino.cc/en/Tutorial/BuiltInExamples 内置示例是Arduino软件(IDE)中包含的草图,单击工具栏菜单打开它们&