Arduino-外部中断函数介绍和使用
📖外部中断函数
attachInterrupt():设置中断,根据不同的开发板,中断引脚不同。
📍参考:https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
🌿对于328P单片机处理INT0和IT1外部中断引脚外,还支持端口引脚(电平变化)中断,分别对应中断向量表中的:PCINT0,PCINT1,PCINT2,PCINT3,几乎所有的引脚都支持这种电平变化中断,不过它们都共用这4个中断源。
ESP8266支持除GPIO16外的任何GPIO中的中断。
detachInterrupt():取消指定引脚的中断interrupts():开中断noInterrupts():关中断
📝注意事项
在中断服务函数中,delay()不起作用,而millis()返回的值也不会递增。在函数中接收的串行数据可能会丢失。在中断服务函数中修改的任何变量都应该声明为volatile。如果想在中断服务函数中使用延时,delayMicroseconds()可以使用这个来延时。
中断函数语法、
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)
digitalPinToInterrupt(pin):中断引脚 ISR中断发生时调用的ISR;这个函数不能接受任何参数,也不能返回任何内容。这个函数有时被称为中断服务程序。 mode:中断模式(4种)
CHANGE:当引脚改变值时触发中断-例如从HIGH到LOW或LOW到HIGH;FALLING:当触发信号从高电平到低电平是触发;RISING:当引脚从低电平到高电平时触发。LOW: 当引脚为低电平时触发中断服务程序
🔖AVR单片机中断控制:
📑Arduino UNO开启外部2号引脚触发中断 (Arduino UNO中断引脚有2个,2和3)
const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}
void loop() {
digitalWrite(ledPin, state);
}
void blink() {
state = !state;
}
📝detachInterrupt(digitalPinToInterrupt(pin)):取消指定类型的中断.
interrupts()函数原型:#define interrupts() sei()
void setup() {}
void loop()
{
noInterrupts();
// critical, time-sensitive code here
interrupts();
// other code here
}
noInterrupts()函数原型:#define noInterrupts() cli()
void setup() {}
void loop()
{
noInterrupts();
// critical, time-sensitive code here
interrupts();
// other code here
}
📘寄存器配置外部中断示例
配置外部中断0(PD2)2号引脚作为外部中断引脚
volatile bool ext_int0_flag = 0;
void ext_int0_init(void)
{
// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Any change
// INT1: Off
EICRA=(0< EIMSK=(0< EIFR=(0< PCICR=(0< } // the setup function runs once when you press reset or power the board void setup() { // initialize digital pin LED_BUILTIN as an output. pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); ext_int0_init(); } ISR(INT0_vect) { ext_int0_flag = 1; } // the loop function runs over and over again forever void loop() { if (ext_int0_flag) { ext_int0_flag = 0; PORTB ^= (1 << PINB5);//板载led 闪烁一次 Serial.println("ext int0 Trg!\r\n"); } } 如果需要开启外部中断0和1: volatile bool ext_int0_flag = 0; volatile bool ext_int1_flag = 0; void ext_int0_init(void) { // External Interrupt(s) initialization // INT0: On // INT0 Mode: Any change // INT1: Off EICRA=(0< EIMSK=(1< EIFR=(1< PCICR=(0< } // the setup function runs once when you press reset or power the board void setup() { // initialize digital pin LED_BUILTIN as an output. pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); ext_int0_init(); } //外部中断0 ISR(INT0_vect) { ext_int0_flag = 1; } //外部中断1 ISR(INT1_vect) { ext_int1_flag = 1; } // the loop function runs over and over again forever void loop() { // digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) // delay(1000); // wait for a second // digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW // delay(1000); // wait for a second // Serial.println("Hello World2"); if (ext_int0_flag) { ext_int0_flag = 0; PORTB ^= (1 << PINB5);//1秒钟闪烁一次 Serial.println("ext int0 Trg!\r\n"); } if (ext_int1_flag) { ext_int1_flag = 0; PORTB ^= (1 << PINB5);//1秒钟闪烁一次 Serial.println("ext int1 Trg!\r\n"); } } 🥕对于Atmega328P、ATmega8等AVR带比较器外设的,可以利用比较器扩展外部中断 📍资料参考来运:https://www.geek-workshop.com/thread-11507-1-1.html📝测试代码: /* 328p通过内部比较器扩展外部中断 对应的引脚是arduino 的PIN D6 ,D7 . AIN0 -> PD6 (正反馈输入引脚) AIN1 -> PD7 (负反馈输入引脚) 比较器大家都比较熟悉,常用的比较器型号像LM393分正反馈与负反馈, 328P片上集成的也是一样有正负反馈. D6是正反馈,D7是负反馈. D6正反馈可以通过寄存器操作与能隙基准源连接,D6引脚 中断号:ISR(ANALOG_COMP_vect) 24 */ #include int pin = 13; volatile int state = LOW; QUN M; //你要做的只是添加上这个 void setup() { pinMode(pin, OUTPUT); M.attachInterrupt(blink, M_CHANGE); //格式是:M.attachInterrupt(blink, M_CHANGE); //blink是要执行的函数, // M_CHANGE 当引脚电平发生改变时,触发中断 // M_RISING 当引脚由低电平变为高电平时,触发中断 // M_FALLING 当引脚由高电平变为低电平时,触发中断 Serial.begin(9600); } void loop() { digitalWrite(pin, state); } void blink() { state = !state;//当负反馈端(AIN1)PD7引脚电平发生改变时,触发中断 Serial.println("ANALOG_COMP ISR Trg"); } 📗不使用库代码实现: //------------------------------------------------------------ // The two LEDs are a red one on D13 which simply blinks every // 'redDelay' milli-seconds and a green one on D8 controlled // by the Analog Comparator interrupt. //------------------------------------------------------------ const uint8_t redLED = 13; #define M_CHANGE 0 #define M_RISING 2 #define M_FALLING 3 void CMP_Init(unsigned char bytes) { PORTD|=_BV(PORTD7); DIDR1|=_BV(AIN1D)|_BV(AIN0D); ACSR=0; ACSR|=_BV(ACIE)|_BV(ACBG)|bytes; SREG|=(1<<7); } void setup() { // put your setup code here, to run once: pinMode(redLED, OUTPUT); Serial.begin(9600); CMP_Init(M_CHANGE); } void loop() { // put your main code here, to run repeatedly: } // The ISR for the Analog Comparator sets the LED on pin 8 to // the same state as the ACO bit in the ACSR register. //------------------------------------------------------------ ISR(ANALOG_COMP_vect) { digitalWrite(redLED, !!(ACSR & (1 << ACO))); Serial.println("ANALOG_COMP ISR Trg"); } 🎈其它相关比较器使用库:https://github.com/NormanDunbar/libAnaComp/tree/main 📙端口引脚电平变化中断 在Atmega328上有两个正常的硬件中断,从中断向量表中可以看到分别是INT0和INT1。这两个中断对应的引脚为PD2和PD3。除了这两个中断外,其余的硬件中断为“Pin-Change Interrupt”,简记为PCINT。PCINT分为三组。每组对应8个Pin,通过寄存器PCICR、PCIFR和PCMSKn(n:0-3),从而实现了所有GPIO都可以用作中断引脚。 📒测试例程: /* PCICR – 引脚改变中断控制寄存器,BIT4:0 ->PCIE4:PCIE0分别对应PF PE PD PC PB端口;当对应为写1时,对应端口引脚中断被使能。 PCIFR – 引脚改变中断标志寄存器BIT4:0 -> PCIF4:PCIF1 分别对应PF PE PD PC PB端口引脚改变中断标志位;对应为写1清零 PCMSK0 引脚改变中断屏蔽寄存器0 BIT7:0 对应PCINT7:PCINT0 对应端口:PB0 - PB7 PCMSK1 引脚改变中断屏蔽寄存器1 BIT7:0 对应PCINT14:PCINT8 对应端口:PC0 - PC7 PCMSK2 引脚改变中断屏蔽寄存器2 BIT7:0 对应PCINT23:PCINT16 对应端口:PD0 - PD7 PCMSK3 – 引脚改变中断屏蔽寄存器3 BIT7:0 对应PCINT27:PCINT24 对应端口:PE0 - PE7 引脚端口 中断向量 中断号 PB0-PB7 PCINT0 3 PC0-PC7 PCINT1 4 PD0-PD7 PCINT2 5 PE0 - PE7 PCINT3 27 ISR (PCINT0_vect) 处理 Pin D8 to D13 ISR (PCINT1_vect) 处理 Pin A0 to A5 ISR (PCINT2_vect) 处理 Pin D0 to D7 ISR (PCINT3_vect) 处理 Pin PE00 to PE7 */ void setup() { // put your setup code here, to run once: pinMode(LED_BUILTIN, OUTPUT); Serial.begin(9600); PCICR=1< PCIFR=1< PCMSK1=(1< sei();//使能中断 } void loop() { // put your main code here, to run repeatedly: } ISR(PCINT1_vect){ //你的中断程序 Serial.println("PCINT1_vect"); PORTB ^= (1 << PINB5);//PB5状态翻转 } ⚡如果程序中使用了软串口(SoftwareSerial), 包含了( #include