AVR单片机教程——定时器中断

发布者:平稳心绪最新更新时间:2020-07-22 来源: 51hei关键字:AVR  单片机教程  定时器中断 手机看文章 扫描二维码
随时随地手机看文章

本文隶属于AVR单片机教程系列。


中断,是单片机的精华。


中断基础

当一个事件发生时,CPU会停止当前执行的代码,转而处理这个事件,这就是一个中断。触发中断的事件成为中断源,处理事件的函数称为中断服务程序(ISR)。


中断在单片机开发中有着举足轻重的地位——没有中断,很多功能就无法实现。比如,在程序干别的事时接受UART总线上的输入,而uart_scan_char等函数只会接收调用该函数后的输入,先前的则会被忽略。利用中断,我们可以在每次接受到一个字节输入时把数据存放到缓冲区中,程序可以从缓冲区中读取已经接收的数据。


AVR单片机支持多种中断,包括外部引脚中断、定时器中断、总线中断等。每一个中断被触发时,通过中断向量表跳转到对应ISR。如果一个中断对应的ISR不存在,链接器会把复位地址放在那里,如果这个中断被响应程序就会复位(但单片机不会复位)。


那么,我们以前从未写过ISR,但经常改变引脚电平,为什么没有复位呢?因为中断默认是不开启的。要启用一个中断,需要让两个位于不同寄存器中的位为1,一个是中断对应的中断使能位,每个中断都有各自的位,另一个是全局中断使能位,位于寄存器SREG中,不能直接存取,需要通过定义在头文件中的sei()函数开全局中断,相对地,cli()用于关全局中断。


先来写第一个带中断的程序吧。从原理图中可以看到,PB2旁边标明了INT2,表示PB2引脚可用于外部中断2。把一个按键连接到PB2引脚上,即开发板最下方的7P排母的最右边。利用中断,我们实现每按一次按键就翻转LED状态的功能。


#include

#include


int main()

{

    PORTB |=    1 << PORTB2;

    EICRA |= 0b10 << ISC20;

    EIMSK |=    1 << INT2;

    DDRC  |=    1 << DDC4;

    sei();

    while (1)

        ;

}


ISR(INT2_vect)

{

    PORTC ^= 1 << PORTC4;

}


ISC21:0两位指定外部中断的类型,这里设置为下降沿,即按键按下时触发;INT2位使能外部中断2;全部初始化完成后,sei()启用全局中断,然后单片机就会相应按键按下的事件了。


ISR(INT2_vect)指示这个函数是外部中断2的ISR。每个中断ISR都有自己的名字,由数据手册12章Source一栏的内容加上_vect组成,这个名字可以当成函数名字来使用。


如果多个中断同时触发,单片机会先响应优先级高的。一些单片机支持自定义的优先级,但在AVR单片机中,只有简单的地址低的优先级高的规则。


中断可以被中断吗?在AVR单片机中,执行一个中断处理函数会自动地关闭全局中断,此时程序不会被中断,但可以手动地sei()使中断可以被处理。程序是否相应中断仅取决于该中断是否被启用,与其优先级无关。


当然,中断不是完美的。其一,你也许已经发现上面的程序不能很好的工作,有时候明明按下了按键,灯却一闪就灭。这是因为,按键存在抖动,比单片机时钟周期长,能触发多个中断。以前把button_down()放在main函数的while循环里时就没有这个问题,正是循环中的delay滤除了这种抖动。


其二,进入和退出中断,除了需要CPU几个周期来改变PC(程序计数器,当前执行指令的地址)外,还需要保护和恢复现场,包括SREG寄存器与ISR中用到的通用寄存器。下面这段汇编代码可以在Solution Explorer中Output Filesxxx.lss中找到。


00000094 <__vector_3>:

#include

#include

ISR(INT2_vect)

