F_CPU Definition
⚠Taktfrequenz für delay-Makros. Vor delay.h definieren.
#define F_CPU 20000000UL // 20 MHz Boardtakt
#include <util/delay.h>
Generische Snippets für PSP. ⚠ hover für Stolpersteine. Click „Copy" zum Kopieren.
Taktfrequenz für delay-Makros. Vor delay.h definieren.
#define F_CPU 20000000UL // 20 MHz Boardtakt
#include <util/delay.h>
Üblicher Header-Block für AVR-Quelldateien.
#include <avr/io.h> // Register, Bit-Namen
#include <avr/interrupt.h> // ISR(), sei(), cli()
#include <avr/pgmspace.h> // PSTR, pgm_read_byte
#include <util/delay.h> // _delay_ms / _delay_us
#include <util/atomic.h> // ATOMIC_BLOCK
#include <stdint.h> // uint8_t etc.
#include <stdlib.h> // malloc, free
#include <stdbool.h> // bool, true, false
Startpunkt: Init-Aufrufe, dann Endlosschleife.
int main(void) {
// Initialisierung hier
// z.B. lcd_init(); initInput();
while (1) {
// Hauptschleife
}
return 0;
}
Vorzeichenlose Integer aus stdint.h.
uint8_t a; // 0..255 (1 Byte)
uint16_t b; // 0..65535 (2 Byte)
uint32_t c; // 0..~4 Mrd (4 Byte)
uint64_t d; // 0..~1.8e19 (8 Byte)
Mit Vorzeichen — für Differenzen, Rückwärts-Schleifen.
int8_t i; // -128..127
int16_t j; // -32768..32767
int32_t k; // ca. -2 Mrd..2 Mrd
Erfordert stdbool.h.
bool flag = true;
if (flag) { /* ... */ }
Typumwandlung explizit. (void) unterdrückt Unused-Warning.
uint8_t small = (uint8_t) bigVal; // narrowing
uint16_t wide = (uint16_t) byte << 8; // bevor shift!
(void) unused; // Warning unterdrücken
Adresse holen mit &, Wert über * lesen/schreiben.
uint8_t x = 42;
uint8_t *p = &x; // p zeigt auf x
uint8_t v = *p; // v = 42 (lesen)
*p = 99; // x ist jetzt 99 (schreiben)
Pointer auf Pointer — Adresse einer Pointervariable.
uint8_t x = 42;
uint8_t *p = &x;
uint8_t **pp = &p;
**pp = 7; // x = 7
arr[i] ist Syntax-Zucker für *(arr+i).
uint8_t arr[5] = {1,2,3,4,5};
uint8_t *p = arr;
uint8_t v = *(p + 2); // = arr[2] = 3
p++; // zeigt jetzt auf arr[1]
Pointer auf Funktion — für Callbacks, Tabellen.
uint8_t myFunc(uint8_t x) { return x + 1; }
uint8_t (*fp)(uint8_t) = myFunc;
uint8_t y = fp(5); // = 6
Funktion ändert Originalvariable über Pointer-Parameter.
void increment(uint8_t *val) {
(*val)++;
}
uint8_t a = 5;
increment(&a); // a == 6
Größe muss Compile-Time-Konstante sein.
uint8_t arr[5] = {1, 2, 3, 4, 5};
uint8_t buf[10] = {0}; // alle 0
char str[] = "Hallo"; // Größe = 6 (inkl. \0)
Anzahl Elemente per sizeof-Trick.
uint8_t arr[5];
size_t n = sizeof(arr) / sizeof(arr[0]); // n = 5
Klassische Indexiteration.
uint8_t arr[5] = {1,2,3,4,5};
for (uint8_t i = 0; i < sizeof(arr)/sizeof(arr[0]); i++) {
// arr[i] verwenden
}
String-Literal landet im Flash, spart SRAM.
lcd_writeProgString(PSTR("Hallo Welt"));
Konstantes Array fest im Flash ablegen.
const char text[] PROGMEM = "Im Flash";
const uint8_t table[] PROGMEM = {0xAA, 0x55, 0xFF};
Auf AVR: Flash und SRAM separate Adressräume — eigene Lese-Instruktion.
const char *p = text; // Pointer auf PROGMEM
char c;
while ((c = pgm_read_byte(p++)) != 0) {
// c verarbeiten
}
Eigenen Typ definieren, dann normal nutzen.
typedef struct {
uint8_t id;
uint16_t value;
} Item;
Item it = { .id = 1, .value = 1234 }; // designated init
uint8_t x = it.id; // Punkt-Operator
pIt->feld ist Kurzform für (*pIt).feld.
Item it = { .id = 1, .value = 1234 };
Item *pIt = ⁢
uint8_t y = pIt->id; // = 1
Mehrere Felder teilen denselben Speicher.
typedef union {
uint16_t whole;
struct {
uint8_t low;
uint8_t high;
} parts;
} U;
U u; u.whole = 0xABCD;
// u.parts.low = 0xCD
// u.parts.high = 0xAB
Mit -fshort-enums (PSP-Default) nur 1 Byte groß.
typedef enum {
OK, // = 0
BUSY, // = 1
ERROR // = 2
} Status;
Status s = BUSY;
Klassisch: init; cond; step.
for (uint8_t i = 0; i < 10; i++) {
// ...
}
Bedingung VOR jedem Durchlauf prüfen.
while (cond) {
// ...
}
Mindestens 1× ausführen, dann prüfen.
do {
// ...
} while (cond);
Mehrwege-Verzweigung.
if (a > b) {
// ...
} else if (a == b) {
// ...
} else {
// ...
}
Mehrwege-Verzweigung über Integer/Enum.
switch (val) {
case 0:
// ...
break;
case 1:
// ...
break;
default:
// ...
break;
}
Inline-if für einfache Wertewahl.
uint8_t max = (a > b) ? a : b;
Iterator muss signed sein für Abbruch bei -1.
for (int8_t i = 9; i >= 0; i--) {
// arr[i]
}
Bit B = 1, andere bleiben unverändert.
REG |= (1 << B);
Bit B = 0, andere bleiben unverändert.
REG &= ~(1 << B);
Bit B umschalten — XOR mit 1 invertiert.
REG ^= (1 << B);
If-Abfrage: ist Bit B gesetzt?
if (REG & (1 << B)) {
// Bit B ist 1
}
Liefert 0 oder 1 — gut zum Zusammensetzen.
uint8_t bit = (REG >> B) & 1;
Maske mit | bauen, dann setzen oder löschen.
REG |= (1 << A) | (1 << B); // A und B setzen
REG &= ~((1 << A) | (1 << B)); // A und B löschen
Bestimmte Bits behalten, Rest auf 0 setzen.
uint8_t low4 = val & 0x0F; // untere 4 Bit
uint8_t high4 = val & 0xF0; // obere 4 Bit
uint8_t mask = val & 0b11000011; // nur Bit 0,1,6,7
Einzelne Bits an Position bringen und verodern.
uint8_t result = b0 | (b1 << 1) | (b2 << 2) | (b3 << 3);
Alle Bits invertieren.
uint8_t inv = ~val; // 0xFF -> 0x00, 0x55 -> 0xAA
DDR-Bit = 1 → Pin ist Ausgang.
DDRx |= (1 << n); // PXn = Output
DDR-Bit = 0 → Pin ist Eingang.
DDRx &= ~(1 << n); // PXn = Input
Auf einem Input-Pin: PORT-Bit = 1 schaltet internen Pull-Up an.
DDRx &= ~(1 << n); // erst Input
PORTx |= (1 << n); // dann Pull-Up an
PORT-Register steuert High/Low am Pin.
PORTx |= (1 << n); // high
PORTx &= ~(1 << n); // low
PORTx ^= (1 << n); // toggeln
PIN-Register zeigt aktuellen Zustand am Pin.
if (PINx & (1 << n)) {
// Pin ist high
}
uint8_t bit = (PINx >> n) & 1;
Alle Ports 8 Pins breit. DDR/PORT/PIN je Port.
// PORTA, PORTB, PORTC, PORTD (je Pins 0..7)
// Register je Port: DDRx, PORTx, PINx
// Bit-Namen aus avr/io.h: PA0..PA7, PB0..PB7, ...
0 = LED an, 1 = LED aus.
// Direktzugriff (Beispiel-Port)
PORTD &= ~(1 << n); // LED n AN
PORTD |= (1 << n); // LED n AUS
PORTD ^= (1 << n); // LED n toggeln
Einmalig in main() ganz am Anfang.
lcd_init();
Cursor-Positionierung 16×2.
lcd_clear(); // alles löschen, Cursor (0,0)
lcd_line1(); // Cursor in Zeile 1, Spalte 0
lcd_line2(); // Cursor in Zeile 2, Spalte 0
lcd_goto(zeile, spalte); // explizite Position
Verschiedene Ausgabe-Funktionen je Datentyp.
lcd_writeChar('A'); // ein Zeichen
lcd_writeDec(42); // dezimal
lcd_writeHexByte(0xFF); // 2 Hex-Zeichen
lcd_writeHexWord(0xABCD); // 4 Hex-Zeichen
lcd_write32bitHex(0x12345678);// 8 Hex-Zeichen
Standardweg für statische Texte.
lcd_writeProgString(PSTR("Hallo Welt"));
Diskreten Wert → "X.XXXV"-String. Macht intern Float-Berechnung.
// Ergebnis = (wert / max) * vRef
lcd_writeVoltage(adcVal, max, vRef);
// z.B. lcd_writeVoltage(113, 255, 5) -> "2.215V"
Vektorname aus avr/io.h (z.B. TIMER0_COMPA_vect).
ISR(VEKTOR_vect) {
// schnell rein, schnell raus
}
sei() einmal nach Init aller ISRs aufrufen.
sei(); // Global Interrupt Enable
cli(); // Global Interrupt Disable (selten nötig)
Jede zwischen ISR und Hauptcode geteilte Variable braucht volatile.
volatile uint8_t flag = 0;
ISR(VEKTOR_vect) {
flag = 1;
}
// Hauptcode
while (!flag) {
// warte
}
flag = 0;
Konsistenter Snapshot einer Mehrbyte-volatile-Variable.
#include <util/atomic.h>
volatile uint16_t bigVar;
uint16_t snapshot;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
snapshot = bigVar; // konsistent
}
Clear Timer on Compare Match — Counter wird bei OCR-Treffer auf 0 gesetzt.
// WGM-Bits (Mode Select) → CTC, siehe Datenblatt
TCCRnA |= (1 << WGMn1);
TCCRnA &= ~(1 << WGMn0);
// ggf. weiteres WGM-Bit in TCCRnB
CSn-Bits konfigurieren Taktteilung. Welche Kombination = welcher Faktor → Datenblatt.
// Prescaler: 1, 8, 64, 256, 1024 (typisch für Timer0)
TCCRnB |= /* (1 << CSnX) | ... */; // gewünschten Prescaler aktivieren
TCCRnB &= ~/* nicht-genutzte CS-Bits */;
Bei welchem Zählerstand soll Match-Interrupt feuern.
OCRnA = N;
TIMSK-Register aktiviert Interrupt-Quelle des Timers.
TIMSKn |= (1 << OCIEnA); // Output Compare Match A IRQ
sei(); // global enable nicht vergessen
Vektorname aus avr/io.h: TIMER0_COMPA_vect, TIMER1_COMPA_vect, …
ISR(TIMERn_COMPA_vect) {
// läuft bei jedem Compare-Match
}
Wie lange dauert ein Timer-Zyklus.
// T = (Prescaler * (OCR + 1)) / F_CPU
//
// Beispiel: Prescaler=64, OCR=249, F_CPU=20 MHz
// T = (64 * 250) / 20_000_000 = 800 µs
REFS = Vref-Quelle, ADLAR = Justierung, MUX = Kanal.
// REFS-Bits: Vref (AVcc / interne 1.1 oder 2.56V / extern)
// ADLAR=0 → right-adjusted (volle 10 Bit in ADCH:ADCL)
// MUX0..4 → ADC-Kanal-Auswahl
ADMUX = /* (REFS-Bits) | (ADLAR-Bit) | (kanal & 0x07) */;
ADC einschalten und Prescaler festlegen.
// ADEN = ADC einschalten
// ADPS-Bits = Prescaler (2,4,8,16,32,64,128)
ADCSRA = (1 << ADEN) | /* ADPS-Bits */;
Wandlung starten, busy-wait bis fertig.
ADCSRA |= (1 << ADSC); // start
while (ADCSRA & (1 << ADSC)) { } // warte bis ADSC = 0
Reihenfolge ist wichtig: ADCL vor ADCH.
uint16_t value = ADCL; // immer ZUERST low
value |= (ADCH << 8); // dann high
// oder kompakt (Compiler hält Reihenfolge):
uint16_t v = ADCL | (ADCH << 8);
ADC-Eingang als Input ohne Pull-Up.
DDRA &= ~(1 << n); // ADC-Pin als Input
PORTA &= ~(1 << n); // Pull-Up AUS (würde messen verfälschen)
Größe als N * sizeof(T) — Compiler rechnet Bytezahl korrekt.
uint16_t *buf = malloc(N * sizeof(uint16_t));
if (!buf) {
// Fehler — kein Speicher
return;
}
Wie normales Array.
buf[0] = 42;
*(buf + 1) = 99;
for (uint8_t i = 0; i < N; i++) {
buf[i] = i;
}
Pointer ungültig nach free.
free(buf);
buf = NULL;
F_CPU muss vorher definiert sein.
_delay_ms(100); // 100 ms warten
_delay_us(50); // 50 µs warten
Wenn Wartezeit zur Laufzeit feststeht: Schleife mit Konstante.
void wait_ms(uint16_t ms) {
while (ms--) {
_delay_ms(1); // Argument konstant
}
}
Diese Flags ändern Sprache-Semantik — Code muss damit kompatibel sein.
| Flag | Wirkung |
|---|---|
-funsigned-char | char ist unsigned char (0..255) |
-funsigned-bitfields | Bitfelder unsigned per Default |
-fpack-struct | Kein Padding in Strukturen — Felder back-to-back |
-fshort-enums | Enum so klein wie nötig (oft 1 Byte) |
-Werror | Warnings = Errors → Code kompiliert nicht |
-std=c99 | C99-Standard (designated initializers, // Kommentare, …) |
Typische Warnungen und ihre Bedeutung.
// "unused parameter":
void f(uint8_t x) { (void)x; /* ... */ }
// "comparison between signed and unsigned":
// → einen der beiden casten
// "implicit declaration of function":
// → Header fehlt oder Prototyp fehlt