Browse Source

implement missing loop station features.

update pico-sdk.
other small improvements.
mark as release 1.0.
Thomas B 6 days ago
parent
commit
ffb9c9f4d6
12 changed files with 180 additions and 57 deletions
  1. 1
    0
      .gitignore
  2. 6
    1
      docs/src/drummachine.md
  3. 4
    0
      docs/src/pcb2.md
  4. 5
    5
      include/config.h
  5. 2
    1
      include/sequence.h
  6. 1
    1
      pico-sdk
  7. 3
    3
      src/adc.c
  8. 8
    4
      src/lcd.c
  9. 16
    9
      src/mem.c
  10. 62
    20
      src/sequence.c
  11. 53
    11
      src/ui.c
  12. 19
    2
      src/usb_cdc.c

+ 1
- 0
.gitignore View File

@@ -24,3 +24,4 @@ docs/src/stl
24 24
 docs/src/inc_*.md
25 25
 
26 26
 3dprint/stl
27
+compile_commands.json

+ 6
- 1
docs/src/drummachine.md View File

@@ -1,3 +1,8 @@
1 1
 # Drum Machine
2 2
 
3
-**TODO**: work in progress
3
+The drum-machine runs through a sequence of a given length, set by the `Length` menu.
4
+The corresponding LED lights up at it's moment in the sequence.
5
+Change the speed the sequence is running at with the `BPM` setting.
6
+If you have selected a `Length` that is longer than the available number of buttons / LEDs, you can switch your "window" of the sequence using the `Bank` setting.
7
+Select the currently edited output channel with the `Channel` menu.
8
+Use the CLEAR button below the encoder to zero-out the currently selected channel.

+ 4
- 0
docs/src/pcb2.md View File

@@ -17,6 +17,10 @@ Revision 2 has changed this to be compatible to THT as well.
17 17
 Revision 1 and 2 are missing diodes on the button matrix.
18 18
 So when holding down three or more buttons simultaneously you get ghosting on other buttons.
19 19
 
20
+Depending on the solenoids you may reach a situation where not all channels can be properly triggered simultaneously.
21
+
22
+**To Do**: try to improve this by adding more capacitance to the power rail or between the voltage regulators and the mosfets?
23
+
20 24
 ## Bill of Materials
21 25
 
22 26
 These are all the parts required to assemble the V2 PCB.

+ 5
- 5
include/config.h View File

@@ -1,7 +1,7 @@
1 1
 /*
2 2
  * config.h
3 3
  *
4
- * Copyright (c) 2022 - 2024 Thomas Buck (thomas@xythobuz.de)
4
+ * Copyright (c) 2022 - 2025 Thomas Buck (thomas@xythobuz.de)
5 5
  *
6 6
  * This program is free software: you can redistribute it and/or modify
7 7
  * it under the terms of the GNU General Public License as published by
@@ -19,16 +19,16 @@
19 19
 #ifndef __CONFIG_H__
20 20
 #define __CONFIG_H__
21 21
 
22
-#define VERSION_MAJOR 0
23
-#define VERSION_MINOR 1
22
+#define VERSION_MAJOR 1
23
+#define VERSION_MINOR 0
24 24
 
25 25
 #define WATCHDOG_PERIOD_MS 100
26 26
 #define LOGO_INIT_MS 1000
27 27
 #define FLASH_LOCK_TIMEOUT_MS 500
28 28
 #define CH_GPIO_DEFAULT_MS 42
29 29
 
30
-// ASCII 0x18 = CAN (cancel)
31
-#define ENTER_BOOTLOADER_MAGIC 0x18
30
+#define ENTER_BOOTLOADER_MAGIC 0x18 // ASCII 0x18 = CAN (cancel)
31
+#define ENTER_BOOTLOADER_BAUD 1200 // support picotool-like magic baudrate reset
32 32
 
33 33
 //#define DISABLE_CDC_DTR_CHECK
34 34
 #define DEBOUNCE_DELAY_MS 5

+ 2
- 1
include/sequence.h View File

@@ -1,7 +1,7 @@
1 1
 /*
2 2
  * sequence.h
3 3
  *
4
- * Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
4
+ * Copyright (c) 2024 - 2025 Thomas Buck (thomas@xythobuz.de)
5 5
  *
6 6
  * This program is free software: you can redistribute it and/or modify
7 7
  * it under the terms of the GNU General Public License as published by
@@ -46,6 +46,7 @@ void sequence_set_beats(uint32_t new_beats);
46 46
 uint32_t sequence_get_beats(void);
47 47
 
48 48
 void sequence_set_bank(uint32_t new_bank);
49
+void sequence_copy_bank(uint32_t new_bank);
49 50
 uint32_t sequence_get_bank(void);
50 51
 
51 52
 void sequence_set_channel(uint32_t new_channel);

+ 1
- 1
pico-sdk

@@ -1 +1 @@
1
-Subproject commit 6a7db34ff63345a7badec79ebea3aaef1712f374
1
+Subproject commit bddd20f928ce76142793bef434d4f75f4af6e433

+ 3
- 3
src/adc.c View File

@@ -1,7 +1,7 @@
1 1
 /*
2 2
  * adc.c
3 3
  *
4
- * Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
4
+ * Copyright (c) 2024 - 2025 Thomas Buck (thomas@xythobuz.de)
5 5
  *
6 6
  * This program is free software: you can redistribute it and/or modify
7 7
  * it under the terms of the GNU General Public License as published by
@@ -55,8 +55,8 @@ static float bat_read(void) {
55 55
 
56 56
 void bat_init(void) {
57 57
     adc_init();
58
-    adc_gpio_init( ADC_PIN);
59
-    adc_select_input( ADC_NUM);
58
+    adc_gpio_init(ADC_PIN);
59
+    adc_select_input(ADC_NUM);
60 60
     filtered = bat_read();
61 61
 }
62 62
 

+ 8
- 4
src/lcd.c View File

@@ -1,7 +1,7 @@
1 1
 /*
2 2
  * lcd.c
3 3
  *
4
- * Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
4
+ * Copyright (c) 2024 - 2025 Thomas Buck (thomas@xythobuz.de)
5 5
  *
6 6
  * This program is free software: you can redistribute it and/or modify
7 7
  * it under the terms of the GNU General Public License as published by
@@ -16,6 +16,7 @@
16 16
  * See <http://www.gnu.org/licenses/>.
17 17
  */
18 18
 
19
+#include <pico/version.h>
19 20
 #include <stdio.h>
20 21
 #include <string.h>
21 22
 
@@ -199,16 +200,19 @@ void lcd_draw_version(void) {
199 200
     char hw_id_str[42] = {0};
200 201
     if (hw_type == HW_PROTOTYPE) {
201 202
         snprintf(hw_id_str, sizeof(hw_id_str) - 1,
202
-                 "HW Prototype");
203
+                 "LARS HW: Proto");
203 204
     } else if (hw_type == HW_V2) {
204 205
         snprintf(hw_id_str, sizeof(hw_id_str) - 1,
205
-                 "HW V2");
206
+                 "LARS HW: V2");
206 207
     } else {
207 208
         snprintf(hw_id_str, sizeof(hw_id_str) - 1,
208
-                 "HW unknown %X", hw_type);
209
+                 "LARS HW: ?? %X", hw_type);
209 210
     }
210 211
     ssd1306_draw_string(&disp, 0, FONT_HEIGHT * 2 + 1 + (FONT_HEIGHT + 1) * 3, 1,
211 212
                         hw_id_str);
212 213
 
214
+    ssd1306_draw_string(&disp, 0, FONT_HEIGHT * 2 + 1 + (FONT_HEIGHT + 1) * 4, 1,
215
+                        "Pico SDK: " PICO_SDK_VERSION_STRING);
216
+
213 217
     ssd1306_show(&disp);
214 218
 }

+ 16
- 9
src/mem.c View File

@@ -1,7 +1,7 @@
1 1
 /*
2 2
  * mem.c
3 3
  *
4
- * Copyright (c) 2023 - 2024 Thomas Buck (thomas@xythobuz.de)
4
+ * Copyright (c) 2023 - 2025 Thomas Buck (thomas@xythobuz.de)
5 5
  *
6 6
  * This program is free software: you can redistribute it and/or modify
7 7
  * it under the terms of the GNU General Public License as published by
@@ -41,8 +41,10 @@ struct mem_contents {
41 41
 static const struct mem_contents data_defaults = MEM_CONTENTS_INIT;
42 42
 static struct mem_contents data_ram = data_defaults;
43 43
 static const uint8_t *data_flash = (const uint8_t *)(XIP_BASE + EEPROM_FLASH_OFFSET);
44
+static uint8_t page_buffer[FLASH_PAGE_SIZE] = {0};
44 45
 
45
-static_assert(sizeof(struct mem_contents) < FLASH_SECTOR_SIZE,
46
+#define MEM_DATA_SIZE sizeof(struct mem_contents)
47
+static_assert(MEM_DATA_SIZE < FLASH_SECTOR_SIZE,
46 48
               "Config needs to fit inside a flash sector");
47 49
 
48 50
 static uint32_t calc_checksum(const struct mem_contents *data) {
@@ -86,29 +88,34 @@ void mem_load(void) {
86 88
     const struct mem_contents *flash_ptr = (const struct mem_contents *)data_flash;
87 89
 
88 90
     if (flash_ptr->version == MEM_VERSION) {
89
-        debug("found matching config (0x%02X)", flash_ptr->version);
91
+        debug("found matching config (0x%02"PRIX8")", flash_ptr->version);
90 92
 
91 93
         uint32_t checksum = calc_checksum(flash_ptr);
92 94
         if (checksum != flash_ptr->checksum) {
93
-            debug("invalid checksum (0x%08lX != 0x%08lX)", flash_ptr->checksum, checksum);
95
+            debug("invalid checksum (0x%08"PRIX32" != 0x%08"PRIX32")", flash_ptr->checksum, checksum);
94 96
         } else {
95 97
             if (flash_ptr->data.ch_count != NUM_CHANNELS) {
96 98
                 debug("invalid channel count (0x%"PRIu32" != 0x%d)", flash_ptr->data.ch_count, NUM_CHANNELS);
97 99
             } else {
98
-                debug("loading from flash (0x%08lX)", checksum);
100
+                debug("loading from flash (0x%08"PRIX32")", checksum);
99 101
                 data_ram = *flash_ptr;
100 102
             }
101 103
         }
102 104
     } else {
103
-        debug("invalid config (0x%02X != 0x%02X)", flash_ptr->version, MEM_VERSION);
105
+        debug("invalid config (0x%02"PRIX8" != 0x%02X)", flash_ptr->version, MEM_VERSION);
104 106
     }
105 107
 }
106 108
 
107 109
 static void mem_write_flash(void *param) {
110
+    // we erase a 4K sector (FLASH_SECTOR_SIZE), but...
108 111
     flash_range_erase(EEPROM_FLASH_OFFSET, FLASH_SECTOR_SIZE);
109 112
 
110
-    // TODO only need to write with length multiple of FLASH_PAGE_SIZE
111
-    flash_range_program(EEPROM_FLASH_OFFSET, param, FLASH_SECTOR_SIZE);
113
+    // ...can write in 256 byte pages (FLASH_PAGE_SIZE)
114
+    for (uint32_t off = 0; off < MEM_DATA_SIZE; off += FLASH_PAGE_SIZE) {
115
+        memset(page_buffer, 0xFF, FLASH_PAGE_SIZE);
116
+        memcpy(page_buffer, param + off, MIN(FLASH_PAGE_SIZE, MEM_DATA_SIZE - off));
117
+        flash_range_program(EEPROM_FLASH_OFFSET + off, page_buffer, FLASH_PAGE_SIZE);
118
+    }
112 119
 }
113 120
 
114 121
 void mem_write(void) {
@@ -119,7 +126,7 @@ void mem_write(void) {
119 126
 
120 127
     data_ram.checksum = calc_checksum(&data_ram);
121 128
 
122
-    debug("writing new data (0x%08lX)", data_ram.checksum);
129
+    debug("writing new data (0x%08"PRIX32")", data_ram.checksum);
123 130
     int r = flash_safe_execute(mem_write_flash, &data_ram, FLASH_LOCK_TIMEOUT_MS);
124 131
     if (r != PICO_OK) {
125 132
         debug("error calling mem_write_flash: %d", r);

+ 62
- 20
src/sequence.c View File

@@ -1,7 +1,7 @@
1 1
 /*
2 2
  * sequence.c
3 3
  *
4
- * Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
4
+ * Copyright (c) 2024 - 2025 Thomas Buck (thomas@xythobuz.de)
5 5
  *
6 6
  * This program is free software: you can redistribute it and/or modify
7 7
  * it under the terms of the GNU General Public License as published by
@@ -38,7 +38,17 @@ static uint32_t max_banks_currently = 0;
38 38
 static uint32_t channel = 0;
39 39
 static bool button_held[NUM_BTNS] = {0};
40 40
 
41
-static enum channels sequence[MAX_BEATS] = {0};
41
+/*
42
+ * 'bank' has a dual meaning here. this is a bit fugly.
43
+ *
44
+ * In drum-machine mode, the bank means which part of the sequence
45
+ * is shown on the available LEDs / buttons.
46
+ * This is always using sequence[0][n]
47
+ *
48
+ * In loop-station mode it actually means the sequence[bank][n].
49
+ */
50
+
51
+static enum channels sequence[MAX_BANKS][MAX_BEATS] = {0};
42 52
 
