原理图
什么是中断?
为微控制器编写的简单程序通常都可以在主函数内部完成,并且几乎不需要使用外设。但是,大多数其他微控制器程序更复杂,需要大量代码。当发生这种情况时,中断会变得非常有用,但究竟什么是中断?
想象一下,我们的微控制器需要同时做两件事:准确跟踪时间并使LED闪烁。我们的程序可以通过重置计时器,递增计数器,然后等待计时器溢出来开始。完成后,我们的代码可以使LED闪烁。虽然这有点完成工作,但是有两个问题。 CPU花费大部分时间坐在延迟循环中,这浪费了CPU时间,并且LED的执行时间很难计算。
那么,我们如何解决这个问题呢?我们可以在计时器上使用中断!因此,我们不是在主代码中递增计数器,而是将代码转换为处理时序的中断服务程序。
通常,微控制器将运行LED闪烁代码,但是一旦定时器生成中断请求,微控制器停止LED闪烁代码,执行定时器中断服务程序,然后返回到LED闪烁代码。这样,LED闪烁代码不会干扰我们的定时器代码,它可以更准确(并且更容易)跟踪时间。
AVR Core上的中断
AVR有一个向量表,每个中断源都跳转到一个唯一的地址。这是非常有利的,因为我们不再需要执行比较来查看触发了哪个中断,这可能需要一些时间。
下表显示了Atmega168上可用的不同中断以及它们跳转到的地址。程序记忆。但是,在我们使用它们之前必须配置几个中断选项。
从ATmega168数据表中提取
表位置
Atmega168具有允许的引导加载程序区域它可以动态地重写自己的程序存储器,这对固件更新很有用。因此,ISR向量表将位于内存中很重要。如果表位于引导加载程序区域中,则在启用引导加载程序时永远不会更新(不推荐)。
因此,如果没有引导加载程序,则应将向量表放在内存的底部(接近地址0x0000),但如果使用引导加载程序,则应将向量表移动到引导加载程序上方。这可以通过改变MCUCR寄存器中的几个位来轻松完成。
如果IVSEL = 0,则ISR位于向量表的起始,否则ISR驻留在引导加载程序中。现在,将其保留为0,因为我们没有使用引导加载程序
如果IVCE = 1,则执行ISR切换。暂时保留为0
中断启用位
每个中断源(I/O引脚,外设等)都有关联中断使能位。与PIC类似,STATUS寄存器中有一个全局中断使能位,需要将其设置为允许中断工作。要找出这些中断标志所在的位置,需要参考数据手册中的特定外设章节。
例如,我们将在定时器0上使用溢出中断,所以如果我们看一下定时器0在章节中,我们发现中断使能位位于TIMSK0寄存器(第89页)中,称为TOIE0。需要将此位设置为1才能触发定时器溢出。该寄存器还有另外两个中断源,A匹配溢出和B匹配溢出,这对PWM功能很有用(将来会介绍)。
注意,设置我在SREG中的位不是使用SREG本身,而是使用函数sei();设置I位和cei();清除I位。
在WinAVR中编写ISR
所以我们现在明白需要启用中断才能启动,但我们如何使用C和WINAVR编译器编写?答案很简单:我们使用特殊保留字ISR并传递中断名称参数来告诉编译器哪个中断函数处理。注意我们需要包含中断头文件,否则中断函数将不起作用!
#include
ISR(TIMER0_OVF_vect)
{
// Interestingly, the AVR automatically clears interrupt flags.。。.unlike the PIC
// Put your code here
}
简单闪烁示例
在这个例子中,ATmega168会使连接到PD0的LED频繁闪烁,其中闪烁的速率受到控制通过定时器0但是,您可能会注意到主功能为空,并且LED在定时器溢出中断服务程序(ISR)内闪烁。这意味着我们可以在while循环中放入我们想要的任何代码,并且该代码不会阻止中断运行。
/*
* AVR Interrupt.c
*
* Created: 09/01/2018
* Author : RobinLaptop
*/
// These are really useful macros that help to get rid of unreadable bit masking code
#define setBit(reg, bit) (reg = reg | (1 《《 bit))
#define clearBit(reg, bit) (reg = reg & ~(1 《《 bit))
#define toggleBit(reg, bit) (reg = reg ^ (1 《《 bit))
#define clearFlag(reg, bit) (reg = reg | (1 《《 bit))
#include
#include
ISR(TIMER0_OVF_vect)
{
// Interestingly, the AVR automatically clears interrupt flags =) 。。..unlike the PIC =(
// Toggle the LED (PD0 , Pin 2)
toggleBit(PORTD, PD0);
}
int main(void)
{
// Initialize Registers
clearBit(TCCR0A, WGM00); // Configure WGM to be 0x00 for normal mode
clearBit(TCCR0A, WGM01);
clearBit(TCCR0B, WGM02);
setBit(TCCR0B, CS00); // Configure clock source to be clock io at 1024 pre-scale
clearBit(TCCR0B, CS01);
setBit(TCCR0B, CS02);
DDRD = 0xFF; // Make PORT D and output
sei(); // Enable interrupts
setBit(TIMSK0, TOIE0); // Enable the timer interrupt
while (1)
{
// Put any code you want here
// It should not affect the interrupt service routine!
}
}
结论
本教程仅涵盖单个中断,即定时器0溢出中断,但它清楚地表明中断是非常强大。如果使用得当,您可以拥有一个系统,它可以在信号到达时立即响应并暂停主代码。这可以用来做很多事情,包括多任务处理,不同外围设备的多重处理,以及创建实时代码!
-
中断
+关注
关注
5文章
899浏览量
41561
发布评论请先 登录
相关推荐
评论