C-Syntax-Cheatsheet

Generische Snippets für PSP. ⚠ hover für Stolpersteine. Click „Copy" zum Kopieren.

Keine Treffer.

01Setup & Boilerplate

F_CPU Definition

Taktfrequenz für delay-Makros. Vor delay.h definieren.

#define F_CPU 20000000UL  // 20 MHz Boardtakt
#include <util/delay.h>

Standard-Includes

Ü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

main() Skelett

Startpunkt: Init-Aufrufe, dann Endlosschleife.

int main(void) {
    // Initialisierung hier
    // z.B. lcd_init(); initInput();

    while (1) {
        // Hauptschleife
    }
    return 0;
}

02Datentypen

Unsigned Integer

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)

Signed Integer

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

Bool

Erfordert stdbool.h.

bool flag = true;
if (flag) { /* ... */ }

Cast

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

03Pointer

Pointer Basics

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)

Doppelpointer

Pointer auf Pointer — Adresse einer Pointervariable.

uint8_t  x = 42;
uint8_t *p  = &x;
uint8_t **pp = &p;
**pp = 7;           // x = 7

Pointer-Arithmetik

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]

Funktionspointer

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

Call by Reference

Funktion ändert Originalvariable über Pointer-Parameter.

void increment(uint8_t *val) {
    (*val)++;
}

uint8_t a = 5;
increment(&a);   // a == 6

04Arrays

Array deklarieren & initialisieren

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)

Array-Größe ermitteln

Anzahl Elemente per sizeof-Trick.

uint8_t arr[5];
size_t n = sizeof(arr) / sizeof(arr[0]);  // n = 5

Array iterieren

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
}

05Strings im Flash (PROGMEM)

String aus Flash (PSTR)

String-Literal landet im Flash, spart SRAM.

lcd_writeProgString(PSTR("Hallo Welt"));

PROGMEM-Variable

Konstantes Array fest im Flash ablegen.

const char text[] PROGMEM = "Im Flash";
const uint8_t table[] PROGMEM = {0xAA, 0x55, 0xFF};

Flash-Byte lesen

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
}

06Struct / Union / Enum

Struct mit typedef

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

Pointer auf Struct (->)

pIt->feld ist Kurzform für (*pIt).feld.

Item it = { .id = 1, .value = 1234 };
Item *pIt = &it;
uint8_t y = pIt->id;     // = 1

Union — Felder überlagern

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

Enum

Mit -fshort-enums (PSP-Default) nur 1 Byte groß.

typedef enum {
    OK,        // = 0
    BUSY,      // = 1
    ERROR      // = 2
} Status;

Status s = BUSY;

07Schleifen & Bedingungen

for-Schleife

Klassisch: init; cond; step.

for (uint8_t i = 0; i < 10; i++) {
    // ...
}

while-Schleife

Bedingung VOR jedem Durchlauf prüfen.

while (cond) {
    // ...
}

do-while

Mindestens 1× ausführen, dann prüfen.

do {
    // ...
} while (cond);

if / else

Mehrwege-Verzweigung.

if (a > b) {
    // ...
} else if (a == b) {
    // ...
} else {
    // ...
}

switch / case

Mehrwege-Verzweigung über Integer/Enum.

switch (val) {
    case 0:
        // ...
        break;
    case 1:
        // ...
        break;
    default:
        // ...
        break;
}

Ternärer Operator

Inline-if für einfache Wertewahl.

uint8_t max = (a > b) ? a : b;

Rückwärts zählen (signed!)

Iterator muss signed sein für Abbruch bei -1.

for (int8_t i = 9; i >= 0; i--) {
    // arr[i]
}

08Bitoperationen & Bitmasken

Bit setzen

Bit B = 1, andere bleiben unverändert.

REG |= (1 << B);

Bit löschen

Bit B = 0, andere bleiben unverändert.

REG &= ~(1 << B);

Bit toggeln

Bit B umschalten — XOR mit 1 invertiert.

REG ^= (1 << B);

Bit prüfen

If-Abfrage: ist Bit B gesetzt?

if (REG & (1 << B)) {
    // Bit B ist 1
}

Bit als 0/1 auslesen

Liefert 0 oder 1 — gut zum Zusammensetzen.

uint8_t bit = (REG >> B) & 1;

Mehrere Bits gleichzeitig

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

Maskieren (untere/obere Bits)

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

Bits zu Wert packen

Einzelne Bits an Position bringen und verodern.

uint8_t result = b0 | (b1 << 1) | (b2 << 2) | (b3 << 3);

Bitweises Komplement

Alle Bits invertieren.

uint8_t inv = ~val;     // 0xFF -> 0x00, 0x55 -> 0xAA

09Register: DDR / PORT / PIN

Pin als Output

DDR-Bit = 1 → Pin ist Ausgang.

DDRx |= (1 << n);    // PXn = Output

Pin als Input

DDR-Bit = 0 → Pin ist Eingang.

DDRx &= ~(1 << n);   // PXn = Input

Pull-Up aktivieren

Auf einem Input-Pin: PORT-Bit = 1 schaltet internen Pull-Up an.

DDRx  &= ~(1 << n);  // erst Input
PORTx |=  (1 << n);  // dann Pull-Up an

