Arduino开发实例-Arduino定时器详解

Arduino定时器详解

文章目录

Arduino 是一个流行的用于开发电子项目的开源平台。 Arduino 板基于微控制器,可以使用不同的外设和传感器执行各种任务。 Arduino 微控制器的重要功能之一是定时器。 定时器是一种可以测量和计算时间间隔的硬件设备。 定时器可用于多种目的,例如生成精确信号、测量经过的时间、创建延迟、触发事件等等。 在本文中,我们将介绍 Arduino 定时器的概念,解释其工作原理,并展示在不同场景中使用 Arduino 定时器的一些示例。

1、什么是Arduino定时器?

定时器是一种可以测量和计算时间间隔的硬件设备。 定时器通常由存储数字的计数器寄存器、提供恒定频率的时钟源以及确定计数器寄存器如何递增或递减的控制单元组成。 定时器可以工作在不同的模式下,例如正常模式、比较模式、捕获模式和 PWM 模式。 在正常模式下,计数器寄存器在每个时钟周期加一,直到达到最大值,然后溢出并再次从零开始。 在比较模式下,计数器寄存器与另一个寄存器中存储的预定义值进行比较,当它们匹配时,会生成输出信号或中断。 在捕捉模式下,计数器寄存器可以在某个边沿(上升沿或下降沿)捕捉另一个输入信号的值,并将其存储在另一个寄存器中。 在 PWM 模式下,计数器寄存器可以通过在特定值打开和关闭输出信号来控制输出信号的占空比。 Arduino 板有不同类型和数量的定时器,具体取决于它们使用的微控制器。 例如,Arduino Uno 有三个定时器:Timer0、Timer1 和 Timer2。 Timer0 和 Timer2 是 8 位定时器,这意味着它们可以存储 0 到 255 的值。 Timer1 是 16 位定时器,这意味着它可以存储 0 到 65535 的值。每个定时器都有不同的功能和特性,可以进行配置 通过设置各种寄存器。

2、如何使用Arduino定时器?

Arduino 定时器可用于多种用途,例如生成精确信号、测量经过的时间、创建延迟、触发事件等。 然而,其中一些函数已经由Arduino核心库实现,例如delay()、millis()、micros()、tone()、servo()等。这些函数在内部使用一个或多个定时器来执行它们的功能。 任务。 因此,如果您想在草图中使用这些函数,您应该了解它们使用哪个计时器以及它们如何影响其行为。 如果要将Arduino定时器用于核心库函数未涵盖的其他用途,则需要直接访问定时器寄存器并根据需要进行配置。 这需要了解微控制器数据表以及定时器寄存器的名称和功能。 您还需要使用一些宏或函数来操作寄存器中的位。 为了说明如何直接使用 Arduino 定时器,我们将展示一些在不同场景下使用 Arduino Uno 定时器的示例。

2.1 示例 1:生成方波信号

在本例中,我们将在比较模式下使用 Timer1 来生成频率为 1 kHz、占空比为 50% 的方波信号。 我们将在 Arduino Uno 的引脚 9 (OC1A) 上输出该信号。

步骤如下:

  • 将Timer1 的时钟源设置为预分频64。这意味着定时器将每64 个时钟周期递增1。 由于 Arduino Uno 的时钟频率为 16 MHz,这意味着计时器将每 4 微秒递增 1。
  • 将Timer1 的比较值设置为249。这意味着当计数器寄存器达到249 时,将产生输出信号或中断。
  • 设置 Timer1 的输出模式以切换比较匹配。 这意味着当计数器寄存器与比较值匹配时,输出引脚将切换其状态(高或低)。
  • 通过设置控制寄存器中的相应位来启用定时器 1。

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Define some macros for convenience
#define TCCR1A \_SFR\_IO8(0x80) // Timer/Counter1 Control Register A
#define TCCR1B \_SFR\_IO8(0x81) // Timer/Counter1 Control Register B
#define OCR1A \_SFR\_IO16(0x88) // Output Compare Register 1 A
#define CS10 0 // Clock Select bit 0
#define CS11 1 // Clock Select bit 1
#define CS12 2 // Clock Select bit 2
#define WGM12 3 // Waveform Generation Mode bit 2
#define COM1A0 6 // Compare Output Mode bit 0 for Channel A
#define COM1A1 7 // Compare Output Mode bit 1 for Channel A

void setup() {
pinMode(9, OUTPUT); // Set pin 9 as output
TCCR1A = 0; // Clear Timer1 Control Register A
TCCR1B = 0; // Clear Timer1 Control Register B
OCR1A = 249; // Set compare value to 249
TCCR1A |= (1 << COM1A0); // Set output mode to toggle on compare match
TCCR1B |= (1 << WGM12); // Set waveform generation mode to CTC (Clear Timer on Compare match)
TCCR1B |= (1 << CS10) | (1 << CS11); // Set clock source to prescale by 64
}

void loop() {
// Nothing to do here, the timer will generate the signal automatically
}


2.2 示例 2:测量输入信号的频率

在此示例中,我们将在捕获模式下使用 Timer2 来测量 Arduino Uno 引脚 5 (T1) 上输入信号的频率。 我们将在串行监视器上显示测量的频率。 步骤如下:

  • 将Timer2 的时钟源设置为预分频8。这意味着定时器将每8 个时钟周期递增1。 由于 Arduino Uno 的时钟频率为 16 MHz,这意味着计时器将每 0.5 微秒递增 1。
  • 将 Timer2 的输入捕捉沿设置为上升沿。 这意味着当输入信号从低电平变为高电平时,定时器将捕获计数器寄存器的值。
  • 使能Timer2的输入捕捉中断。 这意味着当输入捕获事件发生时,将执行中断服务程序(ISR)。
  • 在 ISR 中,从输入捕获寄存器读取捕获的值并将其存储在变量中。 然后计算两次连续捕获之间的时间差并将其存储在另一个变量中。 然后通过将一秒除以时间差来计算频率并将其存储在另一个变量中。 然后在串行监视器上打印频率。

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Define some macros for convenience
#define TCCR2 \_SFR\_IO8(0x45) // Timer/Counter2 Control Register
#define TCNT2 \_SFR\_IO8(0x44) // Timer/Counter2 Register
#define OCR2 \_SFR\_IO8(0x43) // Output Compare Register 2
#define ICR2 \_SFR\_IO16(0x46) // Input Capture Register 2
#define TIMSK \_SFR\_IO8(0x59) // Timer/Counter Interrupt Mask Register
#define TIFR \_SFR\_IO8(0x58) // Timer/Counter Interrupt Flag Register
#define CS20 0 // Clock Select bit 0 for Timer2
#define CS21 1 // Clock Select bit 1 for Timer2
#define CS22 2 // Clock Select bit 2 for Timer2
#define WGM21 3 // Waveform Generation Mode bit 1 for Timer2
#define WGM20 6 // Waveform Generation Mode bit 0 for Timer2
#define FOC2 7 // Force Output Compare for Timer2
#define ICES2 WGM20 // Input Capture Edge Select for Timer2
#define ICIE2 FOC2 // Input Capture Interrupt Enable for Timer2

volatile unsigned int lastCapture = 0; // Variable to store the last captured value
volatile unsigned int timeDiff = 0; // Variable to store the time difference between two captures
volatile unsigned long frequency = 0; // Variable to store the calculated frequency

void setup() {
Serial.begin(9600); // Initialize serial communication at baud rate of 9600 bps
TCCR2 = 0; // Clear Timer2 Control Register
TCNT2 = 0; // Clear Timer2 Counter Register
OCR2 = 255; // Set Output Compare value to maximum (not used)

TCCR2 |= (1 << ICES2); // Set input capture edge to rising
TCCR2 |= (1 << CS21); // Set clock source to prescale by
TIMSK |= (1 << ICIE2); // Enable input capture interrupt

}

void loop() {
// Nothing to do here, the timer will measure the frequency and print it in the ISR.
}

// Define the ISR for input capture interrupt
ISR(TIMER2_CAPT_vect) {
unsigned int currentCapture = ICR2; // Read the current captured value from the input capture register
timeDiff = currentCapture - lastCapture; // Calculate the time difference between two captures
lastCapture = currentCapture; // Update the last captured value
frequency = 1000000UL / timeDiff; // Calculate the frequency by dividing one second by the time difference (in microseconds)
Serial.print("Frequency: "); // Print a label on the serial monitor
Serial.print(frequency); // Print the frequency on the serial monitor
Serial.println(" Hz"); // Print a unit on the serial monitor
}


2.3 示例 3:创建自定义延迟函数

在此示例中,我们将在正常模式下使用 Timer0 创建自定义延迟函数,该函数可以将草图的执行暂停指定的毫秒数。 我们将使用此函数使 Arduino Uno 引脚 13 上的 LED 闪烁。 步骤如下:

  • 将 Timer0 的时钟源设置为预分频 64。这意味着定时器将每 64 个时钟周期递增 1。 由于 Arduino Uno 的时钟频率为 16 MHz,这意味着计时器将每 4 微秒递增 1。
  • 将Timer0的溢出值设置为249。这意味着当计数器寄存器达到249时,它将溢出并重新从零开始。 这也意味着每 1 毫秒(4 微秒 x 250)就会发生一次溢出事件。 - 使能Timer0的溢出中断。 这意味着当发生溢出事件时,将执行中断服务程序(ISR)。
  • 在 ISR 中,增加一个全局变量,用于计算草图启动后经过的毫秒数。
  • 在自定义延迟函数中,接受一个参数,指定延迟的毫秒数。 然后将全局变量的当前值与之前的值进行比较,并等待差值等于或大于参数。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Define some macros for convenience
#define TCCR0 \_SFR\_IO8(0x33) // Timer/Counter0 Control Register
#define TCNT0 \_SFR\_IO8(0x32) // Timer/Counter0 Register
#define OCR0 \_SFR\_IO8(0x3C) // Output Compare Register 0
#define TIMSK \_SFR\_IO8(0x59) // Timer/Counter Interrupt Mask Register
#define TIFR \_SFR\_IO8(0x58) // Timer/Counter Interrupt Flag Register
#define CS00 0 // Clock Select bit 0 for Timer0
#define CS01 1 // Clock Select bit 1 for Timer0
#define CS02 2 // Clock Select bit 2 for Timer0
#define WGM01 3 // Waveform Generation Mode bit 1 for Timer0
#define WGM00 6 // Waveform Generation Mode bit 0 for Timer0
#define FOC0 7 // Force Output Compare for Timer0
#define TOIE0 FOC0 // Timer/Counter0 Overflow Interrupt Enable

volatile unsigned long millisCount = 0; // Variable to store the number of milliseconds elapsed since the sketch started

void setup() {
pinMode(13, OUTPUT); // Set pin 13 as output
TCCR0 = 0; // Clear Timer0 Control Register
TCNT0 = 0; // Clear Timer0 Counter Register
OCR0 = 249; // Set overflow value to
TCCR0 |= (1 << CS00) | (1 << CS01); // Set clock source to prescale by
TIMSK |= (1 << TOIE0); // Enable overflow interrupt

}

void loop() {
// Blink an LED on pin 13 using the custom delay function
digitalWrite(13, HIGH); // Turn on the LED
customDelay(500); // Wait for half a second
digitalWrite(13, LOW); // Turn off the LED
customDelay(500); // Wait for half a second
}

// Define the ISR for overflow interrupt
ISR(TIMER0_OVF_vect) {
millisCount++; // Increment the milliseconds count by one
}

// Define the custom delay function
void customDelay(unsigned long ms) {
unsigned long start = millisCount; // Store the current value of milliseconds count in a local variable
while (millisCount - start < ms) { // Wait until the difference between the current and previous values is equal to or greater than the parameter
// Do nothing, just wait
}
}


3、总结

在本文中,我们介绍了 Arduino 定时器的概念,解释了它的工作原理,并展示了在不同场景中使用 Arduino 定时器的一些示例。 我们了解到Arduino定时器是一种可以测量和计数时间间隔的硬件设备,它可以工作在不同的模式下,如正常模式、比较模式、捕获模式和PWM模式。 我们还了解到,Arduino 板根据所使用的微控制器而具有不同类型和数量的定时器,并且一些核心库函数在内部使用一个或多个定时器来执行其任务。 我们还了解到,我们可以直接访问定时器寄存器并根据需要配置它们,但这需要对微控制器数据表以及定时器寄存器的名称和功能有一定的了解。 我们希望本文能够帮助您了解 Arduino 定时器的基础知识以及如何在您的项目中使用它。 如果您有任何疑问或建议,请随时在下方留言。

文章来源: https://iotsmart.blog.csdn.net/article/details/131354925