跳转到主要内容

AVR开发 Arduino方法(二) 中断子系统

judy 提交于

在了解中断子系统之前,首先要了解中断的概念。你正在看书,这时电话响了,你会怎么做呢?相信大多数人会这样:先标记看到的位置,接完电话回来后继续阅读。这就是一个现实生活中中断的例子,我们把“电话响了”成为中断源。Arduino UNO R3的主处理器ATMega328P拥有26个中断源,如下表所示:

<table border="1" style="width: 586px;">
<tbody>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center"><strong>向量号</strong></p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p align="center"><strong>程序地址</strong></p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p align="center"><strong>中断源</strong></p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p align="center"><strong>中断定义</strong></p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p align="center"><strong>中断服务程序名称</strong></p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">1</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0000</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>RESET</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>外部电平复位,上电复位,掉电检测复位,看门狗复位</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>&nbsp;</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">2</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0002</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>INT0</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>外部中断请求0</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>INT0_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">3</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0004</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>INT1</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>外部中断请求1</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>INT1_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">4</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0006</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>PCINT0</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>引脚电平变化中断请求0</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>PCINT0_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">5</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0008</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>PCINT1</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>引脚电平变化中断请求1</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>PCINT1_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">6</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x000A</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>PCINT2</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>引脚电平变化中断请求2</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>PCINT2_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">7</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x000C</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>WDT</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>看门狗溢出中断</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>WDT_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">8</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x000E</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TIMER2&nbsp;COMPA</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>定时/计数器2比较匹配A</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TIMER2_COMPA_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">9</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0010</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TIMER2&nbsp;COMPB</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>定时/计数器2比较匹配B</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TIMER2_COMPB_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">10</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0012</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TIMER2&nbsp;OVF</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>定时/计数器2溢出</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TIMER2_OVF_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">11</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0014</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TIMER1&nbsp;CAPT</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>定时/计数器1事件捕捉</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TIMER1_CAPT_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">12</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0016</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TIMER1&nbsp;COMPA</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>定时/计数器1比较匹配A</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TIMER1_COMPA_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">13</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0018</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TIMER1&nbsp;COMPB</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>定时/计数器1比较匹配B</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TIMER1_COMPB_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">14</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x001A</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TIMER1&nbsp;OVF</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>定时/计数器1溢出</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TIMER1_OVF_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">15</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x001C</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TIMER0&nbsp;COMPA</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>定时/计数器0比较匹配A</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TIMER0_COMPA_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">16</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x001E</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TIMER0&nbsp;COMPB</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>定时/计数器0比较匹配B</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TIMER0_COMPB_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">17</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0020</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TIMER0&nbsp;OVF</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>定时/计数器0溢出</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TIMER0_OVF_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">18</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0022</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>SPI&nbsp;STC</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>SPI串行传输结束</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>SPI_STC_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">19</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0024</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>USART&nbsp;RX</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>USART接收结束</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>USART_RX_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">20</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0026</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>USART&nbsp;UDRE</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>USART数据寄存器空</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>USART_UDRE_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">21</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0028</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>USART&nbsp;TX</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>USART,发送结束</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>USART_TX_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">22</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x002A</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>ADC</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>模数转换结束</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>ADC_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">23</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x002C</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>EE&nbsp;READY</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>EEPROM准备好</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>EE_READY_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">24</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x002E</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>ANALOG&nbsp;COMP</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>模拟比较器</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>ANALOG_COMP_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">25</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0030</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>TWI</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>两线串行接口</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>TWI_vect</p>
</td>
</tr>
<tr>
<td style="width: 19px;" valign="center" width="60">
<p align="center">26</p>
</td>
<td style="width: 85px;" valign="center" width="73">
<p>0x0032</p>
</td>
<td style="width: 84px;" valign="center" width="74">
<p>SPM&nbsp;READY</p>
</td>
<td style="width: 190px;" valign="center" width="231">
<p>保存程序存储器内容就绪</p>
</td>
<td style="width: 148px;" valign="center" width="130">
<p>SPM_ready_vect</p>
</td>
</tr>
</tbody>
</table>

这里以外部中断0为例了解对中断子系统的编程,沿用上一章中用于数字输入示例的电路,这个示例使得按键在按下时LED的状态取反:

<pre style="overflow-x:auto; background-color:#e9e9e9;">// Interrupt.ino
const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;

