在了解中断子系统之前,首先要了解中断的概念。你正在看书,这时电话响了,你会怎么做呢?相信大多数人会这样:先标记看到的位置,接完电话回来后继续阅读。这就是一个现实生活中中断的例子,我们把“电话响了”成为中断源。Arduino UNO R3的主处理器ATMega328P拥有26个中断源,如下表所示:
| 向量号 | 程序地址 | 中断源 | 中断定义 | 中断服务程序名称 | 
| 1 | 0x0000 | RESET | 外部电平复位,上电复位,掉电检测复位,看门狗复位 |  | 
| 2 | 0x0002 | INT0 | 外部中断请求0 | INT0_vect | 
| 3 | 0x0004 | INT1 | 外部中断请求1 | INT1_vect | 
| 4 | 0x0006 | PCINT0 | 引脚电平变化中断请求0 | PCINT0_vect | 
| 5 | 0x0008 | PCINT1 | 引脚电平变化中断请求1 | PCINT1_vect | 
| 6 | 0x000A | PCINT2 | 引脚电平变化中断请求2 | PCINT2_vect | 
| 7 | 0x000C | WDT | 看门狗溢出中断 | WDT_vect | 
| 8 | 0x000E | TIMER2 COMPA | 定时/计数器2比较匹配A | TIMER2_COMPA_vect | 
| 9 | 0x0010 | TIMER2 COMPB | 定时/计数器2比较匹配B | TIMER2_COMPB_vect | 
| 10 | 0x0012 | TIMER2 OVF | 定时/计数器2溢出 | TIMER2_OVF_vect | 
| 11 | 0x0014 | TIMER1 CAPT | 定时/计数器1事件捕捉 | TIMER1_CAPT_vect | 
| 12 | 0x0016 | TIMER1 COMPA | 定时/计数器1比较匹配A | TIMER1_COMPA_vect | 
| 13 | 0x0018 | TIMER1 COMPB | 定时/计数器1比较匹配B | TIMER1_COMPB_vect | 
| 14 | 0x001A | TIMER1 OVF | 定时/计数器1溢出 | TIMER1_OVF_vect | 
| 15 | 0x001C | TIMER0 COMPA | 定时/计数器0比较匹配A | TIMER0_COMPA_vect | 
| 16 | 0x001E | TIMER0 COMPB | 定时/计数器0比较匹配B | TIMER0_COMPB_vect | 
| 17 | 0x0020 | TIMER0 OVF | 定时/计数器0溢出 | TIMER0_OVF_vect | 
| 18 | 0x0022 | SPI STC | SPI串行传输结束 | SPI_STC_vect | 
| 19 | 0x0024 | USART RX | USART接收结束 | USART_RX_vect | 
| 20 | 0x0026 | USART UDRE | USART数据寄存器空 | USART_UDRE_vect | 
| 21 | 0x0028 | USART TX | USART,发送结束 | USART_TX_vect | 
| 22 | 0x002A | ADC | 模数转换结束 | ADC_vect | 
| 23 | 0x002C | EE READY | EEPROM准备好 | EE_READY_vect | 
| 24 | 0x002E | ANALOG COMP | 模拟比较器 | ANALOG_COMP_vect | 
| 25 | 0x0030 | TWI | 两线串行接口 | TWI_vect | 
| 26 | 0x0032 | SPM READY | 保存程序存储器内容就绪 | SPM_ready_vect | 
这里以外部中断0为例了解对中断子系统的编程,沿用上一章中用于数字输入示例的电路,这个示例使得按键在按下时LED的状态取反:
// Interrupt.ino
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;
}与外部中断相关的Arduino库函数有:
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode):启用指定引脚的外部中断并连接到指定中断服务程序
pin:指定外部中断的引脚
ISR:指定中断服务程序的名称
mode:LOW(低电平触发中断),CHANG(逻辑电平变化触发中断),RISING(上升沿触发中断)或FALLING(下降沿触发中断)
detachInterrupt(digitalPinToInterrupt(pin)):禁用指定中断
pin:指定取消外部中断的引脚
interrupts():开启总中断
noInterrupts():禁用总中断
ATMega328P的外部中断由2个相关寄存器控制,外部中断控制寄存器EICRA的结构如下图所示:
|  |  |  | INT1 | INT0 | ||
|  |  |  | ISC11 | ISC10 | ISC01 | ISC00 | 
ISCx[1:0](x = 0, 1)位用于设置外部中断的触发方式,如下表所示:
| ISCx[1:0] (x = 0, 1) | 外部中断触发方式 | 
| 00 | 低电平 | 
| 01 | 逻辑电平变化 | 
| 10 | 下降沿 | 
| 11 | 上升沿 | 
外部中断屏蔽寄存器EIMSK用于设置是否屏蔽外部中断,它的结构如下图所示:
|  |  |  |  |  | INT1 | INT0 | 
若向其中某位写入1,则该位控制的外部中断启用;写入0则禁用。
通过直接访问寄存器改写以上程序为:
// Interrupt_reg.ino
volatile byte state = LOW;
void setup() {
  DDRB |= (1 << PB5);
  
  DDRD &= ~(1 << PD2);
  PORTD |= (1 << PD2);
  EICRA &= ~(1 << ISC01) & ~(1 << ISC00);
  EIMSK |= (1 << INT0);
  sei(); // 启用总中断
}
void loop() {
  if (state == HIGH) {
    PORTB |= (1 << PB5);
  } else {
    PORTB &= ~(1 << PB5);
  }
}
// 外部中断0中断处理函数
ISR(INT0_vect) {
  state = !state;
} 
            PID控制算法是结合比例、积分和微分三种环节于一体的控制算法,它是连续系统中技术最为成熟、应用最为广泛的一种控制算法。
Arduino-ESP32与ESP-IDF的版本对应表。
Arduino+ESP32上使用TFT_eSPI库快速点亮这个屏幕,驱动芯片ST7789
Queue 库提供了一个通用的 C++ 动态队列实现,专为在 Arduino 项目中使用而定制。
 
            本程序是小鹏物联网智能浇花套件的单机版程序(不连接物联网),供同学们参考。
 
            ArrayList 类是一个 C++ 模板类,它提供了 ArrayList 的实现,以便轻松存储任何指定类型的值。它允许使用索引进行高效存储和检索,支持排序操作。
 
            ESP32系列(包括ESP32-S3)搭载Xtensa双核处理器,默认情况下Arduino框架仅使用单核运行用户代码,通过多核编程,可以充分利用硬件资源来提升系统响应和性能。
在本文中,先解释 MSB(最高有效位)和 LSB(最低有效位)的概念,以及 MSBFIRST 和 LSBFIRST。然后展示了 MSBFIRST 和 LSBFIRST 的使用如何影响移位寄存器的输出。
TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速与方向,输入电压在3V~12V,因此在集成化、小型化的电机控制系统中,它可以作为理想的电机驱动器件。
 
            Arduino-ESP32项目提供的Preferences库是一个专为ESP32设计的非易失性存储解决方案,它替代了传统的Arduino EEPROM库,提供了更强大、更可靠的数据存储功能。
用于Arduino的Adafruit_GFX库为我们所有的LCD和OLED显示器提供了通用语法和图形功能集。这使Arduino程序可以轻松地在显示类型之间进行调整,而不必花太多精力……
