Arduino成长日记6 - 中断机制
中断是在程序运行中经常用到的功能,用于处理一下实时性比较高的事件,首先来了解一下中断的概念。
中断
当出现需要及时处理的事件(中断请求)时,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()函数
的计时工作,同时控制着数字引脚 pin5
,pin6
的PWM功能;Timer1定时器负责数字引脚 pin9
、pin10
的PWM功能,也影响 Servo.h库
的使用;Timer2定时器负责数字引脚 pin11
,pin3
的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 - 中断机制相关推荐
- Arduino成长日记2 - Arduino编程基础
上一篇讲述了什么是Arduino以及各类Arduino开发板的参数,本篇开始介绍开发环境搭建以及一些编程基础. Arduino开发环境 开发环境即Arduino项目的编程环境 – Arduino ID ...
- 网络工程师成长日记333-某城市政府项目
网络工程师成长日记333-某城市政府项目 这是我的第333篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 直接上干货,拓扑图: 工程目的:排除故障 配置如下: LinWei#show ...
- 网络工程师成长日记365-IBIS西安工程回忆录
网络工程师成长日记365-IBIS西安工程回忆录 这是我的第365篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 我的第一次真实工程经历 5.26日这一天我的心情是格外的兴奋,激动. ...
- 网络工程师成长日记421-某银行技术支持
网络工程师成长日记421-某银行技术支持 这是我的第421篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 由于昨天的任务没有完成,客户要求我们今天继续去完成昨晚没有完成的任务. 今天良 ...
- 网络工程师成长日记417-西安如家酒店无线覆盖技术支持
网络工程师成长日记417-西安如家酒店无线覆盖技术支持 这是我的第417篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 西安某汉庭连锁酒店WIFI部署项目报告 29日中午,我们来到位于 ...
- 网络工程师成长日记370-阿尔斯通
网络工程师成长日记370-阿尔斯通 这是我的第370篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 4月20日下午,我和老大一起去西高新的高科大厦去进行H3C防火墙的安装 这是我第一次 ...
- vue替换全部符合’字符串_技术成长日记-Vim实用技巧-4.7查找替换
1. 普通查找 / 向下查找 ? 向上查找 n 查找下一个目标字符串 N 查找上一个目标字符串 q/ 回溯查找历史,输入该命令后会在状态栏上方显示一个查找历史回溯窗口,如图4.1: 图4.1 回溯查找 ...
- puppet成长日记二 Package资源详细介绍及案例分析
puppet成长日记二 Package资源详细介绍及案例分析 一.系统环境 1.puppet服务端 Release:RHEL6.4 HOSTNAME: puppetserver.rsyslog.org ...
- 网络工程师成长日记382-西部数据Juniper网络设备调试
网络工程师成长日记382-西部数据Juniper网络设备调试 这是我的第382篇原创文章,记录网络工程师行业的点点滴滴,结交IT行业有缘之人 西部数据Juniper网络设备调试 早上九点怀着无比激动的 ...
最新文章
- python常用包下载_Python及其常用模块库下载及安装
- .Net转Java自学之路—基础巩固篇十八(正则)
- 如何在全局程序集缓存 (GAC) 中安装 DLL 文件
- java+enum+devicetype_JSF web编程:通过enum数组生成
- git reset后本地拉取_一份值得收藏的 Git 异常处理清单
- Nginx伪静态配置和常用Rewrite伪静态规则集锦
- linux shell脚本链接操作符,Shell脚本中的操作符
- 关于bootstrap的table表显示无法找到匹配内容的问题随笔
- 阿里云云计算 52在线实验--云监控初体验
- DOS BAT脚本批量打开Edge网页
- 【哼歌检索】十大语音搜索应用服务
- 泰坦尼克号预测python_泰坦尼克号生存预测(python)
- 【新手必看】渗透测试学习书籍推荐
- shopnc怎么使用 php,ShopNC单用户版/安装php
- 深度学习常见问题整理
- office文件图标显示不正常
- 常用颜色对应RGB颜色图
- Personalized Top-N Sequential Recommendation via Convolutional Sequence Embedding
- mysql 查询分析器_mysql查询分析工具|mysql查询分析器(MySQL Query Browser)下载v1.1.20 官方版_ IT猫扑网...
- E-PUCK机器人-开始
热门文章
- 黑胶唱片的“另类”用途
- .NET进阶篇-丑话先说,Flag先立
- python量化交易笔记---13.描述性统计
- Unity角色模型由Generic改成Humanoid之后hips节点动画失效的问题
- 《红楼梦》人物关系有多复杂?一张图帮你理清楚!
- 学计算机可以考小学数学的教师编吗,入编说丨小学数学考编经验
- 【香蕉oi】耍望节(数位DP+倍增优化)
- 手工做迷宫_纸盒子废物利用手工自制小迷宫教玩具给幼儿玩(步骤图解)
- hihocoder #1272 : 买零食
- quartus错误集锦(未完待续)