Programming lesson
CSE/ECE474 Lab 2 – Digitale I/O und Timing von Ausgängen: Schritt-für-Schritt-Tutorial
Lerne in diesem Tutorial, wie du mit dem Arduino Mega digitale Ausgänge steuerst, LEDs timst, Töne erzeugst und mehrere Tasks parallel ausführst – ganz ohne bestehende Bibliotheken. Ideal für Studierende der CSE/ECE474.
Einleitung: Warum Timing und direkte Registermanipulation wichtig sind
In der heutigen Welt der eingebetteten Systeme – von Smartwatches über KI-gesteuerte Roboter bis hin zu Gaming-Controllern – ist präzises Timing und die direkte Steuerung von Hardware-Registern essenziell. Das Lab CSE/ECE474 Lab 2 fordert dich heraus, ohne Bibliotheken wie digitalWrite() auszukommen und stattdessen die AVR-Register des Arduino Mega zu programmieren. Klingt nach viel Arbeit? Keine Sorge – dieses Tutorial führt dich durch alle Schritte, von der LED-Sequenz bis zur gleichzeitigen Ausgabe von Musik und LED-Matrix. Du wirst sehen: Sobald du die Register verstehst, hast du die volle Kontrolle über deinen Mikrocontroller.
Teil 1: Hardware-Bit-Manipulation – LEDs direkt ansteuern
1.1 Aufbau und erste Schritte mit pinMode() und digitalWrite()
Zuerst verkabelst du drei LEDs mit Vorwiderständen (250–500 Ohm) an den Pins 47, 48 und 49. Dann programmierst du eine einfache Sequenz: Jede LED leuchtet für 333 ms, dann wechselt die nächste – mit einer Gesamtperiode von 1 Sekunde. Das ist die Basis für alles Weitere.
void setup() {
pinMode(47, OUTPUT);
pinMode(48, OUTPUT);
pinMode(49, OUTPUT);
}
void loop() {
digitalWrite(47, HIGH);
delay(333);
digitalWrite(47, LOW);
digitalWrite(48, HIGH);
delay(333);
digitalWrite(48, LOW);
digitalWrite(49, HIGH);
delay(333);
digitalWrite(49, LOW);
}1.2 Identifiziere die Port- und Bitnummern
Im Datenblatt des ATmega2560 findest du: Pin 47 = Port L, Bit 4; Pin 48 = Port L, Bit 5; Pin 49 = Port L, Bit 6. Merke dir: DDRL steuert die Richtung, PORTL die Ausgabe.
1.3 Direkte Registermanipulation – ohne pinMode() und digitalWrite()
Jetzt wird’s spannend: Du setzt die Bits direkt in den Registern. Definiere Makros wie DDRL und PORTL und schreibe eigene Funktionen bit_set() und bit_clr().
#define LED1 4 // Port L, Bit 4
#define LED2 5
#define LED3 6
void setup() {
DDRL |= (1 << LED1) | (1 << LED2) | (1 << LED3); // Alle als Ausgang
}
void loop() {
PORTL |= (1 << LED1);
_delay_ms(333);
PORTL &= ~(1 << LED1);
PORTL |= (1 << LED2);
_delay_ms(333);
PORTL &= ~(1 << LED2);
PORTL |= (1 << LED3);
_delay_ms(333);
PORTL &= ~(1 << LED3);
}Dieser Ansatz ist nicht nur schneller, sondern gibt dir die Kontrolle, die du für komplexe Timing-Aufgaben brauchst – ähnlich wie ein E-Sport-Profi, der jede Tasteneingabe direkt an die Hardware weiterleitet, ohne Verzögerung durch Abstraktionsschichten.
Teil 2: 16-Bit Timer/Counter – Töne für deinen Speaker erzeugen
2.1 Timer4 konfigurieren
Der Arduino Mega hat mehrere 16-Bit-Timer. Für Lab 2 verwendest du Timer4, um einen Rechteckton auf Pin OC4A (Pin 6) zu erzeugen. Wähle den Fast-PWM-Modus mit 50% Tastverhältnis (duty cycle). Setze die Bits im Register TCCR4A und TCCR4B, ohne die anderen Bits zu stören.
void setup_timer4(uint16_t ocr) {
// Set Fast PWM, non-inverting, TOP=OCR4A
TCCR4A = (1 << WGM41) | (1 << COM4A0); // WGM41=1, COM4A0=1 toggles on compare
TCCR4B = (1 << WGM42) | (1 << CS40); // WGM42=1, Prescaler=1 (CS40)
OCR4A = ocr;
DDRH |= (1 << 3); // Set Pin 6 (OC4A) as output
}Die Frequenz berechnest du mit: f = 16 MHz / (2 * (OCR4A + 1)). Für 400 Hz ergibt sich OCR4A = 19999.
2.2 Eine Melodie abspielen
Dein Programm soll nacheinander 400 Hz, 250 Hz, 800 Hz und eine Sekunde Stille ausgeben. Nutze eine Schleife und rufe setup_timer4() mit den passenden Werten auf. Vergiss nicht, den Timer nach der Stille zu deaktivieren (TCCR4B = 0).
void play_tone(uint16_t freq, uint16_t duration_ms) {
if (freq == 0) {
TCCR4B = 0; // Stille
delay(duration_ms);
return;
}
uint16_t ocr = (uint16_t)(16000000UL / (2UL * freq)) - 1;
setup_timer4(ocr);
delay(duration_ms);
TCCR4B = 0; // Stille nach dem Ton (optional)
}Teste die Töne mit einem Oszilloskop – die Frequenz sollte genau stimmen. Falls der Ton verzerrt klingt, schalte einen Widerstand (ca. 100 Ohm) in Reihe zum Speaker. Das reduziert Obertöne und macht den Klang sauberer – ein Tipp, den selbst erfahrene Maker oft erst nach Trial-and-Error lernen.
Teil 3: Mehrere Tasks gleichzeitig – Round-Robin-Scheduling
3.1 Drei Tasks definieren
Jetzt kombinierst du LED-Sequenz (Task A), Tonausgabe (Task B) und einen Steuer-Task (Task C). Du verwendest globale Flags, damit Task C Tasks A und B starten und stoppen kann. Der loop() ruft nacheinander alle Tasks auf – ein einfaches Round-Robin-Verfahren.
volatile bool runA = false;
volatile bool runB = false;
void taskA() {
if (!runA) return;
// LED-Sequenz (wie in Teil 1.3)
}
void taskB() {
if (!runB) return;
// Ton-Sequenz (wie in Teil 2.2)
}
void taskC() {
static unsigned long lastSwitch = 0;
unsigned long now = millis();
// Phase 1: Task A 2s
if (now - lastSwitch < 2000) {
runA = true; runB = false;
}
// Phase 2: Task B 4s
else if (now - lastSwitch < 6000) {
runA = false; runB = true;
}
// Phase 3: Pause 1s
else if (now - lastSwitch < 7000) {
runA = false; runB = false;
}
else {
lastSwitch = now;
}
}
void loop() {
taskC();
taskA();
taskB();
}3.2 Erweiterung: Gleichzeitige Ausführung
In Teil 3.2 müssen Task A und Task B gleichzeitig laufen. Das erreichst du, indem du beide Flags auf true setzt. Achte darauf, dass die Tonausgabe nicht durch die delay()-Aufrufe der LED-Sequenz blockiert wird. Verwende stattdessen nicht-blockierende Verzögerungen mit millis() für die LEDs.
3.3 Musik spielen: „Mary Had a Little Lamb“
Ersetze die Ton-Sequenz durch die Noten des Kinderlieds. Die Frequenzen findest du im Anhang der Lab-Anleitung. Spiele jede Note für 250 ms (oder ein Vielfaches davon). Das klingt dann wie eine richtige Melodie – und nicht wie ein glitchiger Piepton. Dein Dozent wird es hören (und bewerten)!
Teil 4: Interaktives Display – 8×8 LED-Matrix mit Joystick
4.1 Ein Punkt wandert über die Matrix
Du steuerst einen einzelnen LED-Punkt mit einem 2-Wege-Thumbstick (XY-Joystick). Lies die analogen Werte ein und bilde sie auf die 8×8-Matrix ab. Verwende entweder eine „rohe“ Matrix mit 16 Pins oder ein MAX7219-Modul über SPI. Die Bibliothek LedControl oder eigener SPI-Code sind erlaubt.
#include <LedControl.h>
LedControl lc = LedControl(12, 11, 10, 1); // DIN, CLK, CS, Anzahl
int xPos = 3, yPos = 3;
void setup() {
lc.shutdown(0, false);
lc.setIntensity(0, 8);
lc.clearDisplay(0);
}
void loop() {
int x = analogRead(A0); // Thumbstick X
int y = analogRead(A1); // Thumbstick Y
// Karte 0-1023 auf 0-7
int newX = map(x, 0, 1023, 0, 7);
int newY = map(y, 0, 1023, 0, 7);
// Alte Position löschen, neue setzen
lc.setLed(0, xPos, yPos, false);
xPos = newX;
yPos = newY;
lc.setLed(0, xPos, yPos, true);
delay(50); // Entprellen
}4.2 Integration: Musik und Display gleichzeitig
Jetzt spielst du „Mary Had a Little Lamb“ und bewegst gleichzeitig den Punkt auf der Matrix. Da die Musik über Timer läuft (interruptfrei) und das Display nur im loop() aktualisiert wird, funktioniert beides parallel. Achte darauf, dass die Ton-Task nicht blockiert – verwende millis() für den Notenwechsel.
Fazit: Vom Register zum System
Dieses Lab zeigt dir, wie du mit einfachen Mitteln – direkter Registerzugriff, Timer und Round-Robin – mehrere Aufgaben gleichzeitig bewältigst. Das ist die Grundlage für jedes eingebettete System, ob in der Industrie, in Smart-Home-Geräten oder in KI-Robotern. Mit den erlernten Techniken kannst du eigene Projekte umsetzen: eine Ampelschaltung, ein MIDI-Player oder sogar ein kleines Spiel mit LED-Matrix und Sound. Viel Erfolg beim Lab – und denk dran: Die Oszilloskop-Messung ist dein bester Freund!