Переглянути джерело

add configuration storage in flash

Thomas Buck 6 місяці тому
джерело
коміт
a59876d82d
15 змінених файлів з 676 додано та 80 видалено
  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 Переглянути файл

@@ -47,6 +47,8 @@ target_sources(drumkit PUBLIC
47 47
     src/usb_cdc.c
48 48
     src/usb_descriptors.c
49 49
     src/usb_midi.c
50
+    src/settings.c
51
+    src/mem.c
50 52
 
51 53
     pico-ssd1306/ssd1306.c
52 54
 )
@@ -95,11 +97,12 @@ target_link_libraries(drumkit
95 97
     tinyusb_device
96 98
     tinyusb_board
97 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 107
 # fix for Errata RP2040-E5 (the fix requires use of GPIO 15)
105 108
 target_compile_definitions(drumkit PUBLIC PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1)

+ 2
- 1
docs/src/usage.md Переглянути файл

@@ -13,6 +13,7 @@ Take a look at the sub-page for each mode for details.
13 13
 
14 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 17
 * hold the encoder `Click` button to show version information of the firmware
18 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 Переглянути файл

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

+ 3
- 1
include/encoder.h Переглянути файл

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

+ 56
- 0
include/mem.h Переглянути файл

@@ -0,0 +1,56 @@
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 Переглянути файл

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

+ 25
- 0
include/settings.h Переглянути файл

@@ -0,0 +1,25 @@
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 Переглянути файл

@@ -21,6 +21,15 @@
21 21
 
22 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 33
 enum machine_modes {
25 34
     MODE_LOOPSTATION = 0,
26 35
     MODE_DRUMMACHINE,

+ 9
- 2
src/encoder.c Переглянути файл

@@ -69,11 +69,18 @@ void encoder_init(void) {
69 69
     positionExtPrev = 0;
70 70
 }
71 71
 
72
-int32_t encoder_pos(void)
73
-{
72
+int32_t encoder_pos(void) {
74 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 84
 void encoder_run(void) {
78 85
     int8_t thisState = 0;
79 86
     if (hw_type == HW_PROTOTYPE) {

+ 64
- 44
src/main.c Переглянути файл

@@ -29,8 +29,10 @@
29 29
 #include "led.h"
30 30
 #include "log.h"
31 31
 #include "logo.h"
32
+#include "mem.h"
32 33
 #include "pulse.h"
33 34
 #include "sequence.h"
35
+#include "settings.h"
34 36
 #include "ui.h"
35 37
 #include "usb.h"
36 38
 #include "main.h"
@@ -45,6 +47,16 @@ static void debug_buttons_callback(enum buttons btn, bool v) {
45 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 60
 void reset_to_bootloader(void) {
49 61
     lcd_draw_bye();
50 62
 
@@ -60,15 +72,6 @@ void reset_to_main(void) {
60 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 75
 static void sleep_ms_wd(uint32_t ms) {
73 76
     for (uint32_t i = 0; i < ms; i++) {
74 77
         watchdog_update();
@@ -76,46 +79,19 @@ static void sleep_ms_wd(uint32_t ms) {
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 83
     gpio_init(gpio_hw_detect);
100 84
     gpio_set_dir(gpio_hw_detect, GPIO_IN);
101 85
     gpio_pull_up(gpio_hw_detect);
86
+
102 87
     if (gpio_get(gpio_hw_detect)) {
103 88
         hw_type = HW_PROTOTYPE;
104 89
     } else {
105 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 95
     // read out button state for debug options
120 96
     buttons_callback(debug_buttons_callback);
121 97
     for (uint i = 0; i < (DEBOUNCE_DELAY_MS + 5); i++) {
@@ -126,12 +102,19 @@ int main(void) {
126 102
         sleep_ms_wd(1);
127 103
     }
128 104
 
105
+    uint32_t cnt = debug_count_buttons();
106
+
129 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 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 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 118
         // show version info
136 119
         lcd_draw_version();
137 120
 
@@ -157,7 +140,7 @@ int main(void) {
157 140
             usb_run();
158 141
             cnsl_run();
159 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,6 +148,43 @@ int main(void) {
165 148
     for (uint i = 0; i < LED_COUNT; i++) {
166 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 189
     sequence_init();
170 190
     ui_init();

+ 131
- 0
src/mem.c Переглянути файл

@@ -0,0 +1,131 @@
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 Переглянути файл

@@ -0,0 +1,239 @@
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 Переглянути файл

@@ -22,12 +22,11 @@
22 22
 
23 23
 #include "led.h"
24 24
 #include "log.h"
25
+#include "mem.h"
25 26
 #include "pulse.h"
26 27
 #include "ui.h"
27 28
 #include "sequence.h"
28 29
 
29
-static const uint32_t channel_times[NUM_CHANNELS] = CH_GPIO_TIMINGS;
30
-
31 30
 static uint64_t us_per_beat = 0;
32 31
 static uint64_t last_t = 0;
33 32
 static uint64_t beattimer_start = 0;
@@ -113,25 +112,25 @@ void sequence_handle_button_loopstation(enum buttons btn, bool rec) {
113 112
     switch (btn) {
114 113
         case BTN_A:
115 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 118
             break;
120 119
         }
121 120
 
122 121
         case BTN_B:
123 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 126
             break;
128 127
         }
129 128
 
130 129
         case BTN_C:
131 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 134
             break;
136 135
         }
137 136
 
@@ -228,10 +227,10 @@ void sequence_run(void) {
228 227
 
229 228
         for (uint ch = 0; ch < NUM_CHANNELS; ch++) {
230 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 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 Переглянути файл

@@ -0,0 +1,114 @@
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 Переглянути файл

@@ -27,21 +27,13 @@
27 27
 #include "lcd.h"
28 28
 #include "led.h"
29 29
 #include "log.h"
30
+#include "mem.h"
30 31
 #include "pulse.h"
31 32
 #include "sequence.h"
32 33
 #include "usb.h"
33 34
 #include "usb_midi.h"
34 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 37
 enum ui_settings {
46 38
     SETTING_MODE = 0,
47 39
 
@@ -95,8 +87,6 @@ static float last_voltage = 0.0f;
95 87
 static float last_percentage = 0.0f;
96 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 90
 enum machine_modes ui_get_machinemode(void) {
101 91
     return machine_mode;
102 92
 }
@@ -268,7 +258,7 @@ static void ui_buttons_midi(enum buttons btn, bool val) {
268 258
         case BTN_H: {
269 259
             if (val) {
270 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,7 +414,7 @@ void ui_midi_set(uint8_t channel, uint8_t note, uint8_t velocity) {
424 414
 
425 415
     note = note % NUM_CHANNELS;
426 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
 

Завантаження…
Відмінити
Зберегти