{

  94:   1f 92           push    r1

  96:   0f 92           push    r0

  98:   0f b6           in  r0, 0x3f    ; 63

  9a:   0f 92           push    r0

  9c:   11 24           eor r1, r1

  9e:   8f 93           push    r24

  a0:   9f 93           push    r25

    PORTC ^= 1 << PORTC4;

  a2:   98 b1           in  r25, 0x08   ; 8

  a4:   80 e1           ldi r24, 0x10   ; 16

  a6:   89 27           eor r24, r25

  a8:   88 b9           out 0x08, r24   ; 8

}

  aa:   9f 91           pop r25

  ac:   8f 91           pop r24

  ae:   0f 90           pop r0

  b0:   0f be           out 0x3f, r0    ; 63

  b2:   0f 90           pop r0

  b4:   1f 90           pop r1

  b6:   18 95           reti


这段代码不必理解,更不用会写。94到a0行是保护现场,依次将寄存器r1、r0、SREG(即0x3f)、r24和r25push进栈,把r1清零,一共用了12个周期,还要加上响应中断的4个周期;a2到a8是恢复现场,把这些寄存器原来的值逆序地从栈上pop出来,用了15个周期;而只有中间aa到b6的语句是用于执行用户代码的,在总共35个周期中只占4个周期。


当然,这个比例很小是因为这个ISR过于简单。但是,ISR更复杂也意味着有更多寄存器需要push和pop,中断的响应时间更长。


这个例子并没有中断效率低下的意思,而是表明不能过于频繁地依赖中断。比如接下来要讲的定时器中断,我通常设置为1ms间隔,只有一次到0.1ms,再快恐怕就起不到定时的作用了。


定时器中断

定时器,顾名思义,定时用的。之前我们在main函数的while (1)循环中,每个周期执行一些代码,然后延时一个固定的时长。我也曾见过根据该次周期的工作量来计算延时时长的操作,但毕竟写BASIC的人学得也basic吧,这种做法的定时仍不精确。利用定时器中断(其实不必中断),我们可以实现精确的定时,使每一周期的时间严格相同。


如果对操作系统有一点了解,就会知道操作系统需要进行任务调度。然而,任务在执行时,并不知道自己该何时被调度走。实际上,是操作系统在定时器中断中打断了任务的正常执行,然后进行调度。定时器中断是操作系统的基础。


在AVR单片机定时器的各种模式中,普通模式和CTC模式常用于产生定时器中断。我们仍然以定时/计数器0为例。


在普通模式中,使用TIMER0_OVF中断,频率为(frac {f_{CPU}} {256 cdot N}),(N)为分频系数。这样产生的定时器中断精确但不确切,因为N的取值是很离散的。如果只需要在中断中进行外设轮询的话,普通模式就足够了。


如果在ISR的第一行就给TCNT0赋值,或是使用TIMER0_COMPA中断并在起始处写TCNT0 = 0,那么可以改变中断频率,但由于有编译器插入的保护现场的代码的存在,这种定时不够精确,而CTC模式解决了这个问题。


在CTC模式中,使用TIMER0_COMPA中断,频率精确地为(frac {f_{CPU}} {N cdot (OCR0A + 1)})(注意没有蜂鸣器频率公式中的(2))。


还需要提醒一句,如果想要中断被响应,必须保证main函数不退出,因为编译器会在退出处加上一句cli()。最简单的方法是在main函数的最后加上一句while (1);。


后台动态扫描

数码管的动态扫描需要每隔一段时间就换一位点亮是一件很烦人的事,尤其是在操控其他外设的程序已经比较复杂的时候。我本来想把中断完美地拖到第二期再讲,没想到自己也受不了动态扫描的折磨,在某个版本的库中就放出了segment_auto函数来接管这项工作。它正是使用了定时器中断。


实现思路很简单,把要显示的数据放在客户和库可以共同取用的变量中,在中断里逐位显示,只要中断够快,就可以实现动态扫描,使每一位看起来都在亮。


#include

#include

#include


void segment_int_init()

{

    // other initializations, ex. pins

    TCCR0A = 0b10 << WGM00; // CTC mode

    TCCR0B = 0b0 << WGM02 | 0b100 << CS00; // divide by 256

    OCR0A = 97; // ~1ms

    TIMSK0 = 1 << OCIE0A; // compare match A interrupt

    sei();

}