void setup() {
pinMode(ledPin, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}

void loop() {
digitalWrite(ledPin, state);
}

void blink() {
state = !state;
}</pre>

与外部中断相关的Arduino库函数有:

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode):启用指定引脚的外部中断并连接到指定中断服务程序

pin:指定外部中断的引脚

ISR:指定中断服务程序的名称

mode:LOW(低电平触发中断),CHANG(逻辑电平变化触发中断),RISING(上升沿触发中断)或FALLING(下降沿触发中断)

detachInterrupt(digitalPinToInterrupt(pin)):禁用指定中断

pin:指定取消外部中断的引脚

interrupts():开启总中断

noInterrupts():禁用总中断

ATMega328P的外部中断由2个相关寄存器控制,外部中断控制寄存器EICRA的结构如下图所示:
<table border="1">
<tbody>
<tr>
<td valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td colspan="2" valign="top" width="162">
<p align="center">INT1</p>
</td>
<td colspan="2" valign="top" width="162">
<p align="center">INT0</p>
</td>
</tr>
<tr>
<td valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td valign="top" width="81">
<p align="center">ISC11</p>
</td>
<td valign="top" width="81">
<p align="center">ISC10</p>
</td>
<td valign="top" width="81">
<p align="center">ISC01</p>
</td>
<td valign="top" width="81">
<p align="center">ISC00</p>
</td>
</tr>
</tbody>
</table>

ISCx[1:0](x = 0, 1)位用于设置外部中断的触发方式,如下表所示:
<table border="1" style="width: 257px;">
<tbody>
<tr>
<td style="width: 101px;" valign="center" width="91">
<p align="center"><strong>ISCx[1:0]</strong></p>

<p align="center"><strong>(x&nbsp;=&nbsp;0,&nbsp;1)</strong></p>
</td>
<td style="width: 140px;" valign="center" width="476">
<p align="center"><strong>外部中断触发方式</strong></p>
</td>
</tr>
<tr>
<td style="width: 101px;" valign="center" width="91">
<p align="center">00</p>
</td>
<td style="width: 140px;" valign="center" width="476">
<p align="justify">低电平</p>
</td>
</tr>
<tr>
<td style="width: 101px;" valign="center" width="91">
<p align="center">01</p>
</td>
<td style="width: 140px;" valign="center" width="476">
<p align="justify">逻辑电平变化</p>
</td>
</tr>
<tr>
<td style="width: 101px;" valign="center" width="91">
<p align="center">10</p>
</td>
<td style="width: 140px;" valign="center" width="476">
<p align="justify">下降沿</p>
</td>
</tr>
<tr>
<td style="width: 101px;" valign="center" width="91">
<p align="center">11</p>
</td>
<td style="width: 140px;" valign="center" width="476">
<p align="justify">上升沿</p>
</td>
</tr>
</tbody>
</table>

外部中断屏蔽寄存器EIMSK用于设置是否屏蔽外部中断,它的结构如下图所示:
<table border="1" style="width: 385px;">
<tbody>
<tr>
<td style="width: 40px;" valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td style="width: 40px;" valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td style="width: 40px;" valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td style="width: 40px;" valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td style="width: 40px;" valign="top" width="81">
<p align="center">&nbsp;</p>
</td>
<td style="width: 70px;" valign="top" width="81">
<p align="center">INT1</p>
</td>
<td style="width: 69px;" valign="top" width="81">
<p align="center">INT0</p>
</td>
</tr>
</tbody>
</table>
若向其中某位写入1,则该位控制的外部中断启用;写入0则禁用。

通过直接访问寄存器改写以上程序为:
<pre style="overflow-x:auto; background-color:#e9e9e9;">// Interrupt_reg.ino
volatile byte state = LOW;

void setup() {
DDRB |= (1 &lt;&lt; PB5);

DDRD &= ~(1 &lt;&lt; PD2);
PORTD |= (1 &lt;&lt; PD2);
EICRA &= ~(1 &lt;&lt; ISC01) & ~(1 &lt;&lt; ISC00);
EIMSK |= (1&lt;&lt; INT0);
sei(); // 启用总中断
}

void loop() {
if (state == HIGH) {
PORTB |= (1 &lt;&lt; PB5);
} else {
PORTB &= ~(1 &lt;&lt; PB5);
}
}

// 外部中断0中断处理函数
ISR(INT0_vect) {
state = !state;
}</pre>

转自:<a href="http://www.cnblogs.com/lets-blu/p/7513709.html">Lets_Blu</a&gt;