Hinweise zur C-Programmierung
Viele nützliche Hinweise zur C-Programmierung in Michael Barr - Embedded C Coding Standard (siehe 1. Kapitel)
Namenskonventionen
- Alle Variablen, Funktionen und Module werden im
snake_case
geschrieben. - Datentypen bekommen zusätzliche den Präfix
_t
: Beispieluint8_t
- Globale Funktionen sollten immer mit den zugehörigen Modulnamen beginnen.
- Konstanten verwenden großgeschreiben:
SNAKE_CASE
Module
- Aufteilen des Programm-Codes in mehrere Module!
- Ein Modul besteht in der Regel aus einer Header- und einer C-Datei.
- Diese Tragen den gleichen Namen!
Aufteilung des Quellcodes in C- und Header-Datei
Inhalt | C-Datei bsp.c | Header bsp.h |
---|---|---|
Kopf | ||
Globale Variablen | ||
Lokale Variablen | --- | |
Prototypen lokaler Funktionen | --- | |
Globale Funktionen | ||
Lokale Funktionen | --- | |
--- |
Konstanten
-
Verwenden von Macros:
- Beispiel:
#define TEST (0x04)
- Macros besitzten keine Datentypen und werden durch den Präcompiler 1:1 in den Quelltext eingefügt.
- Immer zusätzliche Klammern setzen!
Beispiel einer fehlerhaften
#define
-Anweisung
- Macros können global in Header definiert werden, oder lokal in der C-Datei.
- Im Header sollte der Name immer mit dem Modulnamen beginnenBSP_X
um Kollisionen mit anderen Modulen zu verhindern.
- Verwenden vonconst
:
- Beispiel:const uint8_t KONSTANTE = 0x55;
- Eine echte Konstante mit Datentyp!
- Global: "doppelte" Definition
- In C-Datei:const uint8_t BSP_KONSTANTE = 0x55;
- In Headerconst extern uint8_t BSP_KONSTANTE;
- Lokal:static
hinzufügen!:static const uint8_t KONSTANTE = 0x55;
- Praxis-Beispiel: Registermap eines SPI-Slaves als eigene Header-Datei: https://github.com/jhester/msp430-cc1101/blob/master/registers.h - Beispiel:
Enumerations
- Enumerations sind eigenen Datentypen in C, die eine Liste von Konstanten enthalten, die automatisch von C-Compiler durchnummeriert werden können.
- Beispiel:
Automatisch nummeriertes Enum mit Längenangabe
typedef enum {
ALARM_BATTERY_LEVEL = 0,
ALARM_WATCHDOG,
ALARM_KEY_PRESSED,
ALARM_BATTERY,
ALARM_LENGTH
} alarms_t;
- Enumations können auch eigene Werte zugewiesen werden.
- Auf diese Art und Weise können Bitfelder definiert werden.
Enum für ein Bitfeld
typedef enum {
ISR_WDT = (1 << 0),
ISR_I2C_UPDATE = (1 << 1),
ISR_BUTTON_PRESSED = (1 << 2),
ISR_BATTERY_READY = (1 << 3),
ISR_ALARM_BATTERY_LEVEL = (1 << 5),
ISR_ALARM_WATCHDOG = (1 << 6),
ISR_BUTTON2_PRESSED = (1 << 7)
} isr_flags_t;
Structs
- Der Struct ist ein eigener Datentyp, in dem mehrere Variablen verschiedenen Types in ein Objekt zusammengefasst werden können.
Beispiel-Struct
typedef enum {
GENDER_MALE = 1, GENDER_FEMALE = 2
} gender_t;
typedef struct {
uint8_t age;
gender_t gender;
uint16_t size_mm;
char name[8];
uint32_t day_of_birth;
} person_t;
const person_t my_person = { .age = 23, .gender = GENDER_MALE, .size = 0x2233,
.name = "Robert", .day_of_birth = 0xaabbccdd };
- So kann z. B. die Übergabe eines Stucts in einer Funktion erfolgen, ohne dass die Daten einzeln übergeben werden müssen.
- Organisation von komplexen Datenstrukturen!
Quellcode-Dokumentation mit Doxygen
- Verwenden Sie Doxygen-Kommentare, um alle Module und Funktionen zu beschreiben.
- Beispiel aus der alten Vorlesung: https://github.com/RobFro96/VLMikrorechnerarchitekturen/blob/master/3_2a_lcd/lcd.c
- Code Composer unterstützt Doxygen: siehe Einstellungen in der Anleitung!
- Durch die Installation von Doxygen kann ein HTML- oder PDF-Dokumentation erstellt werden.
siehe https://www.doxygen.nl/index.html