mirror of
https://github.com/ravinrabbid/DonCon2040.git
synced 2024-11-20 03:37:07 +01:00
Add persistent settings store
In preparation for on-screen menu.
This commit is contained in:
parent
b440a35630
commit
0f1fd87a07
@ -1,6 +1,9 @@
|
||||
#ifndef _PERIPHERALS_DISPLAY_H_
|
||||
#define _PERIPHERALS_DISPLAY_H_
|
||||
|
||||
#include "usb/usb_driver.h"
|
||||
#include "utils/InputState.h"
|
||||
|
||||
#include <ssd1306/ssd1306.h>
|
||||
|
||||
#include "hardware/i2c.h"
|
||||
@ -26,6 +29,10 @@ class Display {
|
||||
Config m_config;
|
||||
State m_state;
|
||||
|
||||
Utils::InputState m_input_state;
|
||||
usb_mode_t m_usb_mode;
|
||||
uint8_t m_player_id;
|
||||
|
||||
ssd1306_t m_display;
|
||||
|
||||
void drawIdleScreen();
|
||||
@ -34,6 +41,10 @@ class Display {
|
||||
public:
|
||||
Display(const Config &config);
|
||||
|
||||
void setInputState(const Utils::InputState &state);
|
||||
void setUsbMode(usb_mode_t mode);
|
||||
void setPlayerId(uint8_t player_id);
|
||||
|
||||
void showIdle();
|
||||
void showMenu();
|
||||
|
||||
|
@ -11,6 +11,13 @@ namespace Doncon::Peripherals {
|
||||
class Drum {
|
||||
public:
|
||||
struct Config {
|
||||
struct Thresholds {
|
||||
uint16_t don_left;
|
||||
uint16_t ka_left;
|
||||
uint16_t don_right;
|
||||
uint16_t ka_right;
|
||||
};
|
||||
|
||||
struct {
|
||||
uint8_t don_left;
|
||||
uint8_t ka_left;
|
||||
@ -18,13 +25,7 @@ class Drum {
|
||||
uint8_t ka_right;
|
||||
} pins;
|
||||
|
||||
struct {
|
||||
uint16_t don_left;
|
||||
uint16_t ka_left;
|
||||
uint16_t don_right;
|
||||
uint16_t ka_right;
|
||||
} trigger_thresholds;
|
||||
|
||||
Thresholds trigger_thresholds;
|
||||
uint8_t sample_count;
|
||||
uint16_t debounce_delay_ms;
|
||||
};
|
||||
|
58
include/utils/SettingsStore.h
Normal file
58
include/utils/SettingsStore.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef _UTILS_SETTINGSSTORE_H_
|
||||
#define _UTILS_SETTINGSSTORE_H_
|
||||
|
||||
#include "peripherals/Drum.h"
|
||||
#include "usb/usb_driver.h"
|
||||
|
||||
#include "hardware/flash.h"
|
||||
|
||||
namespace Doncon::Utils {
|
||||
|
||||
class SettingsStore {
|
||||
private:
|
||||
const static uint32_t m_flash_size = FLASH_SECTOR_SIZE;
|
||||
const static uint32_t m_flash_offset = PICO_FLASH_SIZE_BYTES - m_flash_size;
|
||||
const static uint32_t m_store_size = FLASH_PAGE_SIZE;
|
||||
const static uint32_t m_store_pages = m_flash_size / m_store_size;
|
||||
const static uint8_t m_magic_byte = 0x39;
|
||||
|
||||
struct __attribute((packed, aligned(1))) Storecache {
|
||||
uint8_t in_use;
|
||||
usb_mode_t usb_mode;
|
||||
Peripherals::Drum::Config::Thresholds trigger_thresholds;
|
||||
|
||||
uint8_t _padding[m_store_size - sizeof(uint8_t) - sizeof(usb_mode_t) -
|
||||
sizeof(Peripherals::Drum::Config::Thresholds)];
|
||||
};
|
||||
static_assert(sizeof(Storecache) == m_store_size);
|
||||
|
||||
enum class RebootType {
|
||||
None,
|
||||
Normal,
|
||||
Bootsel,
|
||||
};
|
||||
|
||||
Storecache m_store_cache;
|
||||
bool m_dirty;
|
||||
|
||||
RebootType m_scheduled_reboot;
|
||||
|
||||
private:
|
||||
Storecache read();
|
||||
|
||||
public:
|
||||
SettingsStore();
|
||||
|
||||
void setUsbMode(usb_mode_t mode);
|
||||
usb_mode_t getUsbMode();
|
||||
|
||||
void setTriggerThresholds(Peripherals::Drum::Config::Thresholds thresholds);
|
||||
Peripherals::Drum::Config::Thresholds getTriggerThresholds();
|
||||
|
||||
void scheduleReboot(bool bootsel = false);
|
||||
|
||||
void store();
|
||||
};
|
||||
} // namespace Doncon::Utils
|
||||
|
||||
#endif // _UTILS_SETTINGSSTORE_H_
|
55
src/main.cpp
55
src/main.cpp
@ -3,6 +3,7 @@
|
||||
#include "peripherals/Drum.h"
|
||||
#include "peripherals/StatusLed.h"
|
||||
#include "usb/usb_driver.h"
|
||||
#include "utils/SettingsStore.h"
|
||||
|
||||
#include "GlobalConfiguration.h"
|
||||
|
||||
@ -14,9 +15,23 @@
|
||||
|
||||
using namespace Doncon;
|
||||
|
||||
queue_t control_queue;
|
||||
queue_t drum_input_queue;
|
||||
queue_t controller_input_queue;
|
||||
|
||||
enum class ControlCommand {
|
||||
SetUsbMode,
|
||||
SetPlayerLed,
|
||||
};
|
||||
|
||||
struct ControlMessage {
|
||||
ControlCommand command;
|
||||
union {
|
||||
usb_mode_t usb_mode;
|
||||
usb_player_led_t player_led;
|
||||
} data;
|
||||
};
|
||||
|
||||
void core1_task() {
|
||||
multicore_lockout_victim_init();
|
||||
|
||||
@ -26,12 +41,28 @@ void core1_task() {
|
||||
gpio_pull_up(Config::Default::i2c_config.scl_pin);
|
||||
i2c_init(Config::Default::i2c_config.block, Config::Default::i2c_config.speed_hz);
|
||||
|
||||
Utils::InputState input_state;
|
||||
Peripherals::Buttons buttons(Config::Default::button_config);
|
||||
Peripherals::StatusLed led(Config::Default::led_config);
|
||||
Peripherals::Display display(Config::Default::display_config);
|
||||
|
||||
Utils::InputState input_state;
|
||||
ControlMessage control_msg;
|
||||
|
||||
while (true) {
|
||||
if (queue_try_remove(&control_queue, &control_msg)) {
|
||||
switch (control_msg.command) {
|
||||
case ControlCommand::SetUsbMode:
|
||||
display.setUsbMode(control_msg.data.usb_mode);
|
||||
break;
|
||||
case ControlCommand::SetPlayerLed:
|
||||
if (control_msg.data.player_led.type == USB_PLAYER_LED_ID) {
|
||||
display.setPlayerId(control_msg.data.player_led.id);
|
||||
} else if (control_msg.data.player_led.type == USB_PLAYER_LED_COLOR) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
buttons.updateInputState(input_state);
|
||||
|
||||
queue_try_add(&controller_input_queue, &input_state.controller);
|
||||
@ -48,18 +79,34 @@ void core1_task() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
queue_init(&control_queue, sizeof(ControlMessage), 1);
|
||||
queue_init(&drum_input_queue, sizeof(Utils::InputState::Drum), 1);
|
||||
queue_init(&controller_input_queue, sizeof(Utils::InputState::Controller), 1);
|
||||
multicore_launch_core1(core1_task);
|
||||
|
||||
Utils::InputState input_state;
|
||||
Peripherals::Drum drum(Config::Default::drum_config);
|
||||
usb_mode_t mode = Config::Default::usb_mode;
|
||||
|
||||
auto settings_store = std::make_shared<Utils::SettingsStore>();
|
||||
|
||||
Peripherals::Drum drum(Config::Default::drum_config);
|
||||
|
||||
auto mode = settings_store->getUsbMode();
|
||||
usb_driver_init(mode);
|
||||
usb_driver_set_player_led_cb([](usb_player_led_t player_led) {
|
||||
auto ctrl_message = ControlMessage{ControlCommand::SetPlayerLed, {.player_led = player_led}};
|
||||
queue_add_blocking(&control_queue, &ctrl_message);
|
||||
});
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
multicore_launch_core1(core1_task);
|
||||
auto readSettings = [&]() {
|
||||
ControlMessage ctrl_message;
|
||||
|
||||
ctrl_message = {ControlCommand::SetUsbMode, {.usb_mode = mode}};
|
||||
queue_add_blocking(&control_queue, &ctrl_message);
|
||||
};
|
||||
|
||||
readSettings();
|
||||
|
||||
while (true) {
|
||||
drum.updateInputState(input_state);
|
||||
|
@ -165,12 +165,17 @@ static const std::array<uint8_t, 546> ka_r_bmp = {
|
||||
0x00, 0xff, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xc0, 0x1f, 0xff, 0xf0, 0x00, 0xff, 0xff,
|
||||
0xff, 0xc1, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00};
|
||||
|
||||
Display::Display(const Config &config) : m_config(config), m_state(State::Idle) {
|
||||
Display::Display(const Config &config)
|
||||
: m_config(config), m_state(State::Idle), m_input_state({}), m_usb_mode(USB_MODE_DEBUG), m_player_id(0) {
|
||||
m_display.external_vcc = false;
|
||||
ssd1306_init(&m_display, 128, 64, m_config.i2c_address, m_config.i2c_block);
|
||||
ssd1306_clear(&m_display);
|
||||
}
|
||||
|
||||
void Display::setInputState(const Utils::InputState &state) { m_input_state = state; }
|
||||
void Display::setUsbMode(usb_mode_t mode) { m_usb_mode = mode; };
|
||||
void Display::setPlayerId(uint8_t player_id) { m_player_id = player_id; };
|
||||
|
||||
void Display::showIdle() { m_state = State::Idle; }
|
||||
void Display::showMenu() { m_state = State::Menu; }
|
||||
|
||||
|
105
src/utils/SettingsStore.cpp
Normal file
105
src/utils/SettingsStore.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include "utils/SettingsStore.h"
|
||||
|
||||
#include "GlobalConfiguration.h"
|
||||
|
||||
#include "hardware/watchdog.h"
|
||||
#include "pico/bootrom.h"
|
||||
#include "pico/multicore.h"
|
||||
|
||||
namespace Doncon::Utils {
|
||||
|
||||
static uint8_t read_byte(uint32_t offset) { return *(reinterpret_cast<uint8_t *>(XIP_BASE + offset)); }
|
||||
|
||||
SettingsStore::SettingsStore()
|
||||
: m_store_cache({m_magic_byte, Config::Default::usb_mode, Config::Default::drum_config.trigger_thresholds, {}}),
|
||||
m_dirty(true), m_scheduled_reboot(RebootType::None) {
|
||||
uint32_t current_page = m_flash_offset + m_flash_size - m_store_size;
|
||||
bool found_valid = false;
|
||||
for (uint8_t i = 0; i < m_store_pages; ++i) {
|
||||
if (read_byte(current_page) == m_magic_byte) {
|
||||
found_valid = true;
|
||||
break;
|
||||
} else {
|
||||
current_page -= m_store_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_valid) {
|
||||
m_store_cache = *(reinterpret_cast<Storecache *>(XIP_BASE + current_page));
|
||||
m_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsStore::setUsbMode(usb_mode_t mode) {
|
||||
if (mode != m_store_cache.usb_mode) {
|
||||
m_store_cache.usb_mode = mode;
|
||||
m_dirty = true;
|
||||
|
||||
scheduleReboot();
|
||||
}
|
||||
}
|
||||
|
||||
usb_mode_t SettingsStore::getUsbMode() { return m_store_cache.usb_mode; }
|
||||
|
||||
void SettingsStore::setTriggerThresholds(Peripherals::Drum::Config::Thresholds thresholds) {
|
||||
if (m_store_cache.trigger_thresholds.don_left != thresholds.don_left ||
|
||||
m_store_cache.trigger_thresholds.don_right != thresholds.don_right ||
|
||||
m_store_cache.trigger_thresholds.ka_left != thresholds.ka_left ||
|
||||
m_store_cache.trigger_thresholds.ka_right != thresholds.ka_right) {
|
||||
|
||||
m_store_cache.trigger_thresholds = thresholds;
|
||||
m_dirty = true;
|
||||
}
|
||||
}
|
||||
Peripherals::Drum::Config::Thresholds SettingsStore::getTriggerThresholds() { return m_store_cache.trigger_thresholds; }
|
||||
|
||||
void SettingsStore::store() {
|
||||
if (m_dirty) {
|
||||
multicore_lockout_start_blocking();
|
||||
uint32_t interrupts = save_and_disable_interrupts();
|
||||
|
||||
uint32_t current_page = m_flash_offset;
|
||||
bool do_erase = true;
|
||||
for (uint8_t i = 0; i < m_store_pages; ++i) {
|
||||
if (read_byte(current_page) == 0xFF) {
|
||||
do_erase = false;
|
||||
break;
|
||||
} else {
|
||||
current_page += m_store_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_erase) {
|
||||
flash_range_erase(m_flash_offset, m_flash_size);
|
||||
current_page = m_flash_offset;
|
||||
}
|
||||
|
||||
flash_range_program(current_page, reinterpret_cast<uint8_t *>(&m_store_cache), sizeof(m_store_cache));
|
||||
|
||||
m_dirty = false;
|
||||
|
||||
restore_interrupts(interrupts);
|
||||
multicore_lockout_end_blocking();
|
||||
}
|
||||
|
||||
switch (m_scheduled_reboot) {
|
||||
case RebootType::Normal:
|
||||
watchdog_enable(1, 1);
|
||||
while (1)
|
||||
;
|
||||
break;
|
||||
case RebootType::Bootsel:
|
||||
reset_usb_boot(0, 0);
|
||||
break;
|
||||
case RebootType::None:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsStore::scheduleReboot(bool bootsel) {
|
||||
if (m_scheduled_reboot != RebootType::Bootsel) {
|
||||
m_scheduled_reboot = (bootsel ? RebootType::Bootsel : RebootType::Normal);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Doncon::Utils
|
Loading…
Reference in New Issue
Block a user