forked from jappel/VersaMCU
Semi working profiles and longer macros
This commit is contained in:
parent
7169d3bbba
commit
098a166a9f
9 changed files with 257 additions and 108 deletions
|
|
@ -7,7 +7,8 @@ enum class ActionType : uint8_t
|
|||
HID_KEY, // Standard-Keyboard-Keycode (direkt in Firmware gesendet)
|
||||
HID_CONSUMER, // Consumer-Control-Keycode (Volume, Media, …)
|
||||
HOST_COMMAND, // Command-ID → Windows-App führt aus (URL, Programm, …)
|
||||
MACRO, // Makro-Slot (data = Slot-Index 0–31) → bis zu 4 HID-Keys sequenziell
|
||||
MACRO, // Makro-Slot (data = Slot-Index 0–31) → bis zu 8 HID-Keys sequenziell
|
||||
PROFILE_SWITCH, // Profil wechseln (data = Profil-Index 0–2); speichert in NVM
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) SAction
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// macro_config.cpp
|
||||
// NVM-Zugriff für die Makro-Tabelle (Row 1, 0x1FF00).
|
||||
// NVM-Zugriff für die Makro-Tabelle (Row 0+1, 0x1FB00–0x1FCFF, 512 Bytes).
|
||||
// Nutzt dieselben NVMCTRL-Hilfsfunktionen wie nvm_config.cpp (dupliziert,
|
||||
// da static – kein gemeinsamer Header für interne NVM-Helfer).
|
||||
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
#include <Arduino.h>
|
||||
#include <string.h>
|
||||
|
||||
static const uint32_t k_macro_addr = 0x1FF00UL; // Row 1 (256B nach Row 0)
|
||||
static const uint32_t k_macro_addr = 0x1FB00UL; // Row 0+1 (zwei Rows à 256B)
|
||||
|
||||
static void nvm_wait() { while (!NVMCTRL->INTFLAG.bit.READY) {} }
|
||||
static void nvm_exec(uint16_t cmd)
|
||||
|
|
@ -37,7 +37,7 @@ bool macro_config_load(SMacroTable& tbl)
|
|||
{
|
||||
memcpy(&tbl, reinterpret_cast<const void*>(k_macro_addr), sizeof(tbl));
|
||||
|
||||
// Prüfen ob Row 1 noch gelöscht ist (alle 0xFF = nie beschrieben)
|
||||
// Prüfen ob beide Rows noch gelöscht sind (alle 0xFF = nie beschrieben)
|
||||
const uint8_t* raw = reinterpret_cast<const uint8_t*>(&tbl);
|
||||
bool all_ff = true;
|
||||
for (uint16_t i = 0; i < sizeof(tbl); i++) {
|
||||
|
|
@ -55,12 +55,17 @@ void macro_config_save(const SMacroTable& tbl)
|
|||
// Auf 4-Byte-ausgerichteten Puffer kopieren bevor nvm_write_page ihn als uint32_t* liest.
|
||||
// SMacroTable ist __attribute__((packed)) und könnte unaligned liegen →
|
||||
// direkter uint32_t*-Cast würde auf Cortex-M0+ einen HardFault auslösen.
|
||||
uint8_t aligned_buf[256] __attribute__((aligned(4)));
|
||||
uint8_t aligned_buf[512] __attribute__((aligned(4)));
|
||||
memcpy(aligned_buf, &tbl, sizeof(tbl));
|
||||
|
||||
NVMCTRL->CTRLB.bit.MANW = 1;
|
||||
|
||||
// Beide Rows löschen (Row 0: 0x1FB00, Row 1: 0x1FC00)
|
||||
nvm_erase_row(k_macro_addr);
|
||||
for (uint8_t p = 0; p < 4; p++) {
|
||||
nvm_erase_row(k_macro_addr + 256);
|
||||
|
||||
// 8 Pages à 64B schreiben
|
||||
for (uint8_t p = 0; p < 8; p++) {
|
||||
nvm_write_page(k_macro_addr + p * 64, aligned_buf + p * 64);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
// macro_config.h
|
||||
// Makro-Tabelle: bis zu 32 Slots, je 4 HID-Key-Steps.
|
||||
// Gespeichert in NVM Row 1 (0x1FF00, 256 Bytes).
|
||||
// Makro-Tabelle: 32 Slots, je 8 HID-Key-Steps.
|
||||
// Gespeichert in NVM Row 0+1 (0x1FB00–0x1FCFF, 512 Bytes).
|
||||
//
|
||||
// Slot-Zuweisung (vom Windows-App vergeben, Board speichert blind):
|
||||
// Slot 0–19 : MX-Buttons (mx_idx)
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#define MACRO_SLOTS 32
|
||||
#define MACRO_MAX_STEPS 4
|
||||
#define MACRO_MAX_STEPS 8
|
||||
|
||||
// Ein einzelner HID-Key-Step im Makro
|
||||
struct __attribute__((packed)) SMacroStep
|
||||
|
|
@ -22,15 +22,15 @@ struct __attribute__((packed)) SMacroStep
|
|||
uint8_t modifier; // HID Modifier-Byte (Ctrl=0x01, Shift=0x02, Alt=0x04, GUI=0x08)
|
||||
};
|
||||
|
||||
// Komplette Makro-Tabelle (32 × 4 × 2 = 256 Bytes = eine NVM-Row)
|
||||
// Komplette Makro-Tabelle (32 × 8 × 2 = 512 Bytes = zwei NVM-Rows)
|
||||
struct __attribute__((packed)) SMacroTable
|
||||
{
|
||||
SMacroStep steps[MACRO_SLOTS][MACRO_MAX_STEPS];
|
||||
};
|
||||
|
||||
// Makro-Tabelle aus NVM lesen (Row 1: 0x1FF00).
|
||||
// Makro-Tabelle aus NVM lesen (Row 0+1: 0x1FB00).
|
||||
// Gibt false zurück wenn der Flash-Bereich noch gelöscht (0xFF) war → leere Tabelle geladen.
|
||||
bool macro_config_load(SMacroTable& tbl);
|
||||
|
||||
// Makro-Tabelle in NVM schreiben (löscht Row 1, schreibt 4 Pages).
|
||||
// Makro-Tabelle in NVM schreiben (löscht Row 0+1, schreibt 8 Pages).
|
||||
void macro_config_save(const SMacroTable& tbl);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
// nvm_config.cpp
|
||||
// NVM-Zugriff für SDeviceConfig (3 Rows ab 0x1FD00, 768B gesamt, 740B genutzt).
|
||||
|
||||
#include "nvm_config.h"
|
||||
#include <Arduino.h>
|
||||
#include <string.h>
|
||||
|
||||
// ── Flash-Adresse (aus Linkerscript) ─────────────────────────────────────────
|
||||
// Kein separates Linker-Symbol nötig – Adresse ist fix und bekannt.
|
||||
static const uint32_t k_config_addr = 0x1FE00UL;
|
||||
static const uint32_t k_config_addr = 0x1FD00UL; // Row 0–2 der Config
|
||||
|
||||
// SAMD21 NVMCTRL ──────────────────────────────────────────────────────────────
|
||||
// Row = 256 Bytes = 4 Pages à 64 Bytes
|
||||
// Schreiben: Row löschen (ER), dann seitenweise schreiben (WP)
|
||||
// ── NVMCTRL-Hilfsfunktionen ───────────────────────────────────────────────────
|
||||
|
||||
static void nvm_wait()
|
||||
{
|
||||
|
|
@ -30,27 +29,23 @@ static void nvm_erase_row(uint32_t addr)
|
|||
|
||||
static void nvm_write_page(uint32_t addr, const uint8_t* data)
|
||||
{
|
||||
// Page-Buffer löschen
|
||||
nvm_exec(NVMCTRL_CTRLA_CMD_PBC);
|
||||
|
||||
// 64 Bytes in den Page-Buffer schreiben (32-Bit-Zugriffe)
|
||||
volatile uint32_t* dst = reinterpret_cast<volatile uint32_t*>(addr);
|
||||
const uint32_t* src = reinterpret_cast<const uint32_t*>(data);
|
||||
for (uint8_t i = 0; i < 64 / 4; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
|
||||
// Page programmieren
|
||||
NVMCTRL->ADDR.reg = addr / 2;
|
||||
nvm_exec(NVMCTRL_CTRLA_CMD_WP);
|
||||
}
|
||||
|
||||
// ── CRC16 (CCITT, Poly 0x1021) ────────────────────────────────────────────────
|
||||
// ── CRC16-CCITT (Poly 0x1021) ─────────────────────────────────────────────────
|
||||
|
||||
uint16_t nvm_config_crc(const SDeviceConfig& cfg)
|
||||
{
|
||||
// CRC über alles nach dem crc-Feld (ab Byte 7)
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(&cfg) + offsetof(SDeviceConfig, mx_actions);
|
||||
uint16_t len = sizeof(SDeviceConfig) - offsetof(SDeviceConfig, mx_actions);
|
||||
// CRC über alles nach dem crc-Feld (ab Byte 7: active_profile … Ende Profil 2)
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(&cfg) + offsetof(SDeviceConfig, active_profile);
|
||||
uint16_t len = sizeof(SDeviceConfig) - offsetof(SDeviceConfig, active_profile);
|
||||
uint16_t crc = 0xFFFF;
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
crc ^= static_cast<uint16_t>(data[i]) << 8;
|
||||
|
|
@ -62,36 +57,48 @@ uint16_t nvm_config_crc(const SDeviceConfig& cfg)
|
|||
}
|
||||
|
||||
// ── Defaults ─────────────────────────────────────────────────────────────────
|
||||
|
||||
void nvm_config_defaults(SDeviceConfig& cfg)
|
||||
{
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.magic = NVM_CONFIG_MAGIC;
|
||||
cfg.version = NVM_CONFIG_VERSION;
|
||||
cfg.magic = NVM_CONFIG_MAGIC;
|
||||
cfg.version = NVM_CONFIG_VERSION;
|
||||
cfg.active_profile = 0;
|
||||
cfg.global_brightness = 255;
|
||||
|
||||
// Alle Aktionen: NONE
|
||||
for (uint8_t i = 0; i < 20; i++)
|
||||
cfg.mx_actions[i] = {ActionType::NONE, 0};
|
||||
for (uint8_t e = 0; e < 4; e++)
|
||||
for (uint8_t a = 0; a < 3; a++)
|
||||
cfg.enc_actions[e][a] = {ActionType::NONE, 0};
|
||||
cfg.enc_sensitivity[e] = 1;
|
||||
|
||||
// Base-LEDs: warm-weiß
|
||||
for (uint8_t i = 0; i < 20; i++) {
|
||||
cfg.led_r[i] = 80;
|
||||
cfg.led_g[i] = 40;
|
||||
cfg.led_b[i] = 0;
|
||||
}
|
||||
for (uint8_t p = 0; p < 3; p++) {
|
||||
SDeviceProfile& prof = cfg.profiles[p];
|
||||
|
||||
// LED-Animationen: Regenbogen (COLOR_CYCLE=5) mit 4s Periode als Standard
|
||||
for (uint8_t i = 0; i < 20; i++) {
|
||||
cfg.led_anim[i] = 5; // LEDAnim::COLOR_CYCLE
|
||||
cfg.led_period_ms[i] = 4000;
|
||||
// Alle Aktionen: NONE
|
||||
for (uint8_t i = 0; i < 20; i++)
|
||||
prof.mx_actions[i] = {ActionType::NONE, 0};
|
||||
for (uint8_t e = 0; e < 4; e++)
|
||||
for (uint8_t a = 0; a < 3; a++)
|
||||
prof.enc_actions[e][a] = {ActionType::NONE, 0};
|
||||
|
||||
// Base-LEDs: warm-weiß
|
||||
for (uint8_t i = 0; i < 20; i++) {
|
||||
prof.led_r[i] = 80;
|
||||
prof.led_g[i] = 40;
|
||||
prof.led_b[i] = 0;
|
||||
prof.led_brightness[i] = 255;
|
||||
}
|
||||
|
||||
// LED-Animationen: Regenbogen mit 4s Periode
|
||||
for (uint8_t i = 0; i < 20; i++) {
|
||||
prof.led_anim[i] = 5; // LEDAnim::COLOR_CYCLE
|
||||
prof.led_period_ms[i] = 4000;
|
||||
}
|
||||
}
|
||||
|
||||
cfg.crc = nvm_config_crc(cfg);
|
||||
}
|
||||
|
||||
// ── Laden ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
bool nvm_config_load(SDeviceConfig& cfg)
|
||||
{
|
||||
memcpy(&cfg, reinterpret_cast<const void*>(k_config_addr), sizeof(cfg));
|
||||
|
|
@ -100,25 +107,31 @@ bool nvm_config_load(SDeviceConfig& cfg)
|
|||
if (cfg.version != NVM_CONFIG_VERSION) { nvm_config_defaults(cfg); return false; }
|
||||
if (cfg.crc != nvm_config_crc(cfg)) { nvm_config_defaults(cfg); return false; }
|
||||
|
||||
// Profil-Index absichern
|
||||
if (cfg.active_profile >= 3) cfg.active_profile = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Speichern ─────────────────────────────────────────────────────────────────
|
||||
|
||||
void nvm_config_save(const SDeviceConfig& cfg)
|
||||
{
|
||||
// Config in temporären Buffer kopieren der auf 256B (Row) aufgefüllt ist.
|
||||
// __attribute__((aligned(4))) ist zwingend: nvm_write_page castet data zu
|
||||
// const uint32_t*, und unaligned 32-Bit-Zugriffe sind HardFaults auf Cortex-M0+.
|
||||
uint8_t row[256] __attribute__((aligned(4)));
|
||||
// Config (740B) in 768B-Puffer kopieren (3 Rows), Rest mit 0xFF füllen.
|
||||
// __attribute__((aligned(4))) ist zwingend: nvm_write_page castet zu uint32_t*.
|
||||
uint8_t row[768] __attribute__((aligned(4)));
|
||||
memset(row, 0xFF, sizeof(row));
|
||||
memcpy(row, &cfg, sizeof(cfg));
|
||||
|
||||
// Automatisches Schreiben deaktivieren (manueller Schreib-Modus)
|
||||
NVMCTRL->CTRLB.bit.MANW = 1;
|
||||
|
||||
// Row 0 der Config löschen und seitenweise schreiben (4 × 64B)
|
||||
// 3 Rows löschen (0x1FD00, 0x1FE00, 0x1FF00)
|
||||
nvm_erase_row(k_config_addr);
|
||||
for (uint8_t p = 0; p < 4; p++) {
|
||||
nvm_erase_row(k_config_addr + 256);
|
||||
nvm_erase_row(k_config_addr + 512);
|
||||
|
||||
// 12 Pages à 64B schreiben
|
||||
for (uint8_t p = 0; p < 12; p++) {
|
||||
nvm_write_page(k_config_addr + p * 64, row + p * 64);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,61 +2,73 @@
|
|||
#include <stdint.h>
|
||||
#include "action.h"
|
||||
|
||||
// ── NVM-Config-Layout (512 Bytes, ab 0x1FE00) ────────────────────────────────
|
||||
// ── NVM-Config-Layout (768 Bytes, ab 0x1FD00) ────────────────────────────────
|
||||
//
|
||||
// Offset Size Inhalt
|
||||
// 0 4 Magic (0x56503202 = 'VP2\x02')
|
||||
// 4 1 Version
|
||||
// 5 2 CRC16 über Bytes 7–222
|
||||
// 7 60 mx_actions[20] – 20 × 3B (SAction packed)
|
||||
// 67 36 enc_actions[4][3] – 12 × 3B
|
||||
// 103 20 led_r[20]
|
||||
// 123 20 led_g[20]
|
||||
// 143 20 led_b[20]
|
||||
// 163 20 led_anim[20] – LEDAnim-Typ pro Button (uint8_t)
|
||||
// 183 40 led_period_ms[20] – Animationsperiode in ms (uint16_t, little-endian)
|
||||
// 223 33 Padding bis 256 Bytes (erste Row voll)
|
||||
// 256 256 Reserviert für zukünftige Erweiterungen (zweite Row)
|
||||
// Row 0 (0x1FD00, 256B): Globaler Header (32B) + Profil 0 (236B) + Padding (−12B → überläuft in Row 1)
|
||||
// Row 1 (0x1FE00, 256B): Profil 0 Rest + Profil 1 (Teil)
|
||||
// Row 2 (0x1FF00, 256B): Profil 1 Rest + Profil 2 + Reserve (28B)
|
||||
//
|
||||
// Gesamt genutzt: 223 Bytes (sizeof SDeviceConfig mit packed SAction)
|
||||
// Profil-Offsets (ab Byte 0 des Config-Blobs):
|
||||
// Header: Bytes 0– 31 (32B)
|
||||
// Profil 0: Bytes 32–267 (236B)
|
||||
// Profil 1: Bytes 268–503 (236B)
|
||||
// Profil 2: Bytes 504–739 (236B)
|
||||
// Reserve: Bytes 740–767 (28B)
|
||||
//
|
||||
// Alle 3 Rows werden immer gemeinsam gelöscht und neu geschrieben.
|
||||
|
||||
#define NVM_CONFIG_MAGIC 0x56503202UL
|
||||
#define NVM_CONFIG_VERSION 2 // Version 2
|
||||
#define NVM_CONFIG_MAGIC 0x56503203UL // 'VP2\x03' – Version 3
|
||||
#define NVM_CONFIG_VERSION 3
|
||||
|
||||
// Encoder-Aktions-Indizes (in SDeviceConfig.enc_actions[])
|
||||
// Encoder-Aktions-Indizes (in SDeviceProfile.enc_actions[])
|
||||
// Reihenfolge: [enc][0]=SW, [enc][1]=CW, [enc][2]=CCW
|
||||
#define ENC_ACTION_SW 0
|
||||
#define ENC_ACTION_CW 1
|
||||
#define ENC_ACTION_CCW 2
|
||||
|
||||
// ── Pro-Profil-Daten (236 Bytes) ─────────────────────────────────────────────
|
||||
|
||||
struct __attribute__((packed)) SDeviceProfile
|
||||
{
|
||||
SAction mx_actions[20]; // 60B – MX-Buttons 0–19
|
||||
SAction enc_actions[4][3]; // 36B – [Encoder 0–3][SW/CW/CCW]
|
||||
|
||||
uint8_t led_r[20]; // 20B
|
||||
uint8_t led_g[20]; // 20B
|
||||
uint8_t led_b[20]; // 20B
|
||||
uint8_t led_brightness[20]; // 20B – per-LED Helligkeit (0–255, Default 255)
|
||||
|
||||
uint8_t led_anim[20]; // 20B – LEDAnim-Typ (0=STATIC … 5=COLOR_CYCLE)
|
||||
uint16_t led_period_ms[20]; // 40B – Animationsperiode in ms
|
||||
// Gesamt: 236B
|
||||
};
|
||||
|
||||
// ── Globale Config (740 Bytes) ────────────────────────────────────────────────
|
||||
|
||||
struct __attribute__((packed)) SDeviceConfig
|
||||
{
|
||||
uint32_t magic;
|
||||
uint8_t version;
|
||||
uint16_t crc;
|
||||
// Globaler Header (32B)
|
||||
uint32_t magic; // 4B
|
||||
uint8_t version; // 1B
|
||||
uint16_t crc; // 2B – CRC16-CCITT über Bytes 7–739
|
||||
uint8_t active_profile; // 1B – aktives Profil (0–2)
|
||||
uint8_t global_brightness; // 1B – globale LED-Helligkeit (0–255)
|
||||
uint8_t enc_sensitivity[4]; // 4B – Schrittweite pro Encoder (reserviert, Default 1)
|
||||
uint8_t _reserve[19]; // 19B – Platz für spätere globale Felder
|
||||
|
||||
// Aktionen
|
||||
SAction mx_actions[20]; // MX-Buttons 0–19 (key_id 5–24)
|
||||
SAction enc_actions[4][3]; // [Encoder 0–3][SW/CW/CCW]
|
||||
|
||||
// Base-LED Farben
|
||||
uint8_t led_r[20];
|
||||
uint8_t led_g[20];
|
||||
uint8_t led_b[20];
|
||||
|
||||
// LED-Animationen pro MX-Button
|
||||
uint8_t led_anim[20]; // LEDAnim-Typ (0=STATIC, 1=BLINK, 2=PULSE, 5=COLOR_CYCLE)
|
||||
uint16_t led_period_ms[20]; // Animationsperiode in ms (0 = Firmware-Default verwenden)
|
||||
// Profile (3 × 236B = 708B)
|
||||
SDeviceProfile profiles[3];
|
||||
// Gesamt: 32 + 708 = 740B
|
||||
};
|
||||
|
||||
// Standardwerte wenn keine gültige Config im NVM
|
||||
void nvm_config_defaults(SDeviceConfig& cfg);
|
||||
|
||||
// Config aus NVM lesen. Gibt false zurück wenn Magic/CRC ungültig → Defaults geladen.
|
||||
// Config aus NVM lesen. Gibt false zurück wenn Magic/CRC/Version ungültig → Defaults geladen.
|
||||
bool nvm_config_load(SDeviceConfig& cfg);
|
||||
|
||||
// Config in NVM schreiben (löscht 2 Rows, schreibt neu).
|
||||
// Config in NVM schreiben (löscht 3 Rows, schreibt 12 Pages).
|
||||
void nvm_config_save(const SDeviceConfig& cfg);
|
||||
|
||||
// CRC16 über die Nutzdaten der Config
|
||||
// CRC16 über die Nutzdaten der Config (Bytes 7–739, nach dem crc-Feld)
|
||||
uint16_t nvm_config_crc(const SDeviceConfig& cfg);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue