中断是在程序运行中经常用到的功能,用于处理一下实时性比较高的事件,首先来了解一下中断的概念。

中断

当出现需要及时处理的事件(中断请求)时,CPU暂停当前工作的执行转而处理应急事件(中断)的过程。即在程序运行过程中,系统出现了一个必须由CPU立即处理的情况,此时,CPU暂停当前程序去处理这个新事件的过程就就是中断。
举个栗子:你正在看着一本Arduino程序设计的书,看到95页时肚子饿了,你记下来页码跑去吃东西,吃完东西后继续从书的95页往下看;这里面看书就类似CPU在正常工作,肚子饿了吃东西就像处理中断,中断事件处理(吃东西)结束后(CPU)又从停下来的地方继续运行(看书)。


Arduino外部中断

外部中断是单片机实时地处理外部事件的一种内部机制。当某种外部事件发生时,单片机的中断系统将迫使CPU暂停正在执行的程序,转而去进行中断事件的处理;中断处理完毕后.又返回被中断的程序处,继续执行下去。
大多数的Arduino板至少拥有两个外部中断引脚:0号中断(引脚2)和1号中断(引脚3)。不同的Arduino控制板的中断引脚还有所区别,如下表所示。

Arduino控制板 int0 int1 int2 int3 int4 int5
Uno,Ethernet 2 3 X X X X
Mega 2 3 21 20 19 18
Leonardo 3 2 0 1 X X
Due IO

表中int表示Arduino板的中断号对应的引脚,X 则表示该板没有对应的中断引脚, 在所有的Arduino控制板中,Arduino Due比较强大的中断功能,允许在所有IO脚触发外部中断。
Arduino编程中的中断均已函数调用的形式来配置及使用,相对来说比较简单,下面是外部中断的常用函数。

1、中断函数
attachInterrupt(interrupt,function,mode)

描述:当发生外部中断时,调用一个指定的函数。在程序中再次调用时可以指定新的中断调用函数;如在setup函数中初始化函数A为中断0的中断调用函数,再次调用 attachInterrupt 函数时,可以指定函数B为中断0的中断调用函数,在程序中有需要可多次调用更新中断函数。
语法:attachInterrupt(interrupt,function,mode)
attachInterrupt(pin,function,mode)(Due专用)
参数:
interrupt,中断编号;pin,引脚编号(Due专用);
function:中断发生时调用的函数,此函数必须不带任何参数,不返回任何值。
mode:定义什么情况下触发中断,以下四个常数为mode的有效值:

  • LOW: 当引脚为低电平时,触发中断;
  • CHANGE:当引脚电平发生变化时,触发中断;
  • RISING:当引脚电平由低变高时,触发中断(上升沿中断);
  • FALLING:当引脚电平由高变低时,触发中断(下降沿中断);
    对于Due而言,多了一个专用参数 – HIGH,即当引脚为高电平时,触发中断;

注意:不要妄想在中断函数中加延时,在Arduino中断函数中,delay()不会生效,millis()不会持续累加;当中断发生时,串口数据可能会出现丢包;在中断函数里面使用到的全局变量应该声明为volatile变量。

示例:

const byte ledPin = 13;
const byte interruptpin = 2;
volatile byte state = LOW;void setup(void)
{pinMode(ledPin,OUTPUT);pinMode(interruptPin,INPUT_PULLUP);attachInterrupt(digitalPinToInterrupt(interruptPin),blink,CHANGE);
}void loop()
{digitalWrite(ledPin,state);
}void blink()
{state = !state;
}
detachInterrupt(interrupt)

描述:关闭对应的中断。
参数:interrupt,禁用中断的编号(0或1)。

2、中断使能函数
interrupts()

描述:启用中断/重新启用中断(在被禁用中断过后)。

noInterrupts()

描述:禁用中断。在程序中如果有一些函数的运行不希望被中断打断,可以调用noInterrupts函数来禁用中断的发生,再配合interrupts函数恢复中断使能。
示例:

void setup(){}void loop(){...noInterrupts();//放置关键函数(不想被打断)interrupts();//放置其他函数...
}

Arduino定时器中断

外部中断是通过检测输入电平的变化,而产生中断信号。除了外部中断方式外,Arduino控制板还可以按时间变化产生中断,这里使用到定时器(Timer),而对应产生的中断被称为定时器中断。
定时器是嵌入式系统中的一个特殊的计数器。它可以对分频后时钟信号的进行计数,当计数值达到设定值,即会产生定时器中断。且通过时钟频率和计数值可以计算出时间,所以可以达到以时间触发中断的效果。
即当需要按一定的时间间隔执行某个操作时,就需要用到定时器中断了。

