龙空技术网

Arduino中断的原理:外部中断和定时器中断

望天空云卷云又舒 66

前言:

此刻大家对“中断定时器工作原理”大约比较看重,兄弟们都需要知道一些“中断定时器工作原理”的相关内容。那么小编也在网摘上收集了一些关于“中断定时器工作原理””的相关文章,希望朋友们能喜欢,朋友们一起来学习一下吧!

要搞清楚什么是中断,我们可以从生活中的一个例子了解。

你正在家里看电视,突然家里座机响了,你起身去接电话,接完电话后继续回来看电视。

这就是生活中的中断现象,也就是一个正在做的事情被外部的事情打断,当执行完外部事情后,继续做原本的事情。

什么是中断?

所谓中断,是指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() {

}

标签: #中断定时器工作原理