Browse Source

add configuration storage in flash

Thomas Buck 8 months ago
parent
commit
a59876d82d
15 changed files with 676 additions and 80 deletions
  1. 6
    3
      CMakeLists.txt
  2. 2
    1
      docs/src/usage.md
  3. 2
    0
      include/config.h
  4. 3
    1
      include/encoder.h
  5. 56
    0
      include/mem.h
  6. 0
    2
      include/sequence.h
  7. 25
    0
      include/settings.h
  8. 9
    0
      include/ui.h
  9. 9
    2
      src/encoder.c
  10. 64
    44
      src/main.c
  11. 131
    0
      src/mem.c
  12. 239
    0
      src/memmap_custom.ld
  13. 13
    14
      src/sequence.c
  14. 114
    0
      src/settings.c
  15. 3
    13
      src/ui.c

+ 6
- 3
CMakeLists.txt View File

47
     src/usb_cdc.c
47
     src/usb_cdc.c
48
     src/usb_descriptors.c
48
     src/usb_descriptors.c
49
     src/usb_midi.c
49
     src/usb_midi.c
50
+    src/settings.c
51
+    src/mem.c
50
 
52
 
51
     pico-ssd1306/ssd1306.c
53
     pico-ssd1306/ssd1306.c
52
 )
54
 )
95
     tinyusb_device
97
     tinyusb_device
96
     tinyusb_board
98
     tinyusb_board
97
     cmake_git_version_tracking
99
     cmake_git_version_tracking
100
+    hardware_flash
101
+    pico_flash
102
+    hardware_exception
98
 )
103
 )
99
 
104
 
100
-# enable usb output, disable uart output
101
-pico_enable_stdio_usb(drumkit 1)
102
-pico_enable_stdio_uart(drumkit 0)
105
+pico_set_linker_script(drumkit ${CMAKE_CURRENT_LIST_DIR}/src/memmap_custom.ld)
103
 
106
 
104
 # fix for Errata RP2040-E5 (the fix requires use of GPIO 15)
107
 # fix for Errata RP2040-E5 (the fix requires use of GPIO 15)
105
 target_compile_definitions(drumkit PUBLIC PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1)
108
 target_compile_definitions(drumkit PUBLIC PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1)

+ 2
- 1
docs/src/usage.md View File

13
 
13
 
14
 You can hold down some buttons while powering on the device to get different effects:
14
 You can hold down some buttons while powering on the device to get different effects:
15
 
15
 
16
-* hold the `REC` button to skip the boot animation
16
+* hold the `REC` button to enter the configuration menu
17
 * hold the encoder `Click` button to show version information of the firmware
17
 * hold the encoder `Click` button to show version information of the firmware
18
 * hold both `REC` and `Click` to enter the button test mode
18
 * hold both `REC` and `Click` to enter the button test mode
19
+* hold any combination of three buttons to skip the boot animation

+ 2
- 0
include/config.h View File

24
 
24
 
25
 #define WATCHDOG_PERIOD_MS 100
25
 #define WATCHDOG_PERIOD_MS 100
26
 #define LOGO_INIT_MS 1000
26
 #define LOGO_INIT_MS 1000
27
+#define FLASH_LOCK_TIMEOUT_MS 500
28
+#define CH_GPIO_DEFAULT_MS 42
27
 
29
 
28
 // ASCII 0x18 = CAN (cancel)
30
 // ASCII 0x18 = CAN (cancel)
29
 #define ENTER_BOOTLOADER_MAGIC 0x18
31
 #define ENTER_BOOTLOADER_MAGIC 0x18

+ 3
- 1
include/encoder.h View File

22
 #include <stdint.h>
22
 #include <stdint.h>
23
 
23
 
24
 void encoder_init(void);
24
 void encoder_init(void);
25
-int32_t encoder_pos(void);
26
 void encoder_run(void);
25
 void encoder_run(void);
27
 
26
 
27
+int32_t encoder_pos(void);
28
+int32_t encoder_get_diff(void);
29
+
28
 #endif // __ENCODER_H__
30
 #endif // __ENCODER_H__

+ 56
- 0
include/mem.h View File

1
+/*
2
+ * mem.h
3
+ *
4
+ * Copyright (c) 2023 - 2024 Thomas Buck (thomas@xythobuz.de)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __MEM_H__
20
+#define __MEM_H__
21
+
22
+#include "hardware/flash.h"
23
+
24
+#include <stdint.h>
25
+#include <stdbool.h>
26
+
27
+#include "sequence.h"
28
+
29
+/*
30
+ * We use the last flash page for our persistent storage.
31
+ * This is kept clear by our custom linker script.
32
+ */
33
+#define EEPROM_FLASH_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE)
34
+
35
+// to migrate settings when struct changes between releases
36
+#define MEM_VERSION 0
37
+
38
+struct mem_data {
39
+    uint32_t boot_anim_ms;
40
+
41
+    uint32_t ch_timings[NUM_CHANNELS];
42
+    uint32_t ch_count; // to invalidate if number of channels changes
43
+};
44
+
45
+// .ch_timings is assigned in mem_load_defaults
46
+#define MEM_DATA_INIT {           \
47
+    .boot_anim_ms = LOGO_INIT_MS, \
48
+    .ch_count = NUM_CHANNELS,     \
49
+}
50
+
51
+void mem_load(void);
52
+void mem_write(void);
53
+struct mem_data *mem_data(void);
54
+void mem_load_defaults(void);
55
+
56
+#endif // __MEM_H__