以UNO为例,一共有3个定时器可以使用:Timer0,Timer1,Timer2。
每个定时器都有自己的函数库,通过使用硬件内部计时器中断来实现中断效果,下面以Timer1库的程序为例说明~
首先需要安装TimeOne库,也就是Timer1的对应库。下面提供两种安装方式,可以点击链接跳到对应的安装教程:
第一种:Arduino IDE安装TimerOne库
第二种:Sublime Text3环境安装TimerOne库
安装完成后,大概了解一下Timer1的库函数,打开.h文件TimerOne.h,除了包含 Arduino.h 外,还有一个宏定义声明了Timer1是一个16位定时器。

#define TIMER1_RESOLUTION 65536UL  // Timer1 is 16 bit

随后在TimerOne类中定义了定时器相关函数,在配置函数中包含了初始化函数initialize()函数,参数默认为1000000毫秒,也就是1秒;setPeriod()函数较长就不发出来了,主要作用是设置定时器的触发周期,在initialize()函数中调用。

 //****************************//  Configuration//****************************void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) {TCCR1B = _BV(WGM13);        // set mode as phase and frequency correct pwm, stop the timerTCCR1A = 0;                 // clear control register A setPeriod(microseconds);}void setPeriod(unsigned long microseconds) __attribute__((always_inline)) {...}

定时器状态控制函数

 //****************************//  Run Control//****************************void start() __attribute__((always_inline)) {TCCR1B = 0;TCNT1 = 0;     // TODO: does this cause an undesired interrupt?resume();}void stop() __attribute__((always_inline)) {TCCR1B = _BV(WGM13);}void restart() __attribute__((always_inline)) {start();}void resume() __attribute__((always_inline)) {TCCR1B = _BV(WGM13) | clockSelectBits;}

PWM控制相关函数(函数体省略)

 //****************************//  PWM outputs//****************************void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) {...}void pwm(char pin, unsigned int duty) __attribute__((always_inline)) {...}void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) {...}void disablePwm(char pin) __attribute__((always_inline)) {...}

最重要的来了,定时器中断相关函数

     //****************************//  Interrupt Function//****************************void attachInterrupt(void (*isr)()) __attribute__((always_inline)) {isrCallback = isr;TIMSK1 = _BV(TOIE1);}void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) {if(microseconds > 0) setPeriod(microseconds);attachInterrupt(isr);}void detachInterrupt() __attribute__((always_inline)) {TIMSK1 = 0;}

大概了解过定时器相关函数后,可以开始编写定时器中断程序了。
1、添加TimerOne头文件(安装库之后);
2、初始化定时器,开启定时器中断;
3、编写定时器中断操作;
简单程序如下

#include "TimerOne.h"//定时器中断函数
void Timer1Interrupt()
{digitalWrite(13,digitalRead(13)^1);            //控制LED闪烁
}void setup()
{pinMode(13,OUTPUT);                            //设置LED引脚为输出Timer1.initialize(500000);                  //Timer1每500毫秒触发一次Timer1.attachInterrupt(Timer1Interrupt);  //配置定时器中断函数,开启中断
}void loop(){}

使用定时器中断可以很好的解决Arduino的单任务机制,但是UNO的3个定时器资源同样跟引脚PWM会有冲突,比如Timer0定时器负责 millis()函数delay()函数 的计时工作,同时控制着数字引脚 pin5pin6 的PWM功能;Timer1定时器负责数字引脚 pin9pin10 的PWM功能,也影响 Servo.h库 的使用;Timer2定时器负责数字引脚 pin11pin3 的PWM功能,也和蜂鸣器的 tone()函数有关。

综上所述,定时器的使用会影响对应引脚的PWM功能,和某些时序函数的运行,比如Timer0负责delay()函数延时操作的,使用pin5、pin6的PWM输出可能会有些微误差,像analogWrite(5,0)和analogWrite(5,6)可能是一样的效果

除了对应的硬件定时器,还可以用Timer库和SimpleTimer库来实现定时效果,这两个库不用到硬件定时器,也是用到 millis()函数 来实现延时,所以对 delay函数 也是会有点影响。


串口中断(没这回事儿)

关于Arduino串口中断的也是找了一段时间没结果,后来发现了Arduino没有串口中断这回事,但是却机缘巧合的在main函数中找到了串口的端倪。
以下是Arduino中main函数的内容~

int main(void)
{init();initVariant();
#if defined(USBCON)USBDevice.attach();
#endifsetup(); for (;;) {loop();if (serialEventRun) serialEventRun();}    return 0;
}

即是Arduino没有串口中断,但是却用for循环来检测串口事件,与loop函数在同个循环中,相当于loop函数运行一次顺带检测一次串口事件。

Arduino成长日记6 - 中断机制相关推荐

  1. Arduino成长日记2 - Arduino编程基础

    上一篇讲述了什么是Arduino以及各类Arduino开发板的参数,本篇开始介绍开发环境搭建以及一些编程基础. Arduino开发环境 开发环境即Arduino项目的编程环境 – Arduino ID ...

  2. 网络工程师成长日记333-某城市政府项目

    网络工程师成长日记333-某城市政府项目 这是我的第333篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 直接上干货,拓扑图: 工程目的:排除故障 配置如下: LinWei#show ...

  3. 网络工程师成长日记365-IBIS西安工程回忆录

    网络工程师成长日记365-IBIS西安工程回忆录 这是我的第365篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 我的第一次真实工程经历 5.26日这一天我的心情是格外的兴奋,激动. ...

  4. 网络工程师成长日记421-某银行技术支持

    网络工程师成长日记421-某银行技术支持 这是我的第421篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 由于昨天的任务没有完成,客户要求我们今天继续去完成昨晚没有完成的任务. 今天良 ...

  5. 网络工程师成长日记417-西安如家酒店无线覆盖技术支持

    网络工程师成长日记417-西安如家酒店无线覆盖技术支持 这是我的第417篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 西安某汉庭连锁酒店WIFI部署项目报告 29日中午,我们来到位于 ...

  6. 网络工程师成长日记370-阿尔斯通

    网络工程师成长日记370-阿尔斯通 这是我的第370篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 4月20日下午,我和老大一起去西高新的高科大厦去进行H3C防火墙的安装 这是我第一次 ...

  7. vue替换全部符合’字符串_技术成长日记-Vim实用技巧-4.7查找替换

    1. 普通查找 / 向下查找 ? 向上查找 n 查找下一个目标字符串 N 查找上一个目标字符串 q/ 回溯查找历史,输入该命令后会在状态栏上方显示一个查找历史回溯窗口,如图4.1: 图4.1 回溯查找 ...

  8. puppet成长日记二 Package资源详细介绍及案例分析

    puppet成长日记二 Package资源详细介绍及案例分析 一.系统环境 1.puppet服务端 Release:RHEL6.4 HOSTNAME: puppetserver.rsyslog.org ...

  9. 网络工程师成长日记382-西部数据Juniper网络设备调试

    网络工程师成长日记382-西部数据Juniper网络设备调试 这是我的第382篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 西部数据Juniper网络设备调试 早上九点怀着无比激动的 ...

最新文章

  1. python常用包下载_Python及其常用模块库下载及安装
  2. .Net转Java自学之路—基础巩固篇十八(正则)
  3. 如何在全局程序集缓存 (GAC) 中安装 DLL 文件
  4. java+enum+devicetype_JSF web编程:通过enum数组生成
  5. git reset后本地拉取_一份值得收藏的 Git 异常处理清单
  6. Nginx伪静态配置和常用Rewrite伪静态规则集锦
  7. linux shell脚本链接操作符,Shell脚本中的操作符
  8. 关于bootstrap的table表显示无法找到匹配内容的问题随笔
  9. 阿里云云计算 52在线实验--云监控初体验
  10. DOS BAT脚本批量打开Edge网页
  11. 【哼歌检索】十大语音搜索应用服务
  12. 泰坦尼克号预测python_泰坦尼克号生存预测(python)
  13. 【新手必看】渗透测试学习书籍推荐
  14. shopnc怎么使用 php,ShopNC单用户版/安装php
  15. 深度学习常见问题整理
  16. office文件图标显示不正常
  17. 常用颜色对应RGB颜色图
  18. Personalized Top-N Sequential Recommendation via Convolutional Sequence Embedding
  19. mysql 查询分析器_mysql查询分析工具|mysql查询分析器(MySQL Query Browser)下载v1.1.20 官方版_ IT猫扑网...
  20. E-PUCK机器人-开始

热门文章

  1. 黑胶唱片的“另类”用途
  2. .NET进阶篇-丑话先说,Flag先立
  3. python量化交易笔记---13.描述性统计
  4. Unity角色模型由Generic改成Humanoid之后hips节点动画失效的问题
  5. 《红楼梦》人物关系有多复杂?一张图帮你理清楚!
  6. 学计算机可以考小学数学的教师编吗,入编说丨小学数学考编经验
  7. 【香蕉oi】耍望节(数位DP+倍增优化)
  8. 手工做迷宫_纸盒子废物利用手工自制小迷宫教玩具给幼儿玩(步骤图解)
  9. hihocoder #1272 : 买零食
  10. quartus错误集锦(未完待续)