static uint8_t segment_int_data[SEGMENT_DIGIT_COUNT];


void segment_int_display(/* ... */)

{

    // store the display pattern in segment_int_data

}


ISR(TIMER0_COMPA_vect)

{

    static uint8_t cur = 0;

    // display the cur-th digit according to segment_int_data

    if (++cur == SEGMENT_DIGIT_COUNT)

        cur = 0;

}


如果你把以上代码放在可执行程序的项目中,那完全没有问题,但如果是放在一个静态库项目中,然后在可执行程序项目中引用它,那么定时器中断的ISR是不会链接进程序的。这是因为,从链接器的角度来讲,这个ISR从来没有被调用过,因此就被当成无用的函数扔掉了。为了让链接器把ISR链接进程序,我们需要在main会执行的代码中调用它,最简单地:


if (0)

    TIMER0_COMPA_vect();

放在初始化中,既达到了目的,又没有运行时的负担。

关键字:AVR  单片机教程  定时器中断 引用地址:AVR单片机教程——定时器中断

上一篇:AVR第6课:数码管显示
下一篇:linux 下面avr开发环境的安装

推荐阅读最新更新时间:2024-11-16 20:54

ATmega8 简介
ATmega8是基于增强的AVR RISC结构的低功耗8位CMOS微控制器。由于其先进的指令 集以及单时钟周期指令执行时间, ATmega8 的数据吞吐率高达1 MIPS/MHz,从而可以 缓减系统在功耗和处理速度之间的矛盾。 ATmega8 AVR 内核具有丰富的指令集和32 个通用工作寄存器。所有的寄存器都直接与算逻单元 (ALU) 相连接,使得一条指令可以在一个时钟周期内同时访问两个独立的寄存器。这种结 构大大提高了代码效率,并且具有比普通的CISC 微控制器最高至10 倍的数据吞吐率。 ATmega8 有如下特点:8K 字节的系统内可编程Flash( 具有同时读写的能力,即RWW), 512 字节 EEPROM,1
[单片机]
基于AVR芯片的光照和CO2控制系统分析
一、项目概述 1.1 引言 温室是一种可以改变植物生长环境、为植物生长创造最佳条件、避免外界四季变化和恶劣气候对其影响的场所。它以采光覆盖材料作为全部或部分结构材料,可在冬季或其他不适宜露地植物生长的季节栽培植物。温室生产以达到调节产期,促进生长发育,防治病虫害及提高质量、产量等为目的。而温室设施的关键技术是环境控制,该技术的最终目标是提高控制与作业精度。随着农业现代化的发展,设施农业工程因其涉及学科广、科技含量高、与人民生活关系密切,己越来越受到世界各国的重视。这也为我国大型现代化温室的发展提供了极好的机遇,并产生巨大的推动作用。本项目以AVR芯片为控制芯片,设计了一套适用于当前西瓜生产的光照和二氧化碳浓度控制系统。 1.2
[单片机]
基于<font color='red'>AVR</font>芯片的光照和CO2控制系统分析
基于AVR单片机多任务嵌入式Internet系统设计
1 引言 目前,嵌入式系统已经广泛渗透到人们的工作、生活中。从家用电器、信息终端、手持通信设备到仪器仪表、制造工业、过程控制等领域,嵌入式设备已随处可见。另一方面,近几年来Internet技术的飞速发展给嵌入式应用带来了新的契机,在未来嵌入式系统中应用Internet技术具有很大的优势。 目前嵌入式Internet技术的实现主要有下面三种方式 : 第一种方式是EMIT技术,采用支持TCP/IP协议的高性能服务器作为网关(emGateway), 嵌入式设备通过RS-232、RS-485或者CAN总线等与网关服务器连接,间接通过服务器网关连接Internet。经过多年的发展EMIT技术已经在工业设备的网络化中得到了广
[单片机]
基于<font color='red'>AVR</font><font color='red'>单片机</font>多任务嵌入式Internet系统设计
AVR开发 Arduino方法(附二) 故障排除:烧录引导程序
在“内存子系统”一章中我们曾提到,Arduino UNO R3开发板上的ATMega328P有0.5KB的Flash空间用于引导程序;因为有引导程序的支持,Arduino可以使用串口上传程序而无需编程器。一般地,按下Arduino UNO R3开发板上的复位按键,13引脚上的LED快速的闪烁3下,代表引导程序正常启动。如果出现上传程序没有响应或复位时LED没有闪烁,排除硬件故障后,可以考虑重新烧录引导程序。 (1) 使用USBasp编程器烧录引导程序 Arduino IDE支持的编程器有AVRISP mkII,USBtinyISP和USBasp等,这里以USBasp为例。 上左图是10P的USBasp接口,右图是6P的
[单片机]
<font color='red'>AVR</font>开发 Arduino方法(附二) 故障排除:烧录引导程序
基于AVR单片机PWM功能的数控恒流源电路设计与产品研制
随着电子技术的深入发展,各种智能仪器越来越多,涉及领域越来越广,而仪器对 电源 的要求也越来越高。现今,电源设备有朝着数字化方向发展的趋势。然而绝大多数数控电源设计是通过高位数的 A/D 和 D/A 芯片来实现的,这虽然能获得较高的精度,但也使得成本大为增加。本文介绍一种基于AVR单片机PWM功能的低成本高精度数控恒流源,能够精确实现0~2A恒流。 系统框图 图1为系统的总体框图。本系统通过小键盘和 LCD 实现人机交流,小键盘负责接收要实现的 电流 值,LCD12864负责显示。AVR单片机根据输入的电流值产生对应的PWM波,经过 滤波 和 功放 电路 后对压控恒流元件进行控制,产生电流,电流再经过采样 电阻 到达
[单片机]
基于<font color='red'>AVR</font><font color='red'>单片机</font>PWM功能的数控恒流源电路设计与产品研制
AVR ATMega16在段式液晶上显示红外码
硬件:ATMega16(8MRC)+HT1621+一体化红外接收头 思路:红外解码采用中断捕捉方式(NEC编码),显示用液晶驱动HT1261 程序如下(WinAVR GCC环境编译): #include avr/io.h #include avr/delay.h #include avr/signal.h #include avr/interrupt.h #include avr/pgmsPACe.h #define HT1621_BIAS 0x29 // 设置LCD偏压发生器为1/3偏压,4个公共端 #define HT1621_RC256K 0x18 // 设置系统时钟源为片内RC(
[单片机]
avr--TWI(I2C)
TWI: 是一种全双工的串行通讯协议,与I2C工作方式相同,由一条数据传输线SDL,一条时钟线SCL组成,对应单片机的外部引脚PC1,PC0。 由于只有两条总线,简化了系统设计。 特点: • 简单,但是强大而灵活的通讯接口,只需要两根线 • 支持主机和从机操作 • 器件可以工作于发送器模式或接收器模式 • 7 位地址空间允许有128 个从机 • 支持多主机仲裁 • 高达400 kHz 的数据传输率 • 斜率受控的输出驱动器 • 可以抑制总线尖峰的噪声抑制器 • 完全可编程的从机地址以及公共地址
[单片机]
AVR使用中应注意的一些问题
  AVR与传统类型的单片机相比,除了必须能实现原来的一些基本的功能,其在结构体系、功能部件、性能和可靠性等多方面有很大的提高和改善。   但使用更好的器件只是为设计实现一个好的系统创造了一个好的基础和可能性,如果还采用和沿袭以前传统的硬件和软件设计思想和方法的话,是不能用好AVR的,甚至也不能真正的了解AVR的特点和长处。   功能越好的器件,需要具备更高技术和能力的人来使用和驾驭它。就象一部好的F1赛车,只有具备高超技术的驾驶员才能充分体会到车的特点,并能最大限度的发挥出车的性能。   AVR具有上手入门快,开发方便简单的特点,但要充分体会和发挥AVR的优点,还需要应用工程师本身的硬软件设计开发能力的不断学习、实践提高
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved