前言:
此刻大家对“中断定时器工作原理”大约比较看重,兄弟们都需要知道一些“中断定时器工作原理”的相关内容。那么小编也在网摘上收集了一些关于“中断定时器工作原理””的相关文章,希望朋友们能喜欢,朋友们一起来学习一下吧!要搞清楚什么是中断,我们可以从生活中的一个例子了解。
你正在家里看电视,突然家里座机响了,你起身去接电话,接完电话后继续回来看电视。
这就是生活中的中断现象,也就是一个正在做的事情被外部的事情打断,当执行完外部事情后,继续做原本的事情。
什么是中断?
所谓中断,是指CPU在正常运行程序时,由于内部/外部事件或由程序预先安排的事件,引起CPU中断正在运行的程序,而转到为内部/外部事件或为预先安排的事件服务的程序中去,服务完毕,再返回去执行波暂时中断的程序。
由于某个事件的发生,CPU暂停当前正在执行的程序,转而执行处理该事件的一个程序。该程序执行完成后,CPU接着执行被暂停的程序。这个过程称为中断。
根据中断源的位置,有两种类型的中断。有的中断源在CPU的内部,称为内部中断。大多数的中断源在CPU的外部,称为外部中断。
根据中断引脚的不同,或者CPU响应中断的不同条件,也可以把中断划分为可屏蔽中断和不可屏蔽中断两种。
使用中断的好处
1、 实行分时操作 提高 CPU 的效率 只有当服务对象向 CPU 发出中断申请时 才去为它服务 这样 我们就可以利用中断功能同时为多个对象服务 从而大大提高了 CPU 的工作效率
2、 实现实时处理
利用中断技术,各个服务对象可以根据需要随时向 CPU 发出中断申请,及时发现和处理中断请求。
进入正题
我们在本次试验中使用ocrobot mango(Arduino UNO兼容板),IDE使用1.0.1版本。
在OCROBOT MANGO(UNO也一样)中,有两个可以使用的外部中断,0(数字引脚2)和1(数字引脚3),本次例子中我们使用D2引脚作为中断引脚,使用A0引脚控制LED。
标准的数字输入与输出--没有中断
如果你按照上面的电路图进行了接线,把下面的代码编译上传进入Arduino。
代码用来读取你输入的值,然后作为条件进行比较,
int pbIn = 2; // 定义输入信号引脚
int ledOut = A0; // 定义输出指示灯引脚
int state = LOW; // 定义默认输入状态
void setup()
{
// 设置输入信号引脚为输入状态、输出引脚为输出状态
pinMode(pbIn, INPUT);
pinMode(ledOut, OUTPUT);
}
void loop()
{
state = digitalRead(pbIn); //读取微动开关状态
digitalWrite(ledOut, state); //把读取的状态赋予LED指示灯
//模拟一个长的流程或者复杂的任务
for (int i = 0; i < 100; i++)
{
//延时10毫秒
delay(10);
}
}
下面视频是该代码的实验效果,按下开关,LED状态不会立刻改变,要按住一会儿才能改变。
使用中断
现在,我们使用相同的电路连接图,然后修改代码以使用硬件中断。然后把下面的代码下载进入控制板中,当按下按钮时,LED的状态就会立刻改变,尽管代码仍然是在主循环中,而且是同样的延时。
int pbIn = 0; // 定义中断引脚为0,也就是D2引脚
int ledOut = A0; // 定义输出指示灯引脚
volatile int state = LOW; // 定义默认输入状态
void setup()
{
// 置ledOut引脚为输出状态
pinMode(ledOut, OUTPUT);
// 监视中断输入引脚的变化
attachInterrupt(pbIn, stateChange, CHANGE);
}
void loop()
{
// 模拟长时间运行的进程或复杂的任务。
for (int i = 0; i < 100; i++)
{
// 什么都不做,等待10毫秒
delay(10);
}
}
void stateChange()
{
state = !state;
digitalWrite(ledOut, state);
}
下面视频是该代码的实验效果,按下开关,LED状态就会立刻变化。
中断命令语法介绍
attachInterrupt(interrupt, function, mode)
interrupt:中断引脚数
function:中断发生时调用的函数,此函数必须不带参数和不返回任何值。该函数有时被称为中断服务程序。
mode:定义何时发生中断,以下四个contstants预定有效值:
LOW 当引脚为低电平时,触发中断
CHANGE 当引脚电平发生改变时,触发中断
RISING 当引脚由低电平变为高电平时,触发中断
FALLING 当引脚由高电平变为低电平时,触发中断.
当发生外部中断时,调用一个指定函数。当中断发生时,该函数会取代正在执行的程序。大多数的Arduino板有两个外部中断:0(数字引脚2)和1(数字引脚3)。
arduino Mega有四个外部中断:数字2(引脚21),3(20针),4(引脚19),5(引脚18)。
注意事项
当中断函数发生时,delya()和millis()的数值将不会继续变化。当中断发生时,串口收到的数据可能会丢失。你应该声明一个变量来在未发生中断时储存变量。
重新分配中断
中断可以再任何时候通过attachInterrupt()命令进行改变。当重新使用attachInterrupt()时,先前分配的中断就会从对应引脚上移除。
启用\停止中断
Arduino也可以忽略所有中断。如果你需要在一段代码中不执行中断,只需要执行 noInterrupts()命令。当这段代码执行完以后,你可以使用 interrupts()命令重新启用中断。
删除中断
终端也可以通过detachInterrupt(interrupt_number)命令进行删除。
定时器中断
定时器的作用
定时器对于单片机来说就类似我们现实生活中的时钟,记录很多和时间相关的事件。在我们平时经常使用的 delay() millis() ,micros() ,delayMicroseconds() ,PWM 波生成的 analogWrite() 和 tone() 函数都是通过定时器实现的,不过这些都被 Arduino 的封装库隐藏起来了,为了让使用者更快更便捷地开发项目。
我们平常使用的 Arduino 单片机为 UNO,NANO和MEGA 2560。UNO 和 NANO 都使用的是 ATmega328 芯片,这款芯片有3个定时器,Timer0,Timer1,Timer2,其中Timer0和Timer2都是8位寄存器(256),Timer1是16位寄存器(65536),意味着更高的分辨率。mege2560 使用的是 ATmege2560 芯片,这款芯片有 6 个定时器,在328 的基础上,增加了 Timer3,Timer4,Timer5。这三个定时器都是16位的寄存器。
定时器的基本概念
1、寄存器
寄存器列表如下,x代表0,1,2,3,4,5这6种定时器。
寄存器 作用
TCCRx:定时器/计数器控制寄存器;预分频器可以在这里配置
TCNTx: 定时器/计数器寄存器
OCRx: 输出比较寄存器
ICRx: 输入捕捉寄存器(仅适用于16位定时器)
TIMSKx: 定时器/计数器中断屏蔽寄存器;启用/禁用定时器中断
TIFRx: 定时器/计数器中断标志寄存器;表示挂起的定时器中断
预分频系数与比较匹配器
Arduino UNO 时钟以16MHz运行。计数器的一个刻度值表示 1 / 16,000,000秒(~63ns),跑完1s需要计数值16,000,000。
(1)Timer0 和 Timer2 是8位定时器,可以存储最大计数器值255。
(2)Timer1 是一个16位定时器,可以存储最大计数器值65535。
一旦计数器达到其最大值,它将回到零(这称为溢出)。因此,需要对时钟频率进行分频处理,即预分频器。通过预分频器控制定时计数器的增量速度。预分频器与定时器的计数速度如下:
定时器速度(HZ) = Arduino UNO时钟速度(16MHz) / 预分频器系数
因此,1预分频器将以16MHz递增计数器,8预分频器将在2MHz递增,64预分频器= 250kHz,依此类推。
现在您可以用以下公式计算中断频率:
中断频率(Hz)=(Arduino时钟速度16MHz)/(预分频器*(比较匹配寄存器+ 1))
重新排列上面的等式,给出你想要的中断频率,你可以求解比较匹配寄存器值:
比较匹配寄存器= [16,000,000Hz /(预分频器*所需的中断频率)] - 1
当你使用定时器0和2时,这个数字必须小于256,对于timer1小于65536。
所以如果你想每秒一次中断(频率为1Hz):
比较匹配寄存器= [16,000,000 /(预分频器 * 1)] -1
预分频器为1024,你得到:
比较匹配寄存器= [16,000,000 /(1024 * 1)] -1 = 15,624
因为256 <15,624 <65,536,你必须使用timer1来实现这个中断。
3、定时器模式
定时器可以配置为不同的模式。
(1)PWM模式。 脉冲宽度调制模式。 OCxy输出用于生成PWM信号。
(2)CTC模式。 比较匹配时清除计时器。 当定时器计数器到达比较匹配寄存器时,定时器将被清除。
定时器的配置
请注意不同型号板子对应配置不同,详细配置请看技术文档。
int toggle0,toggle1,toggle2;
void setup(){
cli();关闭全局中断
//设置定时器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 = 1999;// = (16*10^6)/(1000*8) - 1 (must be <65536)
TCCR1B |= (1 << WGM12);//打开CTC模式
TCCR1B |= (1 << CS11);//设置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();//打开全局中断
}
//中断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)
//产生频率为2Hz / 2 = 1Hz的脉冲波
if(toggle1>=500)
digitalWrite(13,HIGH);
if(toggle1<=500)
digitalWrite(13,LOW);
toggle1 += 1;
if(toggle1 >= 1000)
toggle1 = 0;
}
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(){
}
定时器中断的使用
使用定时器中断前,必须先安装对应的 Timer 库,然后导入到 Arduino 的库文件里,并在程序中引用头文件 Timer.h 。
实例:
// 秒切换一次引脚13的电平
// 包含定时器库的头文件
#include <FlexiTimer2.h>
// 中断服务程序
void flash() {
static boolean output = HIGH;
digitalWrite(13, output);
output = !output;
}
void setup() {
pinMode(13, OUTPUT);
FlexiTimer2::set(500, flash); // 中断设置函数,每 500ms 进入一次中断
FlexiTimer2::start(); // 开始计时
}
void loop() {
}
标签: #中断定时器工作原理