Efficiënt 10 knoppen bedienen met één interrupt op Arduino UNO
Onderdelen en benodigdheden
| Onderdeel | Aantal |
|---|---|
| Arduino UNO (of een andere microcontroller) | 1 |
| Breadboard | 1 |
| Jumper wires | 21 |
| Tactiele schakelaars (elke knop is geschikt) | 10 |
Apps en online diensten
- Arduino IDE
Over dit project
Inleiding
Interrupts zijn handig. Ze maken code niet alleen eenvoudiger, maar kunnen ook worden gebruikt voor nauwkeurige timing of om de Arduino uit de slaapmodus te wekken.
Stel je voor dat je een gebruikersinterface hebt, bijvoorbeeld een afstandsbediening die op batterijen draait. Je wilt de Arduino (of een standalone ATmega) in power-down modus zetten om stroom te besparen. In power-down modus kan de Arduino alleen gewekt worden door een externe interrupt. De ATmega328P chip in een Arduino Uno heeft slechts twee externe pin-interrupts (INT0 en INT1 op pinnen 2 en 3). Omdat een gebruikersinterface waarschijnlijk meer dan twee knoppen heeft, is dat een probleem.
De standaardoplossing is om alle knoppen normaal aan te sluiten, maar ze ook met een diode aan een interrupt-pin te koppelen. Dit vercompliceert de schakeling aanzienlijk.

Naast standaard externe interrupts heeft de ATmega328P ook pin-change interrupts. Er zijn bibliotheken voor en ze zijn een goede oplossing.
Tijdens een programmeerwedstrijd ontdekte ik echter hoe je dit kunt doen met standaard externe interrupts zonder extra elektronische componenten.
De schakeling



We gebruiken een paar knoppen. Ik gebruikte er tien, die netjes op mijn breadboard paste. (Ik had er geen meer.) Je kunt één knop per pin aansluiten, wat betekent dat je tot 20 knoppen op een Uno kunt aansluiten en tot 70 op een Mega! (Als je echt 70 knoppen nodig hebt, raad ik multiplexing aan; je hebt geen hele Mega nodig.)
Elke knop heeft één kant aangesloten op een willekeurige pin (in mijn geval 4-13). De andere kanten van alle knoppen zijn samen verbonden met één interrupt-geschikte pin (in mijn geval pin 2).

De code
De code is hieronder te vinden. Upload hem naar je bord. Open de seriële monitor. Wanneer je een knop indrukt, verschijnt het nummer ervan. Zoals je ziet, wordt de loop()-functie helemaal niet gebruikt.

Hoe werkt het?
Er is dus een interrupt. In mijn geval is deze aan pin 2 gekoppeld en geconfigureerd als FALLING.
Om diodes te vermijden, herschikt de Arduino de schakeling on-the-fly. Er zijn twee mogelijke configuraties: Gemeenschappelijke modus en Gedifferentieerde modus.
Gemeenschappelijke modus
De meeste tijd staat de schakeling in gemeenschappelijke modus. De interrupt-pin is geconfigureerd als INPUT_PULLUP en de rest als OUTPUT en LOW.

void configureCommon() {
pinMode(commonPin, INPUT_PULLUP);
for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
pinMode(buttonPins[i], OUTPUT);
digitalWrite(buttonPins[i], LOW);
}
}
In gemeenschappelijke modus trekt het indrukken van een willekeurige knop de interrupt-pin laag en activeert de interrupt. Zodra dat gebeurt, herschikt de interrupt service routine de pinnen voor gedifferentieerde modus.
Gedifferentieerde modus
Zodra onze interrupt triggert, schakelen we snel over naar gedifferentieerde modus.
Gedifferentieerde modus is het tegenovergestelde van gemeenschappelijke modus. De interrupt-pin wordt OUTPUT en LOW en de rest INPUT_PULLUP.

void configureDistinct() {
pinMode(commonPin, OUTPUT);
digitalWrite(commonPin, LOW);
for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
}
In gedifferentieerde modus worden alleen de pinnen die overeenkomen met daadwerkelijk ingedrukte knoppen laag getrokken. We kunnen gemakkelijk over alle pinnen itereren om te achterhalen welke de interrupt heeft geactiveerd.
Daarna kan de Arduino terugschakelen naar gemeenschappelijke modus en wachten op de volgende interrupt. Of, afhankelijk van je toepassing, kan het in gedifferentieerde modus blijven en gebruikersinverwerken zoals normaal, voordat het terugschakelt naar gemeenschappelijke modus voordat de Arduino in slaap gaat.
Een complexer voorbeeld
Laten we iets complexers proberen. We koppelen een servo en mappen elke knop op een andere hoek. (1=0°, 2=20°... 10=120°) We voeden de Arduino met een paar batterijen.



In dit voorbeeld zetten we de Arduino na vijf seconden inactiviteit in power-down modus om stroom te besparen. Je kunt tutorials over slaapmodus online vinden. Daarnaast voeden we de servo via een transistor, zodat we hem uitschakelen wanneer hij niet in gebruik is.
De code voor dit voorbeeld is ook hieronder te vinden.
Code
Seriële logger
const int commonPin = 2;
const int buttonPins[] = {4,5,6,7,8,9,10,11,12,13};
unsigned long lastFire = 0;
void setup() {
configureCommon();
attachInterrupt(digitalPinToInterrupt(commonPin), pressInterrupt, FALLING);
Serial.begin(9600);
}
void loop() {
// Leeg!
}
void pressInterrupt() {
if (millis() - lastFire < 200) return; // Debounce
lastFire = millis();
configureDistinct();
for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
if (!digitalRead(buttonPins[i])) {
press(i);
}
}
configureCommon();
}
void configureCommon() {
pinMode(commonPin, INPUT_PULLUP);
for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
pinMode(buttonPins[i], OUTPUT);
digitalWrite(buttonPins[i], LOW);
}
}
void configureDistinct() {
pinMode(commonPin, OUTPUT);
digitalWrite(commonPin, LOW);
for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
}
void press(int button) {
Serial.println(button + 1);
}
Servo met slaapmodus
#include
#include
#include
const int commonPin = 2;
const int buttonPins[] = {4,5,6,7,8,9,10,11,12,13};
const int servoEnablePin = A1;
const int servoPin = A0;
Servo servo;
unsigned long lastFire = 0;
int status = 0;
void setup() {
pinMode(commonPin, INPUT_PULLUP);
configureCommon();
attachInterrupt(digitalPinToInterrupt(commonPin), pressInterrupt, FALLING);
servo.attach(servoPin);
pinMode(servoEnablePin, OUTPUT);
}
void loop() {
if (millis() - lastFire > 5000) {
digitalWrite(servoEnablePin, LOW);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();
}
delay(10);
}
void pressInterrupt() {
sleep_disable();
power_all_enable();
if (millis() - lastFire < 200) return;
lastFire = millis();
configureDistinct();
for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
if (!digitalRead(buttonPins[i])) {
press(i);
}
}
configureCommon();
}
void configureCommon() {
pinMode(commonPin, INPUT_PULLUP);
for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
pinMode(buttonPins[i], OUTPUT);
digitalWrite(buttonPins[i], LOW);
}
}
void configureDistinct() {
pinMode(commonPin, OUTPUT);
digitalWrite(commonPin, LOW);
for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
}
void press(int button) {
digitalWrite(servoEnablePin, HIGH);
servo.write(map(button, 0, 9, 0, 180));
}
Schema's
Bestanden: multiinterrupt_hoF76Oc4T5.fzz, servo_with_sleep_u9ZqxF0jhY.fzz
Productieproces
- Een DTMF-decoder bouwen met alleen een Arduino – Geen extra IC's nodig
- Bouw een indrukwekkend monitor Ambilight-systeem met Arduino
- Bouw een Ultrasonische Zweefapparaat met Arduino – Stap-voor-stap Handleiding
- Een slimme voltmeter bouwen met Arduino en smartphone – Eenvoudig DIY-project
- Real-time IoT hartslagmonitor met Arduino en MAX30100
- Arduino Uno WiFi Webserver: LED aan/uit schakelen via browser
- Slimme Parkeer Teller: Real-time Voertuigvolging met Arduino, Processing en PHP
- Bouw een geautomatiseerde Google Chrome Dino Game met Arduino
- Bouw een compacte FM-radio met Arduino Nano en RDA5807M
- Draadloze Arduino Programmeer Shield met HC-05 Bluetooth – Geen USB Nodig
- Bouw een Digitale Horloge op Arduino met een Finitie Toestandsmachine