8 Step macro and profile switching fully working
This commit is contained in:
+16
-8
@@ -255,13 +255,16 @@ void CMainController::poll_vendor()
|
||||
cfg.version == NVM_CONFIG_VERSION &&
|
||||
cfg.crc == nvm_config_crc(cfg))
|
||||
{
|
||||
nvm_config_save(cfg);
|
||||
init_buttons();
|
||||
usb_serial_send(USB_EVT_CONFIG_ACK, 0); // Erfolg melden
|
||||
if (nvm_config_save(cfg)) {
|
||||
init_buttons();
|
||||
usb_serial_send(USB_EVT_CONFIG_ACK, 0); // Erfolg melden
|
||||
} else {
|
||||
usb_serial_send(USB_EVT_CONFIG_NACK, 0); // NVM-Timeout
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usb_serial_send(USB_EVT_CONFIG_NACK, 0); // Fehler melden
|
||||
usb_serial_send(USB_EVT_CONFIG_NACK, 0); // CRC/Magic-Fehler
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -288,8 +291,11 @@ void CMainController::poll_vendor()
|
||||
if (m_macro_receiving) {
|
||||
m_macro_receiving = false;
|
||||
memcpy(&m_macros, m_macro_buf, sizeof(m_macros));
|
||||
macro_config_save(m_macros);
|
||||
usb_serial_send(USB_EVT_MACRO_ACK, 0);
|
||||
if (macro_config_save(m_macros)) {
|
||||
usb_serial_send(USB_EVT_MACRO_ACK, 0);
|
||||
} else {
|
||||
usb_serial_send(USB_EVT_MACRO_NACK, 0); // NVM-Timeout
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -439,8 +445,10 @@ void CMainController::execute_action_down(SAction action, uint8_t key_id)
|
||||
target = (cfg.active_profile + 1) % 3; // Zyklus: 0→1→2→0
|
||||
if (target > 2) break;
|
||||
cfg.active_profile = target;
|
||||
nvm_config_save(cfg);
|
||||
init_buttons();
|
||||
cfg.crc = nvm_config_crc(cfg); // CRC nach Änderung aktualisieren
|
||||
if (nvm_config_save(cfg))
|
||||
init_buttons();
|
||||
// Bei NVM-Timeout: kein Profil-Wechsel (Config unverändert in NVM)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+23
-13
@@ -9,28 +9,37 @@
|
||||
|
||||
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)
|
||||
static bool nvm_wait()
|
||||
{
|
||||
// ~400ms Timeout bei 48MHz, konservativ 4 Zyklen pro Loop-Iteration
|
||||
uint32_t timeout = 48000000UL / 4 * 400 / 1000; // ≈ 4 800 000
|
||||
while (!NVMCTRL->INTFLAG.bit.READY) {
|
||||
if (--timeout == 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool nvm_exec(uint16_t cmd)
|
||||
{
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | cmd;
|
||||
nvm_wait();
|
||||
return nvm_wait();
|
||||
}
|
||||
|
||||
static void nvm_erase_row(uint32_t addr)
|
||||
static bool nvm_erase_row(uint32_t addr)
|
||||
{
|
||||
nvm_wait();
|
||||
if (!nvm_wait()) return false;
|
||||
NVMCTRL->ADDR.reg = addr / 2;
|
||||
nvm_exec(NVMCTRL_CTRLA_CMD_ER);
|
||||
return nvm_exec(NVMCTRL_CTRLA_CMD_ER);
|
||||
}
|
||||
|
||||
static void nvm_write_page(uint32_t addr, const uint8_t* data)
|
||||
static bool nvm_write_page(uint32_t addr, const uint8_t* data)
|
||||
{
|
||||
nvm_exec(NVMCTRL_CTRLA_CMD_PBC);
|
||||
if (!nvm_exec(NVMCTRL_CTRLA_CMD_PBC)) return false;
|
||||
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];
|
||||
NVMCTRL->ADDR.reg = addr / 2;
|
||||
nvm_exec(NVMCTRL_CTRLA_CMD_WP);
|
||||
return nvm_exec(NVMCTRL_CTRLA_CMD_WP);
|
||||
}
|
||||
|
||||
bool macro_config_load(SMacroTable& tbl)
|
||||
@@ -50,7 +59,7 @@ bool macro_config_load(SMacroTable& tbl)
|
||||
return true;
|
||||
}
|
||||
|
||||
void macro_config_save(const SMacroTable& tbl)
|
||||
bool 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 →
|
||||
@@ -61,11 +70,12 @@ void macro_config_save(const SMacroTable& tbl)
|
||||
NVMCTRL->CTRLB.bit.MANW = 1;
|
||||
|
||||
// Beide Rows löschen (Row 0: 0x1FB00, Row 1: 0x1FC00)
|
||||
nvm_erase_row(k_macro_addr);
|
||||
nvm_erase_row(k_macro_addr + 256);
|
||||
if (!nvm_erase_row(k_macro_addr)) return false;
|
||||
if (!nvm_erase_row(k_macro_addr + 256)) return false;
|
||||
|
||||
// 8 Pages à 64B schreiben
|
||||
for (uint8_t p = 0; p < 8; p++) {
|
||||
nvm_write_page(k_macro_addr + p * 64, aligned_buf + p * 64);
|
||||
if (!nvm_write_page(k_macro_addr + p * 64, aligned_buf + p * 64)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -33,4 +33,5 @@ struct __attribute__((packed)) SMacroTable
|
||||
bool macro_config_load(SMacroTable& tbl);
|
||||
|
||||
// Makro-Tabelle in NVM schreiben (löscht Row 0+1, schreibt 8 Pages).
|
||||
void macro_config_save(const SMacroTable& tbl);
|
||||
// Gibt false zurück wenn eine NVM-Operation nicht rechtzeitig fertig wird.
|
||||
bool macro_config_save(const SMacroTable& tbl);
|
||||
|
||||
+25
-15
@@ -8,35 +8,44 @@
|
||||
static const uint32_t k_config_addr = 0x1FD00UL; // Row 0–2 der Config
|
||||
|
||||
// ── NVMCTRL-Hilfsfunktionen ───────────────────────────────────────────────────
|
||||
//
|
||||
// nvm_wait() hat einen Timeout (~400ms bei 48MHz) damit das Board nicht
|
||||
// einfriert wenn der NVMCTRL aus unbekanntem Grund nicht READY meldet.
|
||||
// (Beobachtet nach bestimmten Bootloader-Firmware-Flash-Zyklen auf SAMD21.)
|
||||
|
||||
static void nvm_wait()
|
||||
static bool nvm_wait()
|
||||
{
|
||||
while (!NVMCTRL->INTFLAG.bit.READY) {}
|
||||
// ~400ms Timeout bei 48MHz, konservativ 4 Zyklen pro Loop-Iteration
|
||||
uint32_t timeout = 48000000UL / 4 * 400 / 1000; // ≈ 4 800 000
|
||||
while (!NVMCTRL->INTFLAG.bit.READY) {
|
||||
if (--timeout == 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nvm_exec(uint16_t cmd)
|
||||
static bool nvm_exec(uint16_t cmd)
|
||||
{
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | cmd;
|
||||
nvm_wait();
|
||||
return nvm_wait();
|
||||
}
|
||||
|
||||
static void nvm_erase_row(uint32_t addr)
|
||||
static bool nvm_erase_row(uint32_t addr)
|
||||
{
|
||||
nvm_wait();
|
||||
if (!nvm_wait()) return false;
|
||||
NVMCTRL->ADDR.reg = addr / 2; // NVMCTRL erwartet Wort-Adresse (16-Bit-Worte)
|
||||
nvm_exec(NVMCTRL_CTRLA_CMD_ER);
|
||||
return nvm_exec(NVMCTRL_CTRLA_CMD_ER);
|
||||
}
|
||||
|
||||
static void nvm_write_page(uint32_t addr, const uint8_t* data)
|
||||
static bool nvm_write_page(uint32_t addr, const uint8_t* data)
|
||||
{
|
||||
nvm_exec(NVMCTRL_CTRLA_CMD_PBC);
|
||||
if (!nvm_exec(NVMCTRL_CTRLA_CMD_PBC)) return false;
|
||||
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];
|
||||
}
|
||||
NVMCTRL->ADDR.reg = addr / 2;
|
||||
nvm_exec(NVMCTRL_CTRLA_CMD_WP);
|
||||
return nvm_exec(NVMCTRL_CTRLA_CMD_WP);
|
||||
}
|
||||
|
||||
// ── CRC16-CCITT (Poly 0x1021) ─────────────────────────────────────────────────
|
||||
@@ -115,7 +124,7 @@ bool nvm_config_load(SDeviceConfig& cfg)
|
||||
|
||||
// ── Speichern ─────────────────────────────────────────────────────────────────
|
||||
|
||||
void nvm_config_save(const SDeviceConfig& cfg)
|
||||
bool nvm_config_save(const SDeviceConfig& cfg)
|
||||
{
|
||||
// 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*.
|
||||
@@ -126,12 +135,13 @@ void nvm_config_save(const SDeviceConfig& cfg)
|
||||
NVMCTRL->CTRLB.bit.MANW = 1;
|
||||
|
||||
// 3 Rows löschen (0x1FD00, 0x1FE00, 0x1FF00)
|
||||
nvm_erase_row(k_config_addr);
|
||||
nvm_erase_row(k_config_addr + 256);
|
||||
nvm_erase_row(k_config_addr + 512);
|
||||
if (!nvm_erase_row(k_config_addr)) return false;
|
||||
if (!nvm_erase_row(k_config_addr + 256)) return false;
|
||||
if (!nvm_erase_row(k_config_addr + 512)) return false;
|
||||
|
||||
// 12 Pages à 64B schreiben
|
||||
for (uint8_t p = 0; p < 12; p++) {
|
||||
nvm_write_page(k_config_addr + p * 64, row + p * 64);
|
||||
if (!nvm_write_page(k_config_addr + p * 64, row + p * 64)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,8 @@ void nvm_config_defaults(SDeviceConfig& cfg);
|
||||
bool nvm_config_load(SDeviceConfig& cfg);
|
||||
|
||||
// Config in NVM schreiben (löscht 3 Rows, schreibt 12 Pages).
|
||||
void nvm_config_save(const SDeviceConfig& cfg);
|
||||
// Gibt false zurück wenn eine NVM-Operation nicht rechtzeitig fertig wird (Board hängt nicht).
|
||||
bool nvm_config_save(const SDeviceConfig& cfg);
|
||||
|
||||
// CRC16 über die Nutzdaten der Config (Bytes 7–739, nach dem crc-Feld)
|
||||
uint16_t nvm_config_crc(const SDeviceConfig& cfg);
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#define USB_EVT_CONFIG_DATA 0x93 // Config-Chunk: Data[1] = Index, Data[2..7] = 6B
|
||||
#define USB_EVT_CONFIG_END 0x94 // Config-Dump abgeschlossen
|
||||
#define USB_EVT_MACRO_ACK 0x95 // Makro-Tabelle erfolgreich gespeichert
|
||||
#define USB_EVT_MACRO_NACK 0x99 // Makro-Tabelle: NVM-Fehler – nicht geschrieben
|
||||
#define USB_EVT_MACRO_BEGIN 0x96 // Beginn Makro-Dump: Data[1] = Chunk-Anzahl
|
||||
#define USB_EVT_MACRO_DATA 0x97 // Makro-Chunk: Data[1] = Index, Data[2..7] = 6B
|
||||
#define USB_EVT_MACRO_END 0x98 // Makro-Dump abgeschlossen
|
||||
|
||||
Reference in New Issue
Block a user