43 53
 void sequence_init(void) {
44 54
     us_per_beat = 0;
@@ -47,8 +57,10 @@ void sequence_init(void) {
47 57
     last_i = 0;
48 58
     max_banks_currently = (beats + (NUM_BTNS - 1)) / NUM_BTNS;
49 59
 
50
-    for (uint i = 0; i < MAX_BEATS; i++) {
51
-        sequence[i] = 0;
60
+    for (uint b = 0; b < MAX_BANKS; b++) {
61
+        for (uint i = 0; i < MAX_BEATS; i++) {
62
+            sequence[b][i] = 0;
63
+        }
52 64
     }
53 65
 }
54 66
 
@@ -85,6 +97,15 @@ void sequence_set_bank(uint32_t new_bank) {
85 97
     bank = (b < max_banks_currently) ? b : 0;
86 98
 }
87 99
 
100
+void sequence_copy_bank(uint32_t new_bank) {
101
+    uint32_t prev = bank;
102
+    sequence_set_bank(new_bank);
103
+
104
+    for (uint i = 0; i < MAX_BEATS; i++) {
105
+        sequence[bank][i] = sequence[prev][i];
106
+    }
107
+}
108
+
88 109
 uint32_t sequence_get_bank(void) {
89 110
     return bank;
90 111
 }
@@ -101,19 +122,19 @@ uint64_t sequence_get_us(void) {
101 122
     return us_per_beat;
102 123
 }
103 124
 
104
-static void sequence_set(uint32_t beat, enum channels ch, bool value) {
125
+static void sequence_set(uint32_t beat, enum channels ch, bool value, uint32_t sel_bank) {
105 126
     if (beat < MAX_BEATS) {
106 127
         if (value) {
107
-            sequence[beat] |= ch;
128
+            sequence[sel_bank][beat] |= ch;
108 129
         } else {
109
-            sequence[beat] &= ~ch;
130
+            sequence[sel_bank][beat] &= ~ch;
110 131
         }
111 132
     }
112 133
 }
113 134
 
114
-static bool sequence_get(uint32_t beat, enum channels ch) {
135
+static bool sequence_get(uint32_t beat, enum channels ch, uint32_t sel_bank) {
115 136
     if (beat < MAX_BEATS) {
116
-        return (sequence[beat] & ch) != 0;
137
+        return (sequence[sel_bank][beat] & ch) != 0;
117 138
     }
118 139
     return false;
119 140
 }
@@ -177,18 +198,28 @@ void sequence_handle_button_loopstation(enum buttons btn, bool val) {
177 198
             }
178 199
 
179 200
             if (button_held[BTN_H]) {
180
-                // right REC: clear all loops in all banks
181
-                // TODO other banks
201
+                // right REC: clear all loops in all banks, including length
182 202
                 sequence_init();
183 203
             } else if (button_held[BTN_D]) {
184 204
                 // left REC: clear the current loop, including the length
185
-                sequence_init();
205
+                us_per_beat = 0;
206
+                for (uint i = 0; i < MAX_BEATS; i++) {
207
+                    sequence[bank][i] = 0;
208
+                }
186 209
             } else if (button_held[BTN_E] || button_held[BTN_F] || button_held[BTN_G]) {
187 210
                 // channel mute buttons: clear only this channel in the current loop
188
-                // TODO
211
+                enum channels to_del = 0;
212
+                if (button_held[BTN_E]) to_del |= CH1;
213
+                if (button_held[BTN_F]) to_del |= CH2;
214
+                if (button_held[BTN_G]) to_del |= CH3;
215
+                for (uint i = 0; i < MAX_BEATS; i++) {
216
+                    sequence_set(i, to_del, false, bank);
217
+                }
189 218
             } else {
190 219
                 // on its own: clear all channels of the current loop, keeping the set length
191
-                // TODO
220
+                for (uint i = 0; i < MAX_BEATS; i++) {
221
+                    sequence[bank][i] = 0;
222
+                }
192 223
             }
193 224
 
194 225
             ui_redraw();
@@ -204,17 +235,17 @@ void sequence_handle_button_loopstation(enum buttons btn, bool val) {
204 235
     if ((button_held[BTN_D] || button_held[BTN_H]) && val) {
205 236
         switch (btn) {
206 237
             case BTN_A: {
207
-                sequence_set(last_i, CH_KICK, true);
238
+                sequence_set(last_i, CH_KICK, true, bank);
208 239
                 break;
209 240
             }
210 241
 
211 242
             case BTN_B: {
212
-                sequence_set(last_i, CH_SNARE, true);
243
+                sequence_set(last_i, CH_SNARE, true, bank);
213 244
                 break;
214 245
             }
215 246
 
216 247
             case BTN_C: {
217
-                sequence_set(last_i, CH_HIHAT, true);
248
+                sequence_set(last_i, CH_HIHAT, true, bank);
218 249
                 break;
219 250
             }
220 251
 
@@ -236,8 +267,16 @@ void sequence_handle_button_drummachine(enum buttons btn) {
236 267
         case BTN_G:
237 268
         case BTN_H: {
238 269
             uint32_t beat = (btn - BTN_A) + bank * LED_COUNT;
239
-            bool val = !sequence_get(beat, 1 << channel);
240
-            sequence_set(beat, 1 << channel, val);
270
+            bool val = !sequence_get(beat, 1 << channel, 0);
271
+            sequence_set(beat, 1 << channel, val, 0);
272
+            break;
273
+        }
274
+
275
+        case BTN_CLEAR: {
276
+            // clear current channel
277
+            for (uint i = 0; i < MAX_BEATS; i++) {
278
+                sequence_set(i, 1 << channel, false, 0);
279
+            }
241 280
             break;
242 281
         }
243 282
 
@@ -281,7 +320,8 @@ void sequence_run(void) {
281 320
                 continue;
282 321
             }
283 322
 
284
-            if (sequence[i] & (1 << ch)) {
323
+            uint32_t b = (ui_get_machinemode() == MODE_DRUMMACHINE) ? 0 : bank;
324
+            if (sequence[b][i] & (1 << ch)) {
285 325
                 // trigger channel solenoid
286 326
                 pulse_trigger_out(ch, mem_data()->ch_timings[ch]);
287 327
 
@@ -290,6 +330,8 @@ void sequence_run(void) {
290 330
                     pulse_trigger_led(ch, mem_data()->ch_timings[ch]);
291 331
                     pulse_trigger_led(ch + 4, mem_data()->ch_timings[ch]);
292 332
                 }
333
+
334
+
293 335
             }
294 336
         }
295 337
 

+ 53
- 11
src/ui.c View File

@@ -1,7 +1,7 @@
1 1
 /*
2 2
  * ui.c
3 3
  *
4
- * Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
4
+ * Copyright (c) 2024 - 2025 Thomas Buck (thomas@xythobuz.de)
5 5
  *
6 6
  * This program is free software: you can redistribute it and/or modify
7 7
  * it under the terms of the GNU General Public License as published by
@@ -39,6 +39,8 @@ enum ui_settings {
39 39
 
40 40
     // loop station
41 41
     SETTING_SPEED,
42
+    SETTING_BANK_SELECT,
43
+    SETTING_BANK_COPY,
42 44
 
43 45
     // drum machine
44 46
     SETTING_BPM,
@@ -57,25 +59,25 @@ static bool allowed_settings[MACHINE_NUM_MODES][SETTING_NUM_MODES] = {
57 59
     // MODE_LOOPSTATION
58 60
     {
59 61
         true, // SETTING_MODE
60
-        true,
61
-        false, false, false, false,
62
-        false, false,
62
+        true, true, true, // SETTING_SPEED, SETTING_BANK_SELECT, SETTING_BANK_COPY
63
+        false, false, false, false, // SETTING_BPM, SETTING_LENGTH, SETTING_BANK, SETTING_CHANNEL
64
+        false, false, // SETTING_CH_RX, SETTING_CH_TX
63 65
     },
64 66
 
65 67
     // MODE_DRUMMACHINE
66 68
     {
67 69
         true, // SETTING_MODE
68
-        false,
69
-        true, true, true, true,
70
-        false, false,
70
+        false, false, false, // SETTING_SPEED, SETTING_BANK_SELECT, SETTING_BANK_COPY
71
+        true, true, true, true, // SETTING_BPM, SETTING_LENGTH, SETTING_BANK, SETTING_CHANNEL
72
+        false, false, // SETTING_CH_RX, SETTING_CH_TX
71 73
     },
72 74
 
73 75
     // MODE_MIDI
74 76
     {
75 77
         true, // SETTING_MODE
76
-        false,
77
-        false, false, false, false,
78
-        true, true,
78
+        false, false, false, // SETTING_SPEED, SETTING_BANK_SELECT, SETTING_BANK_COPY
79
+        false, false, false, false, // SETTING_BPM, SETTING_LENGTH, SETTING_BANK, SETTING_CHANNEL
80
+        true, true, // SETTING_CH_RX, SETTING_CH_TX
79 81
     },
80 82
 };
81 83
 
@@ -85,6 +87,7 @@ static uint32_t last_bat_fetch = 0;
85 87
 static float last_voltage = 0.0f;
86 88
 static float last_percentage = 0.0f;
87 89
 static uint8_t midi_rx = 0, midi_tx = 0;
90
+static uint32_t bank_select_num = 0, bank_copy_num = 0;
88 91
 
89 92
 enum machine_modes ui_get_machinemode(void) {
90 93
     return machine_mode;
@@ -153,6 +156,18 @@ void ui_redraw(void) {
153 156
             break;
154 157
         }
155 158
 
159
+        case SETTING_BANK_SELECT: {
160
+            snprintf(mode, sizeof(mode) - 1, "Sel-Bnk:");
161
+            snprintf(val, sizeof(val) - 1, "%"PRIu32, bank_select_num);
162
+            break;
163
+        }
164
+
165
+        case SETTING_BANK_COPY: {
166
+            snprintf(mode, sizeof(mode) - 1, "Cpy-Bnk:");
167
+            snprintf(val, sizeof(val) - 1, "%"PRIu32, bank_copy_num);
168
+            break;
169
+        }
170
+
156 171
         case SETTING_CH_RX: {
157 172
             snprintf(mode, sizeof(mode) - 1, "Rx-Ch:");
158 173
             snprintf(val, sizeof(val) - 1, "%"PRIu8, midi_rx + 1);
@@ -187,11 +202,25 @@ static void ui_buttons(enum buttons btn, bool val) {
187 202
     switch (btn) {
188 203
         case BTN_CLICK: {
189 204
             if (val) {
205
+                enum ui_settings prev_setting = ui_setting;
206
+
190 207
                 // only allow settings for this mode
191 208
                 do {
192 209
                     ui_setting = (ui_setting + 1) % SETTING_NUM_MODES;
193 210
                 } while (!allowed_settings[machine_mode][ui_setting]);
194 211
 
212
+                if (prev_setting == SETTING_BANK_COPY) {
213
+                    sequence_copy_bank(bank_copy_num);
214
+                } else if (prev_setting == SETTING_BANK_SELECT) {
215
+                    sequence_set_bank(bank_select_num);
216
+                }
217
+
218
+                if (ui_setting == SETTING_BANK_COPY) {
219
+                    bank_copy_num = sequence_get_bank();
220
+                } else if (ui_setting == SETTING_BANK_SELECT) {
221
+                    bank_select_num = sequence_get_bank();
222
+                }
223
+
195 224
                 ui_redraw();
196 225
             }
197 226
             break;
@@ -212,7 +241,7 @@ static void ui_buttons(enum buttons btn, bool val) {
212 241
                 }
213 242
 
214 243
                 case MODE_MIDI: {
215
-                    if (val) {
244
+                    if (val && (btn != BTN_CLEAR)) {
216 245
                         usb_midi_tx(midi_tx, btn - BTN_A, 0x7F);
217 246
                         pulse_trigger_led(btn - BTN_A, mem_data()->ch_timings[0]);
218 247
                     }
@@ -297,6 +326,19 @@ void ui_encoder(int32_t val) {
297 326
             break;
298 327
         }
299 328
 
329
+        case SETTING_BANK_SELECT: {
330
+            int32_t tmp = bank_select_num + val;
331
+            KEEP_IN_RANGE(tmp, 0, (int32_t)sequence_get_max_banks());
332
+            break;
333
+        }
334
+
335
+        case SETTING_BANK_COPY: {
336
+            int32_t tmp = bank_copy_num + val;
337
+            KEEP_IN_RANGE(tmp, 0, (int32_t)sequence_get_max_banks());
338
+            break;
339
+            break;
340
+        }
341
+
300 342
         case SETTING_CH_RX: {
301 343
             int32_t tmp = midi_rx + val;
302 344
             KEEP_IN_RANGE(tmp, 0, MIDI_MAX_CH);

+ 19
- 2
src/usb_cdc.c View File

@@ -1,7 +1,7 @@
1 1
 /*
2 2
  * Extended from TinyUSB example code.
3 3
  *
4
- * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
4
+ * Copyright (c) 2022 - 2025 Thomas Buck (thomas@xythobuz.de)
5 5
  *
6 6
  * The MIT License (MIT)
7 7
  *
@@ -76,9 +76,14 @@ static void cdc_task(void) {
76 76
         char buf[cdc_buf_len + 1];
77 77
         uint32_t count = tud_cdc_read(buf, cdc_buf_len);
78 78
 
79
+#ifdef ENTER_BOOTLOADER_MAGIC
80
+        // TODO support magic byte in other places instead of offset 0?
79 81
         if ((count >= 1) && (buf[0] == ENTER_BOOTLOADER_MAGIC)) {
80 82
             reset_to_bootloader();
81
-        } else if (reroute_cdc_debug) {
83
+        } else
84
+#endif // ENTER_BOOTLOADER_MAGIC
85
+
86
+        if (reroute_cdc_debug) {
82 87
             debug_handle_input((const uint8_t *)buf, count);
83 88
         } else {
84 89
             cnsl_handle_input((const uint8_t *)buf, count);
@@ -108,6 +113,18 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
108 113
     last_dtr = dtr;
109 114
 }
110 115
 
116
+#ifdef ENTER_BOOTLOADER_BAUD
117
+
118
+// Invoked when line coding is change via SET_LINE_CODING
119
+void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding) {
120
+    (void) itf;
121
+    if (p_line_coding->bit_rate == ENTER_BOOTLOADER_BAUD) {
122
+        reset_to_bootloader();
123
+    }
124
+}
125
+
126
+#endif // ENTER_BOOTLOADER_BAUD
127
+
111 128
 // invoked when CDC interface received data from host
112 129
 void tud_cdc_rx_cb(uint8_t itf) {
113 130
     (void) itf;

Loading…
Cancel
Save