+ 0
- 2
include/sequence.h View File

34
     NUM_CHANNELS = 3
34
     NUM_CHANNELS = 3
35
 };
35
 };
36
 
36
 
37
-#define CH_GPIO_TIMINGS { 42, 42, 42 } // in milliseconds
38
-
39
 #define MAX_BEATS 128
37
 #define MAX_BEATS 128
40
 #define MAX_BANKS (MAX_BEATS / NUM_BTNS)
38
 #define MAX_BANKS (MAX_BEATS / NUM_BTNS)
41
 
39
 

+ 25
- 0
include/settings.h View File

1
+/*
2
+ * settings.h
3
+ *
4
+ * Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __SETTINGS_H__
20
+#define __SETTINGS_H__
21
+
22
+void settings_init(void);
23
+void settings_run(void);
24
+
25
+#endif // __SETTINGS_H__

+ 9
- 0
include/ui.h View File

21
 
21
 
22
 #include <stdint.h>
22
 #include <stdint.h>
23
 
23
 
24
+#define KEEP_IN_RANGE(val, min, len) { \
25
+    while (val > (len - min)) {        \
26
+        val -= len;                    \
27
+    }                                  \
28
+    while (val < min) {                \
29
+        val += len;                    \
30
+    }                                  \
31
+}
32
+
24
 enum machine_modes {
33
 enum machine_modes {
25
     MODE_LOOPSTATION = 0,
34
     MODE_LOOPSTATION = 0,
26
     MODE_DRUMMACHINE,
35
     MODE_DRUMMACHINE,

+ 9
- 2
src/encoder.c View File

69
     positionExtPrev = 0;
69
     positionExtPrev = 0;
70
 }
70
 }
71
 
71
 
72
-int32_t encoder_pos(void)
73
-{
72
+int32_t encoder_pos(void) {
74
     return positionExt;
73
     return positionExt;
75
 }
74
 }
76
 
75
 
76
+int32_t encoder_get_diff(void) {
77
+    static int32_t last_epos = 0;
78
+    int32_t epos = encoder_pos();
79
+    int32_t diff = epos - last_epos;
80
+    last_epos = epos;
81
+    return diff;
82
+}
83
+
77
 void encoder_run(void) {
84
 void encoder_run(void) {
78
     int8_t thisState = 0;
85
     int8_t thisState = 0;
79
     if (hw_type == HW_PROTOTYPE) {
86
     if (hw_type == HW_PROTOTYPE) {

+ 64
- 44
src/main.c View File

29
 #include "led.h"
29
 #include "led.h"
30
 #include "log.h"
30
 #include "log.h"
31
 #include "logo.h"
31
 #include "logo.h"
32
+#include "mem.h"
32
 #include "pulse.h"
33
 #include "pulse.h"
33
 #include "sequence.h"
34
 #include "sequence.h"
35
+#include "settings.h"
34
 #include "ui.h"
36
 #include "ui.h"
35
 #include "usb.h"
37
 #include "usb.h"
36
 #include "main.h"
38
 #include "main.h"
45
     debug_buttons[btn] = v;
47
     debug_buttons[btn] = v;
46
 }
48
 }
47
 
49
 
50
+static uint32_t debug_count_buttons(void) {
51
+    uint32_t cnt = 0;
52
+    for (uint32_t i = 0; i < NUM_BTNS; i++) {
53
+        if (debug_buttons[i]) {
54
+            cnt++;
55
+        }
56
+    }
57
+    return cnt;
58
+}
59
+
48
 void reset_to_bootloader(void) {
60
 void reset_to_bootloader(void) {
49
     lcd_draw_bye();
61
     lcd_draw_bye();
50
 
62
 
60
     while (1);
72
     while (1);
61
 }
73
 }
62
 
74
 
63
-static void encoder_handle(void) {
64
-    static int32_t last_epos = 0;
65
-    int32_t epos = encoder_pos();
66
-    if (epos != last_epos) {
67
-        ui_encoder(epos - last_epos);
68
-        last_epos = epos;
69
-    }
70
-}
71
-
72
 static void sleep_ms_wd(uint32_t ms) {
75
 static void sleep_ms_wd(uint32_t ms) {
73
     for (uint32_t i = 0; i < ms; i++) {
76
     for (uint32_t i = 0; i < ms; i++) {
74
         watchdog_update();
77
         watchdog_update();
76
     }
79
     }
77
 }
80
 }
78
 
81
 
79
-void main_loop_hw(void) {
80
-    watchdog_update();
81
-
82
-    usb_run();
83
-    cnsl_run();
84
-    buttons_run();
85
-    encoder_run();
86
-    sequence_run();
87
-    pulse_run();
88
-    ui_run();
89
-
90
-    encoder_handle();
91
-}
92
-
93
-int main(void) {
94
-    watchdog_enable(WATCHDOG_PERIOD_MS, 1);
95
-
96
-    cnsl_init();
97
-    usb_init();
98
-
82
+static void hw_type_detection(void) {
99
     gpio_init(gpio_hw_detect);
83
     gpio_init(gpio_hw_detect);
100
     gpio_set_dir(gpio_hw_detect, GPIO_IN);
84
     gpio_set_dir(gpio_hw_detect, GPIO_IN);
101
     gpio_pull_up(gpio_hw_detect);
85
     gpio_pull_up(gpio_hw_detect);
86
+
102
     if (gpio_get(gpio_hw_detect)) {
87
     if (gpio_get(gpio_hw_detect)) {
103
         hw_type = HW_PROTOTYPE;
88
         hw_type = HW_PROTOTYPE;
104
     } else {
89
     } else {
105
         hw_type = HW_V2;
90
         hw_type = HW_V2;
106
     }
91
     }
92
+}
107
 
93
 
108
-    bat_init();
109
-    buttons_init();
110
-    encoder_init();
111
-    lcd_init();
112
-    led_init();
113
-
114
-    // show logo
115
-    lcd_draw_bitmap(logo_data,
116
-                    LOGO_WIDTH, LOGO_HEIGHT,
117
-                    0, 0);
118
-
94
+static void animate_boot_combos(void) {
119
     // read out button state for debug options
95
     // read out button state for debug options
120
     buttons_callback(debug_buttons_callback);
96
     buttons_callback(debug_buttons_callback);
121
     for (uint i = 0; i < (DEBOUNCE_DELAY_MS + 5); i++) {
97
     for (uint i = 0; i < (DEBOUNCE_DELAY_MS + 5); i++) {
126
         sleep_ms_wd(1);
102
         sleep_ms_wd(1);
127
     }
103
     }
128
 
104
 
105
+    uint32_t cnt = debug_count_buttons();
106
+
129
     // handle special button combos on boot
107
     // handle special button combos on boot
130
-    if (debug_buttons[BTN_REC] && debug_buttons[BTN_CLICK]) {
108
+    if ((cnt == 2) && debug_buttons[BTN_REC] && debug_buttons[BTN_CLICK]) {
131
         lcd_debug_buttons();
109
         lcd_debug_buttons();
132
-    } else if (debug_buttons[BTN_REC] && (!debug_buttons[BTN_CLICK])) {
110
+    } else if ((cnt == 1) && debug_buttons[BTN_REC]) {
111
+        // enter settings menu
112
+        settings_init();
113
+        settings_run();
114
+        sleep_ms_wd(mem_data()->boot_anim_ms);
115
+    } else if (cnt == 3) {
133
         // skip splash screen
116
         // skip splash screen
134
-    } else if ((!debug_buttons[BTN_REC]) && debug_buttons[BTN_CLICK]) {
117
+    } else if ((cnt == 1) && debug_buttons[BTN_CLICK]) {
135
         // show version info
118
         // show version info
136
         lcd_draw_version();
119
         lcd_draw_version();
137
 
120
 
157
             usb_run();
140
             usb_run();
158
             cnsl_run();
141
             cnsl_run();
159
             led_set(i, true);
142
             led_set(i, true);
160
-            sleep_ms_wd(LOGO_INIT_MS / LED_COUNT);
143
+            sleep_ms_wd(mem_data()->boot_anim_ms / LED_COUNT);
161
         }
144
         }
162
     }
145
     }
163
 
146
 
165
     for (uint i = 0; i < LED_COUNT; i++) {
148
     for (uint i = 0; i < LED_COUNT; i++) {
166
         led_set(i, false);
149
         led_set(i, false);
167
     }
150
     }
151
+}
152
+
153
+void main_loop_hw(void) {
154
+    watchdog_update();
155
+
156
+    usb_run();
157
+    cnsl_run();
158
+    buttons_run();
159
+    encoder_run();
160
+    sequence_run();
161
+    pulse_run();
162
+    ui_run();
163
+
164
+    ui_encoder(encoder_get_diff());
165
+}
166
+
167
+int main(void) {
168
+    watchdog_enable(WATCHDOG_PERIOD_MS, 1);
169
+
170
+    cnsl_init();
171
+    usb_init();
172
+    mem_load();
173
+
174
+    hw_type_detection();
175
+
176
+    bat_init();
177
+    buttons_init();
178
+    encoder_init();
179
+    lcd_init();
180
+    led_init();
181
+
182
+    // show logo
183
+    lcd_draw_bitmap(logo_data,
184
+                    LOGO_WIDTH, LOGO_HEIGHT,
185
+                    0, 0);
186
+
187
+    animate_boot_combos();
168
 
188
 
169
     sequence_init();
189
     sequence_init();
170
     ui_init();
190
     ui_init();

+ 131
- 0
src/mem.c View File

1
+/*
2
+ * mem.c
3
+ *
4
+ * Copyright (c) 2023 - 2024 Thomas Buck (thomas@xythobuz.de)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include <string.h>
20
+
21
+#include "pico/flash.h"
22
+
23
+#include "config.h"
24
+#include "log.h"
25
+#include "sequence.h"
26
+#include "mem.h"
27
+
28
+struct mem_contents {
29
+    uint8_t version;
30
+    uint32_t checksum;
31
+
32
+    struct mem_data data;
33
+};
34
+
35
+#define MEM_CONTENTS_INIT { \
36
+    .version = MEM_VERSION, \
37
+    .checksum = 0,          \
38
+    .data = MEM_DATA_INIT,  \
39
+}
40
+
41
+static const struct mem_contents data_defaults = MEM_CONTENTS_INIT;
42
+static struct mem_contents data_ram = data_defaults;
43
+static const uint8_t *data_flash = (const uint8_t *)(XIP_BASE + EEPROM_FLASH_OFFSET);
44
+
45
+static_assert(sizeof(struct mem_contents) < FLASH_SECTOR_SIZE,
46
+              "Config needs to fit inside a flash sector");
47
+
48
+static uint32_t calc_checksum(const struct mem_contents *data) {
49
+    uint32_t c = 0xFFFFFFFF;
50
+    const uint8_t *d = (const uint8_t *)data;
51
+
52
+    const size_t offset_checksum = offsetof(struct mem_contents, checksum);
53
+    const size_t size_checksum = sizeof(data->checksum);
54
+
55
+    for (size_t i = 0; i < sizeof(struct mem_contents); i++) {
56
+        if ((i >= offset_checksum) && (i < (offset_checksum + size_checksum))) {
57
+            continue;
58
+        }
59
+
60
+        // adapted from "Hacker's Delight"
61
+        c ^= d[i];
62
+        for (size_t j = 0; j < 8; j++) {
63
+            uint32_t mask = -(c & 1);
64
+            c = (c >> 1) ^ (0xEDB88320 & mask);
65
+        }
66
+    }
67
+
68
+    return ~c;
69
+}
70
+
71
+void mem_load_defaults(void) {
72
+    data_ram = data_defaults;
73
+
74
+    for (uint32_t i = 0; i < NUM_CHANNELS; i++) {
75
+        data_ram.data.ch_timings[i] = CH_GPIO_DEFAULT_MS;
76
+    }
77
+}
78
+
79
+void mem_load(void) {
80
+    mem_load_defaults();
81
+
82
+    if (!flash_safe_execute_core_init()) {
83
+        debug("error calling flash_safe_execute_core_init");
84
+    }
85
+
86
+    const struct mem_contents *flash_ptr = (const struct mem_contents *)data_flash;
87
+
88
+    if (flash_ptr->version == MEM_VERSION) {
89
+        debug("found matching config (0x%02X)", flash_ptr->version);
90
+
91
+        uint32_t checksum = calc_checksum(flash_ptr);
92
+        if (checksum != flash_ptr->checksum) {
93
+            debug("invalid checksum (0x%08lX != 0x%08lX)", flash_ptr->checksum, checksum);
94
+        } else {
95
+            if (flash_ptr->data.ch_count != NUM_CHANNELS) {
96
+                debug("invalid channel count (0x%"PRIu32" != 0x%d)", flash_ptr->data.ch_count, NUM_CHANNELS);
97
+            } else {
98
+                debug("loading from flash (0x%08lX)", checksum);
99
+                data_ram = *flash_ptr;
100
+            }
101
+        }
102
+    } else {
103
+        debug("invalid config (0x%02X != 0x%02X)", flash_ptr->version, MEM_VERSION);
104
+    }
105
+}
106
+
107
+static void mem_write_flash(void *param) {
108
+    flash_range_erase(EEPROM_FLASH_OFFSET, FLASH_SECTOR_SIZE);
109
+
110
+    // TODO only need to write with length multiple of FLASH_PAGE_SIZE
111
+    flash_range_program(EEPROM_FLASH_OFFSET, param, FLASH_SECTOR_SIZE);
112
+}
113
+
114
+void mem_write(void) {
115
+    if (memcmp(&data_ram, data_flash, sizeof(struct mem_contents)) == 0) {
116
+        debug("no change, skip write");
117
+        return;
118
+    }
119
+
120
+    data_ram.checksum = calc_checksum(&data_ram);
121
+
122
+    debug("writing new data (0x%08lX)", data_ram.checksum);
123
+    int r = flash_safe_execute(mem_write_flash, &data_ram, FLASH_LOCK_TIMEOUT_MS);
124
+    if (r != PICO_OK) {
125
+        debug("error calling mem_write_flash: %d", r);
126
+    }
127
+}
128
+
129
+struct mem_data *mem_data(void) {
130
+    return &data_ram.data;
131
+}

+ 239
- 0
src/memmap_custom.ld View File

1
+/*
2
+ * Based on Pico SDK memmap_default.ld
3
+ * https://community.element14.com/products/raspberry-pi/b/blog/posts/raspberry-pico-c-sdk-reserve-a-flash-memory-block-for-persistent-storage
4
+ */
5
+
6
+/*
7
+ * TODO hard-coded
8
+ */
9
+__PERSISTENT_STORAGE_LEN = 4k;
10
+
11
+MEMORY
12
+{
13
+    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k - __PERSISTENT_STORAGE_LEN
14
+    FLASH_PERSISTENT(r) : ORIGIN = 0x10000000 + (2048k - __PERSISTENT_STORAGE_LEN) , LENGTH = __PERSISTENT_STORAGE_LEN
15
+    RAM(rwx) : ORIGIN =  0x20000000, LENGTH = 256k
16
+    SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
17
+    SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
18
+}
19
+
20
+ENTRY(_entry_point)
21
+
22
+SECTIONS
23
+{
24
+    /* Second stage bootloader is prepended to the image. It must be 256 bytes big
25
+       and checksummed. It is usually built by the boot_stage2 target
26
+       in the Raspberry Pi Pico SDK
27
+    */
28
+
29
+    .flash_begin : {
30
+        __flash_binary_start = .;
31
+    } > FLASH
32
+
33
+    .boot2 : {
34
+        __boot2_start__ = .;
35
+        KEEP (*(.boot2))
36
+        __boot2_end__ = .;
37
+    } > FLASH
38
+
39
+    ASSERT(__boot2_end__ - __boot2_start__ == 256,
40
+        "ERROR: Pico second stage bootloader must be 256 bytes in size")
41
+
42
+    /* The second stage will always enter the image at the start of .text.
43
+       The debugger will use the ELF entry point, which is the _entry_point
44
+       symbol if present, otherwise defaults to start of .text.
45
+       This can be used to transfer control back to the bootrom on debugger
46
+       launches only, to perform proper flash setup.
47
+    */
48
+
49
+    .text : {
50
+        __logical_binary_start = .;
51
+        KEEP (*(.vectors))
52
+        KEEP (*(.binary_info_header))
53
+        __binary_info_header_end = .;
54
+        KEEP (*(.reset))
55
+        /* TODO revisit this now memset/memcpy/float in ROM */
56
+        /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
57
+         * FLASH ... we will include any thing excluded here in .data below by default */
58
+        *(.init)
59
+        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
60
+        *(.fini)
61
+        /* Pull all c'tors into .text */
62
+        *crtbegin.o(.ctors)
63
+        *crtbegin?.o(.ctors)
64
+        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
65
+        *(SORT(.ctors.*))
66
+        *(.ctors)
67
+        /* Followed by destructors */
68
+        *crtbegin.o(.dtors)
69
+        *crtbegin?.o(.dtors)
70
+        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
71
+        *(SORT(.dtors.*))
72
+        *(.dtors)
73
+
74
+        *(.eh_frame*)
75
+        . = ALIGN(4);
76
+    } > FLASH
77
+
78
+    .rodata : {
79
+        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
80
+        . = ALIGN(4);
81
+        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
82
+        . = ALIGN(4);
83
+    } > FLASH
84
+
85
+    .ARM.extab :
86
+    {
87
+        *(.ARM.extab* .gnu.linkonce.armextab.*)
88
+    } > FLASH
89
+
90
+    __exidx_start = .;
91
+    .ARM.exidx :
92
+    {
93
+        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
94
+    } > FLASH
95
+    __exidx_end = .;
96
+
97
+    /* Machine inspectable binary information */
98
+    . = ALIGN(4);
99
+    __binary_info_start = .;
100
+    .binary_info :
101
+    {
102
+        KEEP(*(.binary_info.keep.*))
103
+        *(.binary_info.*)
104
+    } > FLASH
105
+    __binary_info_end = .;
106
+    . = ALIGN(4);
107
+
108
+   .ram_vector_table (NOLOAD): {
109
+        *(.ram_vector_table)
110
+    } > RAM
111
+
112
+    .data : {
113
+        __data_start__ = .;
114
+        *(vtable)
115
+
116
+        *(.time_critical*)
117
+
118
+        /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
119
+        *(.text*)
120
+        . = ALIGN(4);
121
+        *(.rodata*)
122
+        . = ALIGN(4);
123
+
124
+        *(.data*)
125
+
126
+        . = ALIGN(4);
127
+        *(.after_data.*)
128
+        . = ALIGN(4);
129
+        /* preinit data */
130
+        PROVIDE_HIDDEN (__mutex_array_start = .);
131
+        KEEP(*(SORT(.mutex_array.*)))
132
+        KEEP(*(.mutex_array))
133
+        PROVIDE_HIDDEN (__mutex_array_end = .);
134
+
135
+        . = ALIGN(4);
136
+        /* preinit data */
137
+        PROVIDE_HIDDEN (__preinit_array_start = .);
138
+        KEEP(*(SORT(.preinit_array.*)))
139
+        KEEP(*(.preinit_array))
140
+        PROVIDE_HIDDEN (__preinit_array_end = .);
141
+
142
+        . = ALIGN(4);
143
+        /* init data */
144
+        PROVIDE_HIDDEN (__init_array_start = .);
145
+        KEEP(*(SORT(.init_array.*)))
146
+        KEEP(*(.init_array))
147
+        PROVIDE_HIDDEN (__init_array_end = .);
148
+
149
+        . = ALIGN(4);
150
+        /* finit data */
151
+        PROVIDE_HIDDEN (__fini_array_start = .);
152
+        *(SORT(.fini_array.*))
153
+        *(.fini_array)
154
+        PROVIDE_HIDDEN (__fini_array_end = .);
155
+
156
+        *(.jcr)
157
+        . = ALIGN(4);
158
+        /* All data end */
159
+        __data_end__ = .;
160
+    } > RAM AT> FLASH
161
+    /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
162
+    __etext = LOADADDR(.data);
163
+
164
+    .uninitialized_data (NOLOAD): {
165
+        . = ALIGN(4);
166
+        *(.uninitialized_data*)
167
+    } > RAM
168
+
169
+    /* Start and end symbols must be word-aligned */
170
+    .scratch_x : {
171
+        __scratch_x_start__ = .;
172
+        *(.scratch_x.*)
173
+        . = ALIGN(4);
174
+        __scratch_x_end__ = .;
175
+    } > SCRATCH_X AT > FLASH
176
+    __scratch_x_source__ = LOADADDR(.scratch_x);
177
+
178
+    .scratch_y : {
179
+        __scratch_y_start__ = .;
180
+        *(.scratch_y.*)
181
+        . = ALIGN(4);
182
+        __scratch_y_end__ = .;
183
+    } > SCRATCH_Y AT > FLASH
184
+    __scratch_y_source__ = LOADADDR(.scratch_y);
185
+
186
+    .bss  : {
187
+        . = ALIGN(4);
188
+        __bss_start__ = .;
189
+        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
190
+        *(COMMON)
191
+        . = ALIGN(4);
192
+        __bss_end__ = .;
193
+    } > RAM
194
+
195
+    .heap (NOLOAD):
196
+    {
197
+        __end__ = .;
198
+        end = __end__;
199
+        KEEP(*(.heap*))
200
+        __HeapLimit = .;
201
+    } > RAM
202
+
203
+    /* .stack*_dummy section doesn't contains any symbols. It is only
204
+     * used for linker to calculate size of stack sections, and assign
205
+     * values to stack symbols later
206
+     *
207
+     * stack1 section may be empty/missing if platform_launch_core1 is not used */
208
+
209
+    /* by default we put core 0 stack at the end of scratch Y, so that if core 1
210
+     * stack is not used then all of SCRATCH_X is free.
211
+     */
212
+    .stack1_dummy (NOLOAD):
213
+    {
214
+        *(.stack1*)
215
+    } > SCRATCH_X
216
+    .stack_dummy (NOLOAD):
217
+    {
218
+        KEEP(*(.stack*))
219
+    } > SCRATCH_Y
220
+
221
+    .flash_end : {
222
+        PROVIDE(__flash_binary_end = .);
223
+    } > FLASH
224
+
225
+    /* stack limit is poorly named, but historically is maximum heap ptr */
226
+    __StackLimit = ORIGIN(RAM) + LENGTH(RAM);
227
+    __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
228
+    __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
229
+    __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
230
+    __StackBottom = __StackTop - SIZEOF(.stack_dummy);
231
+    PROVIDE(__stack = __StackTop);
232
+
233
+    /* Check if data + heap + stack exceeds RAM limit */
234
+    ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
235
+
236
+    ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
237
+    /* todo assert on extra code */
238
+}
239
+

+ 13
- 14
src/sequence.c View File

22
 
22
 
23
 #include "led.h"
23
 #include "led.h"
24
 #include "log.h"
24
 #include "log.h"
25
+#include "mem.h"
25
 #include "pulse.h"
26
 #include "pulse.h"
26
 #include "ui.h"
27
 #include "ui.h"
27
 #include "sequence.h"
28
 #include "sequence.h"
28
 
29
 
29
-static const uint32_t channel_times[NUM_CHANNELS] = CH_GPIO_TIMINGS;
30
-
31
 static uint64_t us_per_beat = 0;
30
 static uint64_t us_per_beat = 0;
32
 static uint64_t last_t = 0;
31
 static uint64_t last_t = 0;
33
 static uint64_t beattimer_start = 0;
32
 static uint64_t beattimer_start = 0;
113
     switch (btn) {
112
     switch (btn) {
114
         case BTN_A:
113
         case BTN_A:
115
         case BTN_E: {
114
         case BTN_E: {
116
-            pulse_trigger_out(0, channel_times[0]);
117
-            pulse_trigger_led(0, channel_times[0]);
118
-            pulse_trigger_led(4, channel_times[0]);
115
+            pulse_trigger_out(0, mem_data()->ch_timings[0]);
116
+            pulse_trigger_led(0, mem_data()->ch_timings[0]);
117
+            pulse_trigger_led(4, mem_data()->ch_timings[0]);
119
             break;
118
             break;
120
         }
119
         }
121
 
120
 
122
         case BTN_B:
121
         case BTN_B:
123
         case BTN_F: {
122
         case BTN_F: {
124
-            pulse_trigger_out(1, channel_times[1]);
125
-            pulse_trigger_led(1, channel_times[1]);
126
-            pulse_trigger_led(5, channel_times[1]);
123
+            pulse_trigger_out(1, mem_data()->ch_timings[1]);
124
+            pulse_trigger_led(1, mem_data()->ch_timings[1]);
125
+            pulse_trigger_led(5, mem_data()->ch_timings[1]);
127
             break;
126
             break;
128
         }
127
         }
129
 
128
 
130
         case BTN_C:
129
         case BTN_C:
131
         case BTN_G: {
130
         case BTN_G: {
132
-            pulse_trigger_out(2, channel_times[2]);
133
-            pulse_trigger_led(2, channel_times[2]);
134
-            pulse_trigger_led(6, channel_times[2]);
131
+            pulse_trigger_out(2, mem_data()->ch_timings[2]);
132
+            pulse_trigger_led(2, mem_data()->ch_timings[2]);
133
+            pulse_trigger_led(6, mem_data()->ch_timings[2]);
135
             break;
134
             break;
136
         }
135
         }
137
 
136
 
228
 
227
 
229
         for (uint ch = 0; ch < NUM_CHANNELS; ch++) {
228
         for (uint ch = 0; ch < NUM_CHANNELS; ch++) {
230
             if (sequence[i] & (1 << ch)) {
229
             if (sequence[i] & (1 << ch)) {
231
-                pulse_trigger_out(ch, channel_times[ch]);
230
+                pulse_trigger_out(ch, mem_data()->ch_timings[ch]);
232
                 if (ui_get_machinemode() == MODE_LOOPSTATION) {
231
                 if (ui_get_machinemode() == MODE_LOOPSTATION) {
233
-                    pulse_trigger_led(ch, channel_times[ch]);
234
-                    pulse_trigger_led(ch + 4, channel_times[ch]);
232
+                    pulse_trigger_led(ch, mem_data()->ch_timings[ch]);
233
+                    pulse_trigger_led(ch + 4, mem_data()->ch_timings[ch]);
235
                 }
234
                 }
236
             }
235
             }
237
         }
236
         }

+ 114
- 0
src/settings.c View File

1
+/*
2
+ * settings.c
3
+ *
4
+ * Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include <stdio.h>
20
+#include <inttypes.h>
21
+
22
+#include "pico/stdlib.h"
23
+#include "hardware/watchdog.h"
24
+
25
+#include "config.h"
26
+#include "buttons.h"
27
+#include "console.h"
28
+#include "encoder.h"
29
+#include "lcd.h"
30
+#include "log.h"
31
+#include "mem.h"
32
+#include "sequence.h"
33
+#include "ui.h"
34
+#include "usb.h"
35
+#include "settings.h"
36
+
37
+static uint32_t setting = 0;
38
+
39
+static void settings_redraw(void) {
40
+    char mode[64] = {0};
41
+    char val[64] = {0};
42
+    char bat[64] = {0};
43
+
44
+    if (setting >= (NUM_CHANNELS + 1)) {
45
+        snprintf(mode, sizeof(mode) - 1, "Memory");
46
+        snprintf(val, sizeof(val) - 1, "Save");
47
+    } else if (setting == NUM_CHANNELS) {
48
+        snprintf(mode, sizeof(mode) - 1, "Boot Anim");
49
+        snprintf(val, sizeof(val) - 1, "%"PRIu32"ms", mem_data()->boot_anim_ms);
50
+    } else {
51
+        snprintf(mode, sizeof(mode) - 1, "Channel %"PRIu32, setting);
52
+        snprintf(val, sizeof(val) - 1, "%"PRIu32"ms", mem_data()->ch_timings[setting]);
53
+    }
54
+
55
+    lcd_draw(mode, val, bat);
56
+}
57
+
58
+static void settings_buttons(enum buttons btn, bool val) {
59
+    switch (btn) {
60
+        case BTN_CLICK: {
61
+            if (val) {
62
+                setting = (setting + 1) % (NUM_CHANNELS + 2);
63
+                settings_redraw();
64
+            }
65
+            break;
66
+        }
67
+
68
+        default: {
69
+            break;
70
+        }
71
+    }
72
+}
73
+
74
+static bool settings_encoder(int32_t val) {
75
+    if (val == 0) {
76
+        return false;
77
+    }
78
+
79
+    if (setting >= (NUM_CHANNELS + 1)) {
80
+        mem_write();
81
+        lcd_draw("Save", "Ok!", "");
82
+        return true;
83
+    } else if (setting == NUM_CHANNELS) {
84
+        int32_t tmp = mem_data()->boot_anim_ms + val;
85
+        KEEP_IN_RANGE(tmp, 0, 10000);
86
+        mem_data()->boot_anim_ms = tmp;
87
+    } else {
88
+        int32_t tmp = mem_data()->ch_timings[setting] + val;
89
+        KEEP_IN_RANGE(tmp, 1, 100);
90
+        mem_data()->ch_timings[setting] = tmp;
91
+    }
92
+
93
+    settings_redraw();
94
+    return false;
95
+}
96
+
97
+void settings_init(void) {
98
+    buttons_callback(settings_buttons);
99
+    settings_redraw();
100
+}
101
+
102
+void settings_run(void) {
103
+    while (1) {
104
+        watchdog_update();
105
+        buttons_run();
106
+        encoder_run();
107
+        usb_run();
108
+        cnsl_run();
109
+
110
+        if (settings_encoder(encoder_get_diff())) {
111
+            break;
112
+        }
113
+    }
114
+}

+ 3
- 13
src/ui.c View File

27
 #include "lcd.h"
27
 #include "lcd.h"
28
 #include "led.h"
28
 #include "led.h"
29
 #include "log.h"
29
 #include "log.h"
30
+#include "mem.h"
30
 #include "pulse.h"
31
 #include "pulse.h"
31
 #include "sequence.h"
32
 #include "sequence.h"
32
 #include "usb.h"
33
 #include "usb.h"
33
 #include "usb_midi.h"
34
 #include "usb_midi.h"
34
 #include "ui.h"
35
 #include "ui.h"
35
 
36
 
36
-#define KEEP_IN_RANGE(val, min, len) { \
37
-    while (val > (len - min)) {        \
38
-        val -= len;                    \
39
-    }                                  \
40
-    while (val < min) {                \
41
-        val += len;                    \
42
-    }                                  \
43
-}
44
-
45
 enum ui_settings {
37
 enum ui_settings {
46
     SETTING_MODE = 0,
38
     SETTING_MODE = 0,
47
 
39
 
95
 static float last_percentage = 0.0f;
87
 static float last_percentage = 0.0f;
96
 static uint8_t midi_rx = 0, midi_tx = 0;
88
 static uint8_t midi_rx = 0, midi_tx = 0;
97
 
89
 
98
-static const uint32_t channel_times[NUM_CHANNELS] = CH_GPIO_TIMINGS;
99
-
100
 enum machine_modes ui_get_machinemode(void) {
90
 enum machine_modes ui_get_machinemode(void) {
101
     return machine_mode;
91
     return machine_mode;
102
 }
92
 }
268
         case BTN_H: {
258
         case BTN_H: {
269
             if (val) {
259
             if (val) {
270
                 usb_midi_tx(midi_tx, btn - BTN_A, 0x7F);
260
                 usb_midi_tx(midi_tx, btn - BTN_A, 0x7F);
271
-                pulse_trigger_led(btn - BTN_A, channel_times[0]);
261
+                pulse_trigger_led(btn - BTN_A, mem_data()->ch_timings[0]);
272
             }
262
             }
273
         }
263
         }
274
 
264
 
424
 
414
 
425
     note = note % NUM_CHANNELS;
415
     note = note % NUM_CHANNELS;
426
     if (velocity > 0) {
416
     if (velocity > 0) {
427
-        pulse_trigger_out(note, channel_times[note]);
417
+        pulse_trigger_out(note, mem_data()->ch_timings[note]);
428
     }
418
     }
429
 }
419
 }
430
 
420
 

Loading…
Cancel
Save