Output schreiben

PORT-Register steuert High/Low am Pin.

PORTx |=  (1 << n);   // high
PORTx &= ~(1 << n);   // low
PORTx ^=  (1 << n);   // toggeln

Eingang lesen

PIN-Register zeigt aktuellen Zustand am Pin.

if (PINx & (1 << n)) {
    // Pin ist high
}
uint8_t bit = (PINx >> n) & 1;

Ports am ATmega644 (Datenblatt)

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, ...

10Buttons (Framework-API)

Buttons initialisieren

Einmal in main() vor Hauptschleife aufrufen.

initInput();

Button-State auslesen

Liefert Nibble: Bit0..3 = Button 0..3 (1 = gedrückt).

uint8_t btns = getInput();
if (btns & (1 << 0)) { /* Button 0 gedrückt */ }
if (btns == 0)         { /* keine Taste */ }

Auf Tastendruck warten

Standard-Pacing-Pattern: warten → reagieren → Loslassen abwarten.

waitForInput();      // blockiert bis Taste gedrückt
// reagieren
waitForNoInput();    // blockiert bis alle losgelassen

11LEDs (Framework-API)

LEDs sind active-low

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

12LCD (Framework-API)

LCD initialisieren

Einmalig in main() ganz am Anfang.

lcd_init();

Display löschen / Cursor setzen

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

Werte ausgeben

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

String aus Flash

Standardweg für statische Texte.

lcd_writeProgString(PSTR("Hallo Welt"));

Spannung anzeigen

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"

13Interrupts

ISR — Interrupt Service Routine

Vektorname aus avr/io.h (z.B. TIMER0_COMPA_vect).

ISR(VEKTOR_vect) {
    // schnell rein, schnell raus
}

Interrupts global an/aus

sei() einmal nach Init aller ISRs aufrufen.

sei();   // Global Interrupt Enable
cli();   // Global Interrupt Disable (selten nötig)

volatile für ISR-Variablen

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;

Atomic-Block für Mehrbyte-Lesen

Konsistenter Snapshot einer Mehrbyte-volatile-Variable.

#include <util/atomic.h>

volatile uint16_t bigVar;
uint16_t snapshot;

ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    snapshot = bigVar;   // konsistent
}

14Timer-Pattern (generisch)

CTC-Mode aktivieren

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

Prescaler wählen

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 */;

Compare-Wert OCR setzen

Bei welchem Zählerstand soll Match-Interrupt feuern.

OCRnA = N;

Compare-Match-Interrupt aktivieren

TIMSK-Register aktiviert Interrupt-Quelle des Timers.

TIMSKn |= (1 << OCIEnA);   // Output Compare Match A IRQ
sei();                       // global enable nicht vergessen

Timer-ISR

Vektorname aus avr/io.h: TIMER0_COMPA_vect, TIMER1_COMPA_vect, …

ISR(TIMERn_COMPA_vect) {
    // läuft bei jedem Compare-Match
}

Periodendauer-Formel

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

15ADC-Pattern (generisch)

ADMUX konfigurieren

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) */;

ADCSRA konfigurieren

ADC einschalten und Prescaler festlegen.

// ADEN  = ADC einschalten
// ADPS-Bits = Prescaler (2,4,8,16,32,64,128)
ADCSRA = (1 << ADEN) | /* ADPS-Bits */;

Single Conversion (Polling)

Wandlung starten, busy-wait bis fertig.

ADCSRA |= (1 << ADSC);              // start
while (ADCSRA & (1 << ADSC)) { }    // warte bis ADSC = 0

ADC-Wert lesen

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-Pin vorbereiten

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)

16malloc / free

Speicher allokieren

Größe als N * sizeof(T) — Compiler rechnet Bytezahl korrekt.

uint16_t *buf = malloc(N * sizeof(uint16_t));
if (!buf) {
    // Fehler — kein Speicher
    return;
}

Allokierten Speicher nutzen

Wie normales Array.

buf[0] = 42;
*(buf + 1) = 99;
for (uint8_t i = 0; i < N; i++) {
    buf[i] = i;
}

Speicher freigeben

Pointer ungültig nach free.

free(buf);
buf = NULL;

17Delays

_delay_ms / _delay_us

F_CPU muss vorher definiert sein.

_delay_ms(100);   // 100 ms warten
_delay_us(50);    // 50 µs warten

Variable Wartezeit (Workaround)

Wenn Wartezeit zur Laufzeit feststeht: Schleife mit Konstante.

void wait_ms(uint16_t ms) {
    while (ms--) {
        _delay_ms(1);   // Argument konstant
    }
}

18Compiler-Flags (PSP-Makefile)

Aktive Flags & ihre Wirkung

Diese Flags ändern Sprache-Semantik — Code muss damit kompatibel sein.

FlagWirkung
-funsigned-charchar ist unsigned char (0..255)
-funsigned-bitfieldsBitfelder unsigned per Default
-fpack-structKein Padding in Strukturen — Felder back-to-back
-fshort-enumsEnum so klein wie nötig (oft 1 Byte)
-WerrorWarnings = Errors → Code kompiliert nicht
-std=c99C99-Standard (designated initializers, // Kommentare, …)

Warnings ernst nehmen

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