什么是中断
中断(Interrupt)是一种在计算机中被广泛应用的一种重要的技术,它是一种CPU事件处理机制。其本质是一个电信号,这个信号由某个硬件或软件产生并由处理器处理。当处理器接收到中断信号时会暂停当前任务,跳到一个叫中断服务例程(ISR)的一段程序中处理中断,当中断被处理完成后,CPU将回到原来的任务继续工作。与中断相对的事件处理机制是轮询,顾名思义就是不断的询问相关的硬件是否做好准备。
中断的一个比较常见的例子是磁盘读写。我们都知道,磁盘的速度与CPU的速度不在一个数量级上面(ms和ns的区别),当CPU希望从磁盘上面拷贝一个或多个扇区到内存时,如果采用轮询的方法,CPU需要在这个过程中不断的将数据从磁盘读入寄存器,再从寄存器写入到内存中。需要注意的是CPU与磁盘的速度有着巨大的差异,两者相差将近6个数量级,这样会使得CPU在把大部分的时间都花在等待磁盘响应上面,这无疑是一种浪费——在磁盘读写期间CPU完全可以进行其他的运算,却浪费在了无意义的等待上面。而如果使用中断的方法,CPU将会把磁盘请求发送给一个叫DMA控制器的硬件,然后把当前任务挂起,先执行其他任务。DMA会“接管”这个工作,有它负责代替CPU完成轮询的工作,并通过总线直接将数据写到内存上面。复制完成后,它会触发一个中断通知CPU已经复制完成,CPU响应后就可以继续执行请求磁盘读写的任务。这样一来,CPU就不再需要等待拖拉的磁盘了。事实上,现代计算机都离不开中断,我们对计算机的任何的一个操作(包括鼠标或键盘),都会触发相应的中断,系统调用也需要通过中断完成(起码经典过程是如此)。
根据中断的来源,我们可以把中断分为软中断和硬中断,顾名思义他们分别就是由软件和硬件触发的中断。
在Arduino中使用中断
Arduino中主要有时钟中断和外部中断,本文所说的中断指的是外部中断。Arduino中的外部中断通常是由Pin口(数字Pin口,不是模拟口)电平改变触发的。
开发板 可以用来注册中断的Pin口
Uno, Nano, Mini, other 328-based 2, 3
注册中断主要是通过attachInterrupt()函数实现的,其原型为:
void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode);
第一个参数为中断号,Arduino上每个可以注册中断的Pin口都会被分配一个中断号,这里需要传入的是中断号而不是Pin口号。但是不同的Arduino开发板上面的中断号分配并不完全一样。各个 开发板的Pin口号和中断号对应关系如下:
Uno, Ethernet PIN 2 PIN 3
另一种方法:
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);
这种方式来注册中断号。
第二个参数是中断服务例程(ISR)的函数指针,在C/C++中直接写该函数的函数名即可。在触发时,该函数将会被调用。该函数必须没有任何的参数也没有任何的返回值。
第三个参数是中断触发条件,由几个可选的值:
LOW 当中断所在Pin口处于低电平时触发
CHANGE 当中断所在Pin口电平改变时触发
RISING 当中断所在Pin口从低电平变为高电平(上升沿)时触发
FALLING 当中断所在Pin口从高电平变为低电平(下降沿)时触发
在Arduino中使用中断需要注意的问题
1、由于中断会打断正常代码的运行,因此ISR的应该尽可能快地执行完毕。
2、在ISR中修改的全局变量要用volatile修饰符修饰以防止编译器优化
3、在ISR中不能使用其他用中断实现的函数,如millis() delay() 等。延时可以使用 delayMicroseconds(),它不是用中断实现的。
最终程序:
/*********************************************************
* 功能:四个项目使用外部中断快速切换
* 引脚接线:DHT11:6
* WS2812b:9
* DS18b20:7
* MQ-3:A0
* DS3231:IIC
*
********************************************************/
#include "U8glib.h"
#include <FastLED.h>
#include <Wire.h>
#include <DallasTemperature.h>
#include <OneWire.h>
#include "DHT.h"
#include <DS3231.h>
#define button1 3 //触摸按键接口定义
#define ONE_WIRE_BUS 7 //DS18b20
#define DHTPIN 6 //DHT11
#define DHTTYPE DHT11
#define NUM_LEDS 2 // LED灯珠数量
#define DATA_PIN 9 // Arduino输出控制信号引脚
#define LED_TYPE WS2812 // LED灯带型号
#define COLOR_ORDER GRB // RGB灯珠中红色、绿色、蓝色LED的排列顺序
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);
uint8_t max_bright = 128; // LED亮度控制变量,可使用数值为 0 ~ 255, 数值越大则光带亮度越高
CRGB leds[NUM_LEDS]; // 建立光带leds
uint8_t openState = 3;
uint8_t c = 0;
float TempC = 0;
float h;
float t;
const int analogInPin = A0;
int sensorValue = 0; // value read from the pot
int outputValue = 0;
int ppm = 0;
uint8_t openState2 = 20; //酒精ppm阈值
uint8_t openState3 = 20; //天数
uint8_t cishu = 0;
uint8_t val, flag=0;
DS3231 Clock;
//uint8_t h;
bool Century=false;
bool h12;
bool PM;
byte ADay, AHour, AMinute, ASecond, ABits;
bool ADy, A12h, Apm;
int second,minute,hour,date,month,year,temperature;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DHT dht(DHTPIN, DHTTYPE);
static unsigned char deng[] U8G_PROGMEM = {0x00,0x00,0x7E,0x3E,0x40,0x22,0x40,0x12,0x42,0x12,0x24,0x0A,0x28,0x12,0x10,0x12,
0x10,0x22,0x28,0x22,0x28,0x22,0x44,0x16,0x42,0x0A,0x01,0x02,0x00,0x02,0x00,0x02,/*"邓",0*/};
static unsigned char zhao[] U8G_PROGMEM = {0x00,0x00,0xBE,0x3F,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x29,0xBE,0x10,0x22,0x3F,
0x22,0x21,0x22,0x21,0x22,0x21,0x3E,0x3F,0x00,0x00,0x12,0x11,0x22,0x22,0x21,0x22,/*"照",1*/};
static unsigned char quan[] U8G_PROGMEM = {0x08,0x00,0xC8,0x3F,0x88,0x20,0x88,0x20,0xBF,0x20,0x08,0x11,0x0C,0x11,0x1C,0x11,
0x2A,0x0A,0x2A,0x0A,0x09,0x04,0x08,0x04,0x08,0x0A,0x08,0x11,0x88,0x20,0x68,0x40,/*"权",2*/};
static unsigned char nian[] U8G_PROGMEM = {0x08,0x00,0x08,0x00,0xF8,0x3F,0x04,0x01,0x04,0x01,0x02,0x01,0xF8,0x1F,0x08,0x01,
0x08,0x01,0x08,0x01,0xFF,0x7F,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,/*"年",0*/};
static unsigned char yue[] U8G_PROGMEM = {0x00,0x00,0xF8,0x1F,0x08,0x10,0x08,0x10,0x08,0x10,0xF8,0x1F,0x08,0x10,0x08,0x10,
0x08,0x10,0xF8,0x1F,0x08,0x10,0x08,0x10,0x04,0x10,0x04,0x10,0x02,0x14,0x01,0x08,/*"月",1*/};
static unsigned char ri[] U8G_PROGMEM = {0x00,0x00,0xF8,0x0F,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0xF8,0x0F,
0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0xF8,0x0F,0x08,0x08,/*"日",2*/};
static unsigned char zhou[] U8G_PROGMEM = {0x00,0x00,0xFC,0x1F,0x84,0x10,0x84,0x10,0xF4,0x17,0x84,0x10,0x84,0x10,0xFC,0x1F,
0x04,0x10,0xE4,0x13,0x24,0x12,0x24,0x12,0xE4,0x13,0x02,0x10,0x02,0x14,0x01,0x08,/*"周",0*/};
static unsigned char si[] U8G_PROGMEM = {0x00,0x00,0x00,0x00,0xFE,0x3F,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,
0x12,0x22,0x12,0x3C,0x0A,0x20,0x06,0x20,0x02,0x20,0xFE,0x3F,0x02,0x20,0x00,0x00,/*"四",1*/};
void button_State(void) //中断回调函数一定要是无返回无参数的函数void
{
//A
// if(!digitalRead(button1)){ //检测按键是否按下
openState++;
// }
// while(digitalRead(button1));
// Serial.println("进入中断");
}
void wendu_Jiance(void)
{
h = dht.readHumidity();
// Read temperature as Celsius (the default)
t = dht.readTemperature();
sensors.requestTemperatures();
TempC = sensors.getTempCByIndex(0);
if(TempC >= 30)
{
leds[0] = CRGB::Red;
FastLED.show();
}
else if(TempC <= 26)
{
leds[0] = CRGB::Green;
FastLED.show();
}
delay(50);
if(h >= 90)
{
leds[1] = CRGB::Red;
FastLED.show();
}
else if(h <= 85)
{
leds[1] = CRGB::Green;
FastLED.show();
}
}
void jiuJin(void)
{
sensorValue = analogRead(analogInPin);
outputValue = map(sensorValue, 0, 1023, 0, 5000);
ppm = outputValue/100-8;
if(ppm<0)
ppm = 0;
Serial.print("output = ");
Serial.println(ppm);
if(ppm>openState2)
digitalWrite(LED_BUILTIN, HIGH);
else
digitalWrite(LED_BUILTIN, LOW);
}
void ws2812_Show(void)
{
if(cishu == 3)
cishu = 0;
if(cishu == 0){
leds[0] = CRGB::Red; // 设置光带中第一个灯珠颜色为红色,leds[0]为第一个灯珠,leds[1]为第二个灯珠
FastLED.show();
delay(100);
leds[1] = CRGB::Red;
FastLED.show();
}
if(cishu == 1){
leds[0] = CRGB::Blue; // 设置光带中第一个灯珠颜色为红色,leds[0]为第一个灯珠,leds[1]为第二个灯珠
FastLED.show();
delay(100);
leds[1] = CRGB::Blue;
FastLED.show();
}
if(cishu == 2){
leds[0] = CRGB::Yellow; // 设置光带中第一个灯珠颜色为红色,leds[0]为第一个灯珠,leds[1]为第二个灯珠
FastLED.show();
delay(100);
leds[1] = CRGB::Yellow;
FastLED.show();
}
}
void ReadDS3231()
{
second=Clock.getSecond();
minute=Clock.getMinute();
hour=Clock.getHour(h12, PM);
date=Clock.getDate();
month=Clock.getMonth(Century);
year=Clock.getYear();
temperature=Clock.getTemperature();
// mode_0();
}
void draw0(void) {//DS18b20
u8g.drawXBMP( 0, 0, 16, 16, deng);
u8g.drawXBMP( 16, 0, 16, 16, zhao);
u8g.drawXBMP( 32, 0, 16, 16, quan);
u8g.setFont(u8g_font_unifont);
// u8g.drawStr( 0, 45, "DengZhaoquan:");
// u8g.drawStr( 0, 12, "190306238");
u8g.setPrintPos(70,15);
u8g.print("NO:1");
// sensors.requestTemperatures();
// TempC = sensors.getTempCByIndex(0);
u8g.setPrintPos(0,30);
u8g.print("DS18B20:");
u8g.print(TempC);
u8g.print(" C");
}
void draw1(void) {
u8g.drawXBMP( 0, 0, 16, 16, deng);
u8g.drawXBMP( 16, 0, 16, 16, zhao);
u8g.drawXBMP( 32, 0, 16, 16, quan);
u8g.setPrintPos(70,15);
u8g.print("NO:2");
u8g.setPrintPos(0,30);
u8g.print("DHT11:");
u8g.setPrintPos(0,45);
u8g.print("h= ");
u8g.print(h);
u8g.print("%");
u8g.setPrintPos(0,60);
u8g.print("t = ");
u8g.print(t);
u8g.print(" C");
u8g.setFont(u8g_font_unifont);
u8g.drawStr( 61, 30, "Numb:");
u8g.setPrintPos(100,30);
u8g.print(c++);
}
void draw2(void) {
u8g.setFont(u8g_font_unifont);
// u8g.drawStr( 0, 45, "DengZhaoquan:");
// u8g.drawStr( 0, 12, "190306238");
u8g.drawXBMP( 16, 0, 16, 16, deng);
u8g.drawXBMP( 32, 0, 16, 16, zhao);
u8g.drawXBMP( 48, 0, 16, 16, quan);
u8g.setPrintPos(70,15);
u8g.print("NO:3");
u8g.setPrintPos(10,29);
u8g.print("sensor = ");
u8g.print(sensorValue);
u8g.setPrintPos(10,43);
u8g.print("output = ");
u8g.print(outputValue);
u8g.print("mv");
u8g.setPrintPos(10,58);
u8g.print("MQ3_PPM = ");
u8g.print(ppm);
}
void draw3(void) {
u8g.drawXBMP( 16, 0, 16, 16, deng);
u8g.drawXBMP( 32, 0, 16, 16, zhao);
u8g.drawXBMP( 48, 0, 16, 16, quan);
u8g.setPrintPos(70,15);
u8g.print("NO:4");
u8g.setPrintPos(0,30);
u8g.print("Time:");
u8g.setPrintPos(0,45);
u8g.print("20");
u8g.print(year,DEC);
u8g.drawXBMP( 32, 32, 16, 16, nian);
u8g.setPrintPos(48,45);
u8g.print(month);
u8g.drawXBMP( 66, 32, 16, 16, yue);
u8g.setPrintPos(82,45);
u8g.print(date);
u8g.drawXBMP( 98, 32, 16, 16, ri);
u8g.setPrintPos(0,60);
u8g.print(hour);
u8g.print(":");
u8g.print(minute);
u8g.print(":");
u8g.print(second);
}
void mode_0()
{
u8g.firstPage();
do {
draw0();
} while( u8g.nextPage() );
}
void mode_1()
{
u8g.firstPage();
do {
draw1();
} while( u8g.nextPage() );
}
void mode_2()
{
u8g.firstPage();
do {
draw2();
} while( u8g.nextPage() );
}
void mode_3()
{
u8g.firstPage();
do {
draw3();
} while( u8g.nextPage() );
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
// pinMode(button1, INPUT);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(button1, INPUT_PULLUP);
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
pinMode(2, OUTPUT);
digitalWrite(2, HIGH);
delay(100);
dht.begin();
LEDS.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS); // 初始化光带
FastLED.setBrightness(max_bright); // 设置光带亮度
Wire.begin();
Clock.setSecond(50);//Set the second
Clock.setMinute(25);//Set the minute
Clock.setHour(15); //Set the hour
Clock.setDoW(4); //Set the day of the week
Clock.setDate(openState3); //Set the date of the month
Clock.setMonth(5); //Set the month of the year
Clock.setYear(21); //Set the year (Last two digits of the year)
attachInterrupt(digitalPinToInterrupt(button1), button_State, FALLING);
// attachInterrupt(11, button, CHANGE);
}
void loop() {
// put your main code here, to run repeatedly:
wendu_Jiance(); //温湿度判断改变灯带颜色
jiuJin(); //酒精检测函数
// ws2812_Show(); //LED显示函数
ReadDS3231(); //读取时间
switch (openState)
{
case 0:
// openState = 0;
mode_0(); //
break;
case 1:
mode_1(); //
break;
case 2:
mode_2(); //
break;
case 3:
mode_3(); //
break;
case 4:
// mode_4(); //
break;
default:
break;
}
if(openState >= 4) openState = 0;
Serial.println(openState);
}
/*
* 附:attachInterrupt()函数的语法介绍
attachInterrupt( digitalPinToInterrupt(pin) , function, mode)
digitalPinToInterrupt(pin) :取得引脚pin的中断号
function:中断发生时调用的函数,此函数必须不带参数和不返回任何值。该函数称为中断服务程序。
mode:定义何时发生中断以下四个contstants预定有效值:
LOW 当引脚为低电平时,触发中断
CHANGE 当引脚电平发生改变时,触发中断
RISING 当引脚由低电平变为高电平时,触发中断
FALLING 当引脚由高电平变为低电平时,触发中断.
————————————————
*/