Arduino的定时器功能
文章目录
引言:
在开发Arduino的过程中,我们往往使用已经封装好的API,对于一些延时或定时器的操作,也有一些封装好的API可供使用,比如millis()、delay(ms)、analogWrite(pin, value)、pulseIn(EchoPin, HIGH)等等,但这些都是写死的API,无法做到像51和32单片机那样自己定义中断服务函数的内容!这些API的底层其实也是操作定时器相关的寄存器来实现的,自己去配置好寄存器,即可自己定义中断服务函数的内容(自定义了指定的定时器相关的配置,原本Arduino的一些API将不能使用,后面将给出对应关系)。
Arduino Uno的定时器:time0(8位)、time1(16位)、time2(8位)
Arduino Mega 2560的定时器:time0(8位)、time1(16位)、time2(8位)、time3(16位)、time4(16位)、time5(16位)
MsTimer2(定时器2)、TimerOne(定时器1)库的使用
定时器对应的API:
定时器0:delay()|、millis()、micros();
定时器1:Servo库(Mega 2560为定时器5);
定时器2:tone() ;
定时器对应Arduino Uno的PWM口:
定时器0:PWM口:5、6(默认976Hz)
定时器1:PWM口:9、10(默认490Hz)
定时器2:PWM口:11、3(默认490Hz)
注意:Uno板子的11脚不能同时使用PWM和SPI协议的功能
定时器对应Arduino Mega 2560的PWM口:
定时器0:PWM口:4、13
定时器1:PWM口:11、12
定时器2:PWM口:9、10
定时器3:PWM口:2、3、5
定时器4:PWM口:6、7、8
定时器5:PWM口:44、45、46
申明:本博客参考的其他博客:arduino 定时器、定时中断与PWM使用以及注意事项、[arduino 计时器和中断](arduino 计时器和中断_arduino timer-CSDN博客)、[【Arduino学习笔记】系列1 - 定时器配置](【Arduino学习笔记】系列1 - 定时器配置_tccr0b-CSDN博客)、[Arduino Uno 定时器中断 timer 0,1, 2 (自定义配置、MsTimer2、TimerOne库)](Arduino Uno 定时器中断 timer 0,1, 2 (自定义配置、MsTimer2、TimerOne库)-CSDN博客)、[AVR定时计数器1的控制寄存器说明](AVR定时计数器1的控制寄存器说明 - 百度文库 (baidu.com))。如有侵权,请联系删除
1.定时器相关的寄存器
8/16位的T/C 可以实现精确的程序定时( 事件管理) 、波形产生和信号测量。其主要特点如下:
- 真正的16位设计( 即允许16位的PWM)
- 2 个独立的输出比较单元
- 双缓冲的输出比较寄存器
- 一个输入捕捉单元
- 输入捕捉噪声抑制器
- 比较匹配发生时清除寄存器( 自动重载)
- 无干扰脉冲,相位正确的PWM
- 可变的PWM 周期
- 频率发生器
- 外部事件计数器
- 4 个独立的中断源(TOV1 、 OCF1A 、OCF1B与ICF1)
1.1 T/C1 控制寄存器 - TCCR1A、TCCR1B
Bit 7:6 – COM1A1:0: 通道A 的比较输出模式
Bit 5:4 – COM1B1:0: 通道B 的比较输出模式
COM1A1:0 与COM1B1:0 分别控制OC1A 与OC1B 状态。如果COM1A1:0 (COM1B1:0 )的一位或两位被写入"1”,FOC1A( FOC1B) 输出功能将取代 I/O 端口功能。此时 FOC1A(FOC1B)相应的输出引脚数据方向控制必须置位以使能输出驱动器。
OC1A(OC1B) 与物理引脚相连时, COM1x1:0 的功能由WGM13:0的设置决定。下表给出当WGM13:0设置为普通模式与CTC模式( 非PWM) 时COM1x1:0 的功能定义。
比较输出模式,非PWM
WGM11、WGM10及WGM13、WGM12:配置定时器工作模式;CS12、CS11、CS10:配置定时器的分频系数
WGM13:0设置为快速PWM 模式时COM1x1:0 的功能定义。比较输出模式,快速 PWM。比较输出模式,相位修正及相频修正 PWM 模式
Bit 3 – FOC1A: 通道A 强制输出比较
Bit 2 – FOC1B: 通道B 强制输出比较
FOC1A/FOC1B 只有当WGM13:0指定为非PWM模式时被激活。为与未来器件兼容,工作
在PWM 模式下对TCCR1A写入时,这两位必须清零。当 FOC1A/FOC1B 位置1 ,立即
强制波形产生单元进行比较匹配。COM1x1:0 的设置改变 OC1A/OC1B 的输出。注意FOC1A/FOC1B 位作为选通信号。COM1x1:0 位的值决定强制比较的效果。
在CTC模式下使用OCR1A 作为TOP值,FOC1A/FOC1B 选通即不会产生中断也不好清除定时器。FOC1A/FOC1B 位总是读为0。
波形产生模式的位描述
注: 1. CTC1 和 PWM11:0 的定义已经不再使用了,要使用WGM12:0 。但是两个版本的功能和位置是兼容的。
CNC1: 入捕捉噪声抑制器
置位ICNC1 将使能输入捕捉噪声抑制功能。此时外部引脚ICP1 的输入被滤波。其作用
是从ICP1 引脚连续进行4 次采样。如果4 个采样值都相等,那么信号送入边沿检测器。因此使能该功能使得输入捕捉被延迟了4 个时钟周期。
ICES1: 输入捕捉触发沿选择
该位选择使用ICP1 上的哪个边沿触发捕获事件。ICES 为"0” 选择的是下降沿触发输入捕捉;ICES1 为"1” 选择的是逻辑电平的上升沿触发输入捕捉。
按照ICES1 的设置捕获到一个事件后,计数器的数值被复制到 ICR1 寄存器。捕获事件还会置为ICF1。如果此时中断使能,输入捕捉事件即被触发。
当ICR1 用作TOP值(见TCCR1A与TCCR1B寄存器中WGM13:0位的描述 ) 时,ICP1
与输入捕捉功能脱开,从而输入捕捉功能被禁用。
时钟选择位描述
2.TCNT1H 与 TCNT1L寄存器
TCNT1H 与TCNT1L 组成了T/C1的数据寄存器TCNT1。通过它们可以直接对定时器/计数器单元的16位计数器进行读写访问。为保证CPU对高字节与低字节的同时读写,必须使用一个8 位临时高字节寄存器TEMP 。TEMP 是所有的16位寄存器共用的,在计数器运行期间修改TCNT1的内容有可能丢失一次TCNT1与OCR1x的比较匹配操作。写TCNT1寄存器将在下一个定时器周期阻塞比较匹配。
3.输出比较寄存器 1A - OCR1AH与 OCR1AL
4.输出比较寄存器 1B - OCR1BH与OCR1BL
该寄存器中的16位数据与TCNT1寄存器中的计数值进行连续的比较,一旦数据匹配,将产生一个输出比较中断,或改变OC1x 的输出逻辑电平。输出比较寄存器长度为16位。为保证CPU对高字节与低字节的同时读写,必须使用一个8 位临时高字节寄存器TEMP 。
5.输入捕捉寄存器 1 - ICR1H 与ICR1L
当外部引脚ICP1(或 T/C1的模拟比较器)有输入捕捉触发信号产生时,计数器TCNT1中的值写入ICR1 中。ICR1 的设定值可作为计数器的TOP值。输入捕捉寄存器长度为16位。为保证CPU对高字节与低字节的同时读写,必须使用一个8 位临时高字节寄存器TEMP 。
6.中断屏蔽寄存器- TIMSK
OCIE为比较匹配中断允许标志位,TOIE为溢出中断允许标志位。
OCIE1A、OCIE1B:比较匹配中断允许;TOIE1:溢出中断允许
7. 中断标志寄存器- TIFR
OCF为比较匹配中断标志位,TOV为溢出中断标志位
ICF1: T/C1 输入捕捉标志位
外部引脚ICP1 出现捕捉事件时ICF1 置位。此外,当ICR1 作为计数器的TOP值时,一旦计数器值达到TOP,ICF1 也置位。执行输入捕捉中断服务程序时ICF1 自动清零。也可以对其写入逻辑"1” 来清除该标志位。
实验程序:
1.time1的匹配模式(CTC)实现定时器的功能
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0; //先将整个TCCR0A寄存器设置为0
TCCR1B = 0; //先将整个TCCR0B寄存器设置为0
TCNT1 = 0; //将计数器值初始化为0
OCR1A = 31250; // compare match register 16MHz/(256*31250)= 2Hz 每0.5s执行一次中断服务函数
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…
}
2.time1的普通模式实现定时器的功能
#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...
}
3.通过time0/1/2输出PWM波(软件PWM,频率高了会波形不稳定)
int toggle0,toggle1,toggle2;
void setup(){
cli();//noInterrupts(); // disable all interrupts 关闭全局中断
/*
//设置定时器0为10kHz(100us)
TCCR0A = 0;//将整个TCCR0A寄存器设置为0
TCCR0B = 0;//将整个TCCR0B寄存器设置为0
TCNT0 = 0;//将计数器值初始化为0
//设置计数器为10kHZ,即100us
OCR0A = 24;//比较匹配寄存器= [16,000,000Hz /(预分频器*所需中断频率)] - 1
//比较匹配寄存器=24,中断间隔=100us即中断频率10khz
TCCR0A |= (1 << WGM01);//打开CTC模式
TCCR0B |= (1 << CS01) | (1 << CS00); //设置CS01位为1,CS00位为1(64倍预分频)
TIMSK0 |= (1 << OCIE0A);//启用定时器比较中断
*/
//设置定时器1为1kHz
TCCR1A = 0;//将整个TCCR1A寄存器设置为0
TCCR1B = 0;//将整个TCCR1B寄存器设置为0
TCNT1 = 0;//将计数器值初始化为0
//设置计数器为1kHZ,即1ms
OCR1A = 156;// = (16*10^6)/(1000*8) - 1 (must be <65536) 100hz/2
TCCR1B |= (1 << WGM12);//打开CTC模式
TCCR1B |= (1 << CS12)| (1 << CS10);// 1024分频 设置CS11位为1(8倍预分频)
TIMSK1 |= (1 << OCIE1A);
/*
//设置定时器2为8kHz
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 8khz increments
OCR2A = 249;// = (16*10^6) / (8000*8) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);//打开CTC模式
// Set CS21 bit for 8 prescaler
TCCR2B |= (1 << CS21);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
*/
sei();//interrupts(); // enable all interrupts //打开全局中断
}
/*
//中断0服务函数
ISR(TIMER0_COMPA_vect){// timer0中断2Hz切换引脚13(LED)
//产生频率为10kHz / 2 = 5kHz的脉冲波
if(toggle0){
digitalWrite(8,HIGH);
toggle0 = 0;
}
else{
digitalWrite(8,LOW);
toggle0 = 1;
}
}
*/
ISR(TIMER1_COMPA_vect){// timer1中断2Hz切换引脚13(LED) //中断优先级相同 3个中断中基本轮不到 执行不了
//产生频率为2Hz / 2 = 1Hz的脉冲波
/*
if(toggle1>=500)
digitalWrite(13,HIGH);
if(toggle1<=500)
digitalWrite(13,LOW);
toggle1 += 1;
if(toggle1 >= 1000)
toggle1 = 0;
*/
if(toggle1){
digitalWrite(13,HIGH);
toggle1 = 0;
}
else{
digitalWrite(13,LOW);
toggle1 = 1;
}
}
/*
ISR(TIMER2_COMPA_vect){// timer2中断8kHz切换引脚9
//产生频率为8kHz / 2 = 4kHz的脉冲波
if(toggle2){
digitalWrite(9,HIGH);
toggle2 = 0;
}
else{
digitalWrite(9,LOW);
toggle2 = 1;
}
}
*/
//loop function
void loop()
{
// your program here...
}
4.MsTimer2库的使用
#include <MsTimer2.h>
int led = 8;
void onTimer()
{
digitalWrite(LED_BUILTIN, HIGH);
delay(300);
digitalWrite(LED_BUILTIN, LOW);
delay(300);
}
void setup() {
Serial.begin(9600);
pinMode(led, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
MsTimer2::set(1000, onTimer); //设置中断,每1000ms进入一次中断服务程序 onTimer()
MsTimer2::start(); //开始计时_开启定时器中
}
void loop() {
digitalWrite(led, HIGH);
delay(1000);
digitalWrite(led, LOW);
delay(1000);
}
5.TimerOne的使用
#include <TimerOne.h>
# 使用 timer1 产生自定义载波频率下不同 pwm 占空比输出 和 定时器中断。
#include <TimerOne.h>
void setup()
{
pinMode(10, OUTPUT);
Timer1.initialize(500000); // initialize timer1, and set a 1/2 second period
Timer1.pwm(9, 512); // setup pwm on pin 9, 50% duty cycle
Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt
}
void callback()
{
digitalWrite(10, !digitalRead(10)); // 状态翻转
}
void loop()
{
// your program here...
}
6.Mega 2560板的time3
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR3A = 0;
TCCR3B = 0;
TCNT3 = 0;
OCR3A = 31250; // compare match register 16MHz/256/2Hz
TCCR3B |= (1 << WGM32); // CTC mode
TCCR3B |= (1 << CS32); // 256 prescaler
TIMSK3 |= (1 << OCIE3A); // enable timer compare interrupt
interrupts(); // enable all interrupts
}
ISR(TIMER3_COMPA_vect) // timer compare interrupt service routine
{
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); // toggle LED pin
}
void loop()
{
// your program here…
}
7.Mega 2560板的time4
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR4A = 0;
TCCR4B = 0;
TCNT4 = 0;
OCR4A = 31250; // compare match register 16MHz/256/2Hz
TCCR4B |= (1 << WGM42); // CTC mode
TCCR4B |= (1 << CS42); // 256 prescaler
TIMSK4 |= (1 << OCIE4A); // enable timer compare interrupt
interrupts(); // enable all interrupts
}
ISR(TIMER4_COMPA_vect) // timer compare interrupt service routine
{
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); // toggle LED pin
}
void loop()
{
// your program here…
}
8.Mega 2560板的time5
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR5A = 0;
TCCR5B = 0;
TCNT5 = 0;
OCR5A = 31250; // compare match register 16MHz/256/2Hz
TCCR5B |= (1 << WGM52); // CTC mode
TCCR5B |= (1 << CS52); // 256 prescaler
TIMSK5 |= (1 << OCIE5A); // enable timer compare interrupt
interrupts(); // enable all interrupts
}
ISR(TIMER5_COMPA_vect) // timer compare interrupt service routine
{
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); // toggle LED pin
}
void loop()
{
// your program here…
}