3.8 KiB
Aktions-Engine
Dateien: config/action.h, CButton.h/.cpp, CMainController.cpp (processEvents, execute_action_down, execute_action_up)
SAction-Struct
struct __attribute__((packed)) SAction {
ActionType type; // 1 Byte
uint16_t data; // 2 Bytes (Keycode, Consumer-Code, Command-ID oder Slot-Index)
};
// Gesamt: 3 Bytes (packed! ohne packed wären es 4 durch Alignment)
packed ist zwingend damit sizeof(SDeviceConfig) == 740 mit der C#-Serialisierung in VersaGUI übereinstimmt.
ActionType
| Typ | Wert | Bedeutung | data-Inhalt |
|---|---|---|---|
NONE |
0 | Keine Aktion | — |
HID_KEY |
1 | Tastendruck via USB HID Keyboard | Low-Byte = HID Keycode, High-Byte = Modifier |
HID_CONSUMER |
2 | Consumer Control (Volume, Media, …) | Consumer Usage ID |
HOST_COMMAND |
3 | Event an VersaGUI senden, App führt aus | Command-ID (frei definiert) |
MACRO |
4 | Makro-Sequenz aus NVM-Tabelle | Slot-Index 0–31 |
PROFILE_SWITCH |
5 | Aktives Profil wechseln | 0–2 = Ziel-Profil, 0xFF = nächstes Profil (Zyklus 0→1→2→0) |
execute_action_down() — Taste gedrückt (Hold-Start)
| ActionType | Verhalten |
|---|---|
HID_KEY |
usb_hid_send_key(keycode, modifier) — Taste bleibt gedrückt bis execute_action_up() |
HID_CONSUMER |
usb_hid_send_consumer(usage_id) — bleibt aktiv bis execute_action_up() |
HOST_COMMAND |
usb_serial_send(USB_EVT_KEY_DOWN, key_id) |
MACRO |
Volle Sequenz ausführen (Steps[slot], keycode==0 = Ende, delay 10+20 ms) |
PROFILE_SWITCH |
NVM laden → active_profile setzen → CRC neu berechnen → NVM speichern → init_buttons() |
NONE |
nop |
execute_action_up() — Taste losgelassen (Hold-Ende)
| ActionType | Verhalten |
|---|---|
HID_KEY |
usb_hid_release_key() |
HID_CONSUMER |
usb_hid_release_consumer() |
HOST_COMMAND |
— (optional: könnte USB_EVT_KEY_UP senden) |
MACRO/PROFILE_SWITCH/NONE |
nop |
PROFILE_SWITCH — Ablauf
SDeviceConfig cfg;
nvm_config_load(cfg); // komplette Config aus NVM
uint8_t target = (uint8_t)action.data;
if (target == 0xFF)
target = (cfg.active_profile + 1) % 3; // Zyklus
cfg.active_profile = target;
cfg.crc = nvm_config_crc(cfg); // CRC MUSS nach Änderung neu berechnet werden!
if (nvm_config_save(cfg)) // bool: false = NVM-Timeout
init_buttons();
Wichtig:
active_profileliegt im CRC-geschützten Bereich (ab Byte 7). Wird die CRC nicht aktualisiert, findet das nächstenvm_config_load()einen CRC-Fehler und lädt die Defaults (alle Aktionen NONE, alle LEDs Regenbogen).
Hold-Modell (HID-Keys und Consumer Controls)
Normale Tasten- und Media-Aktionen folgen dem Hold-Modell:
KEY_DOWN-Event vom Board → execute_action_down() → HID Key-Down senden
[Taste bleibt physisch gedrückt...]
KEY_UP-Event vom Board → execute_action_up() → HID Key-Up senden
Das OS erkennt die gedrückte Taste und startet sein eigenes Key-Repeat nach ~500 ms — wie auf einer normalen Tastatur.
Tap-Modell (Encoder CW/CCW)
Encoder-Bewegungen sind diskret (kein Halten möglich) und verwenden das Tap-Modell:
ENC_CW/ENC_CCW-Event → execute_action_down() + delay(10) + execute_action_up()
(Atomare Sequenz für jeden Encoder-Schritt.)
Work-Loop-Reihenfolge
void work() {
matrix_scan(); // → Events in Queue (KEY_DOWN, KEY_UP, ENC_CW, ENC_CCW)
poll_vendor(); // Serial-Pakete verarbeiten (PC↔Board Kommandos)
processEvents(); // → execute_action_down/up() aufrufen
updateLEDs(); // Dirty-LEDs aktualisieren
}
processEvents() verarbeitet:
KEY_DOWN→execute_action_down()KEY_UP→execute_action_up()ENC_CW/ENC_CCW→execute_action_down()+delay(10)+execute_action_up()