Просмотр исходного кода

implement missing loop station features.

update pico-sdk.
other small improvements.
mark as release 1.0.
Thomas B 6 дней назад
Родитель
Сommit
ffb9c9f4d6
12 измененных файлов: 180 добавлений и 57 удалений
  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 Просмотреть файл

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

+ 6
- 1
docs/src/drummachine.md Просмотреть файл

1
 # Drum Machine
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 Просмотреть файл

17
 Revision 1 and 2 are missing diodes on the button matrix.
17
 Revision 1 and 2 are missing diodes on the button matrix.
18
 So when holding down three or more buttons simultaneously you get ghosting on other buttons.
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
 ## Bill of Materials
24
 ## Bill of Materials
21
 
25
 
22
 These are all the parts required to assemble the V2 PCB.
26
 These are all the parts required to assemble the V2 PCB.

+ 5
- 5
include/config.h Просмотреть файл

1
 /*
1
 /*
2
  * config.h
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
  * This program is free software: you can redistribute it and/or modify
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
7
  * it under the terms of the GNU General Public License as published by
19
 #ifndef __CONFIG_H__
19
 #ifndef __CONFIG_H__
20
 #define __CONFIG_H__
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
 #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
27
 #define FLASH_LOCK_TIMEOUT_MS 500
28
 #define CH_GPIO_DEFAULT_MS 42
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
 //#define DISABLE_CDC_DTR_CHECK
33
 //#define DISABLE_CDC_DTR_CHECK
34
 #define DEBOUNCE_DELAY_MS 5
34
 #define DEBOUNCE_DELAY_MS 5

+ 2
- 1
include/sequence.h Просмотреть файл

1
 /*
1
 /*
2
  * sequence.h
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
  * This program is free software: you can redistribute it and/or modify
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
7
  * it under the terms of the GNU General Public License as published by
46
 uint32_t sequence_get_beats(void);
46
 uint32_t sequence_get_beats(void);
47
 
47
 
48
 void sequence_set_bank(uint32_t new_bank);
48
 void sequence_set_bank(uint32_t new_bank);
49
+void sequence_copy_bank(uint32_t new_bank);
49
 uint32_t sequence_get_bank(void);
50
 uint32_t sequence_get_bank(void);
50
 
51
 
51
 void sequence_set_channel(uint32_t new_channel);
52
 void sequence_set_channel(uint32_t new_channel);

+ 1
- 1
pico-sdk

1
-Subproject commit 6a7db34ff63345a7badec79ebea3aaef1712f374
1
+Subproject commit bddd20f928ce76142793bef434d4f75f4af6e433

+ 3
- 3
src/adc.c Просмотреть файл

1
 /*
1
 /*
2
  * adc.c
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
  * This program is free software: you can redistribute it and/or modify
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
7
  * it under the terms of the GNU General Public License as published by
55
 
55
 
56
 void bat_init(void) {
56
 void bat_init(void) {
57
     adc_init();
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
     filtered = bat_read();
60
     filtered = bat_read();
61
 }
61
 }
62
 
62
 

+ 8
- 4
src/lcd.c Просмотреть файл

1
 /*
1
 /*
2
  * lcd.c
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
  * This program is free software: you can redistribute it and/or modify
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
7
  * it under the terms of the GNU General Public License as published by
16
  * See <http://www.gnu.org/licenses/>.
16
  * See <http://www.gnu.org/licenses/>.
17
  */
17
  */
18
 
18
 
19
+#include <pico/version.h>
19
 #include <stdio.h>
20
 #include <stdio.h>
20
 #include <string.h>
21
 #include <string.h>
21
 
22
 
199
     char hw_id_str[42] = {0};
200
     char hw_id_str[42] = {0};
200
     if (hw_type == HW_PROTOTYPE) {
201
     if (hw_type == HW_PROTOTYPE) {
201
         snprintf(hw_id_str, sizeof(hw_id_str) - 1,
202
         snprintf(hw_id_str, sizeof(hw_id_str) - 1,
202
-                 "HW Prototype");
203
+                 "LARS HW: Proto");
203
     } else if (hw_type == HW_V2) {
204
     } else if (hw_type == HW_V2) {
204
         snprintf(hw_id_str, sizeof(hw_id_str) - 1,
205
         snprintf(hw_id_str, sizeof(hw_id_str) - 1,
205
-                 "HW V2");
206
+                 "LARS HW: V2");
206
     } else {
207
     } else {
207
         snprintf(hw_id_str, sizeof(hw_id_str) - 1,
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
     ssd1306_draw_string(&disp, 0, FONT_HEIGHT * 2 + 1 + (FONT_HEIGHT + 1) * 3, 1,
211
     ssd1306_draw_string(&disp, 0, FONT_HEIGHT * 2 + 1 + (FONT_HEIGHT + 1) * 3, 1,
211
                         hw_id_str);
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
     ssd1306_show(&disp);
217
     ssd1306_show(&disp);
214
 }
218
 }

+ 16
- 9
src/mem.c Просмотреть файл

1
 /*
1
 /*
2
  * mem.c
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
  * This program is free software: you can redistribute it and/or modify
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
7
  * it under the terms of the GNU General Public License as published by
41
 static const struct mem_contents data_defaults = MEM_CONTENTS_INIT;
41
 static const struct mem_contents data_defaults = MEM_CONTENTS_INIT;
42
 static struct mem_contents data_ram = data_defaults;
42
 static struct mem_contents data_ram = data_defaults;
43
 static const uint8_t *data_flash = (const uint8_t *)(XIP_BASE + EEPROM_FLASH_OFFSET);
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
               "Config needs to fit inside a flash sector");
48
               "Config needs to fit inside a flash sector");
47
 
49
 
48
 static uint32_t calc_checksum(const struct mem_contents *data) {
50
 static uint32_t calc_checksum(const struct mem_contents *data) {
86
     const struct mem_contents *flash_ptr = (const struct mem_contents *)data_flash;
88
     const struct mem_contents *flash_ptr = (const struct mem_contents *)data_flash;
87
 
89
 
88
     if (flash_ptr->version == MEM_VERSION) {
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
         uint32_t checksum = calc_checksum(flash_ptr);
93
         uint32_t checksum = calc_checksum(flash_ptr);
92
         if (checksum != flash_ptr->checksum) {
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
         } else {
96
         } else {
95
             if (flash_ptr->data.ch_count != NUM_CHANNELS) {
97
             if (flash_ptr->data.ch_count != NUM_CHANNELS) {
96
                 debug("invalid channel count (0x%"PRIu32" != 0x%d)", flash_ptr->data.ch_count, NUM_CHANNELS);
98
                 debug("invalid channel count (0x%"PRIu32" != 0x%d)", flash_ptr->data.ch_count, NUM_CHANNELS);
97
             } else {
99
             } else {
98
-                debug("loading from flash (0x%08lX)", checksum);
100
+                debug("loading from flash (0x%08"PRIX32")", checksum);
99
                 data_ram = *flash_ptr;
101
                 data_ram = *flash_ptr;
100
             }
102
             }
101
         }
103
         }
102
     } else {
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
 static void mem_write_flash(void *param) {
109
 static void mem_write_flash(void *param) {
110
+    // we erase a 4K sector (FLASH_SECTOR_SIZE), but...
108
     flash_range_erase(EEPROM_FLASH_OFFSET, FLASH_SECTOR_SIZE);
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
 void mem_write(void) {
121
 void mem_write(void) {
119
 
126
 
120
     data_ram.checksum = calc_checksum(&data_ram);
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
     int r = flash_safe_execute(mem_write_flash, &data_ram, FLASH_LOCK_TIMEOUT_MS);
130
     int r = flash_safe_execute(mem_write_flash, &data_ram, FLASH_LOCK_TIMEOUT_MS);
124
     if (r != PICO_OK) {
131
     if (r != PICO_OK) {
125
         debug("error calling mem_write_flash: %d", r);
132
         debug("error calling mem_write_flash: %d", r);

+ 62
- 20
src/sequence.c Просмотреть файл

1
 /*
1
 /*
2
  * sequence.c
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
  * This program is free software: you can redistribute it and/or modify
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
7
  * it under the terms of the GNU General Public License as published by
38
 static uint32_t channel = 0;
38
 static uint32_t channel = 0;
39
 static bool button_held[NUM_BTNS] = {0};
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
 void sequence_init(void) {
53
 void sequence_init(void) {
44
     us_per_beat = 0;
54
     us_per_beat = 0;
47
     last_i = 0;
57
     last_i = 0;
48
     max_banks_currently = (beats + (NUM_BTNS - 1)) / NUM_BTNS;
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
     bank = (b < max_banks_currently) ? b : 0;
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
 uint32_t sequence_get_bank(void) {
109
 uint32_t sequence_get_bank(void) {
89
     return bank;
110
     return bank;
90
 }
111
 }
101
     return us_per_beat;
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
     if (beat < MAX_BEATS) {
126
     if (beat < MAX_BEATS) {
106
         if (value) {
127
         if (value) {
107
-            sequence[beat] |= ch;
128
+            sequence[sel_bank][beat] |= ch;
108
         } else {
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
     if (beat < MAX_BEATS) {
136
     if (beat < MAX_BEATS) {
116
-        return (sequence[beat] & ch) != 0;
137
+        return (sequence[sel_bank][beat] & ch) != 0;
117
     }
138
     }
118
     return false;
139
     return false;
119
 }
140
 }
177
             }
198
             }
178
 
199
 
179
             if (button_held[BTN_H]) {
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
                 sequence_init();
202
                 sequence_init();
183
             } else if (button_held[BTN_D]) {
203
             } else if (button_held[BTN_D]) {
184
                 // left REC: clear the current loop, including the length
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
             } else if (button_held[BTN_E] || button_held[BTN_F] || button_held[BTN_G]) {
209
             } else if (button_held[BTN_E] || button_held[BTN_F] || button_held[BTN_G]) {
187
                 // channel mute buttons: clear only this channel in the current loop
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
             } else {
218
             } else {
190
                 // on its own: clear all channels of the current loop, keeping the set length
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
             ui_redraw();
225
             ui_redraw();
204
     if ((button_held[BTN_D] || button_held[BTN_H]) && val) {
235
     if ((button_held[BTN_D] || button_held[BTN_H]) && val) {
205
         switch (btn) {
236
         switch (btn) {
206
             case BTN_A: {
237
             case BTN_A: {
207
-                sequence_set(last_i, CH_KICK, true);
238
+                sequence_set(last_i, CH_KICK, true, bank);
208
                 break;
239
                 break;
209
             }
240
             }
210
 
241
 
211
             case BTN_B: {
242
             case BTN_B: {
212
-                sequence_set(last_i, CH_SNARE, true);
243
+                sequence_set(last_i, CH_SNARE, true, bank);
213
                 break;
244
                 break;
214
             }
245
             }
215
 
246
 
216
             case BTN_C: {
247
             case BTN_C: {
217
-                sequence_set(last_i, CH_HIHAT, true);
248
+                sequence_set(last_i, CH_HIHAT, true, bank);
218
                 break;
249
                 break;
219
             }
250
             }
220
 
251
 
236
         case BTN_G:
267
         case BTN_G:
237
         case BTN_H: {
268
         case BTN_H: {
238
             uint32_t beat = (btn - BTN_A) + bank * LED_COUNT;
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
             break;
280
             break;
242
         }
281
         }
243
 
282
 
281
                 continue;
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
                 // trigger channel solenoid
325
                 // trigger channel solenoid
286
                 pulse_trigger_out(ch, mem_data()->ch_timings[ch]);
326
                 pulse_trigger_out(ch, mem_data()->ch_timings[ch]);
287
 
327
 
290
                     pulse_trigger_led(ch, mem_data()->ch_timings[ch]);
330
                     pulse_trigger_led(ch, mem_data()->ch_timings[ch]);
291
                     pulse_trigger_led(ch + 4, mem_data()->ch_timings[ch]);
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 Просмотреть файл

1
 /*
1
 /*
2
  * ui.c
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
  * This program is free software: you can redistribute it and/or modify
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
7
  * it under the terms of the GNU General Public License as published by
39
 
39
 
40
     // loop station
40
     // loop station
41
     SETTING_SPEED,
41
     SETTING_SPEED,
42
+    SETTING_BANK_SELECT,
43
+    SETTING_BANK_COPY,
42
 
44
 
43
     // drum machine
45
     // drum machine
44
     SETTING_BPM,
46
     SETTING_BPM,
57
     // MODE_LOOPSTATION
59
     // MODE_LOOPSTATION
58
     {
60
     {
59
         true, // SETTING_MODE
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
     // MODE_DRUMMACHINE
67
     // MODE_DRUMMACHINE
66
     {
68
     {
67
         true, // SETTING_MODE
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
     // MODE_MIDI
75
     // MODE_MIDI
74
     {
76
     {
75
         true, // SETTING_MODE
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
 static float last_voltage = 0.0f;
87
 static float last_voltage = 0.0f;
86
 static float last_percentage = 0.0f;
88
 static float last_percentage = 0.0f;
87
 static uint8_t midi_rx = 0, midi_tx = 0;
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
 enum machine_modes ui_get_machinemode(void) {
92
 enum machine_modes ui_get_machinemode(void) {
90
     return machine_mode;
93
     return machine_mode;
153
             break;
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
         case SETTING_CH_RX: {
171
         case SETTING_CH_RX: {
157
             snprintf(mode, sizeof(mode) - 1, "Rx-Ch:");
172
             snprintf(mode, sizeof(mode) - 1, "Rx-Ch:");
158
             snprintf(val, sizeof(val) - 1, "%"PRIu8, midi_rx + 1);
173
             snprintf(val, sizeof(val) - 1, "%"PRIu8, midi_rx + 1);
187
     switch (btn) {
202
     switch (btn) {
188
         case BTN_CLICK: {
203
         case BTN_CLICK: {
189
             if (val) {
204
             if (val) {
205
+                enum ui_settings prev_setting = ui_setting;
206
+
190
                 // only allow settings for this mode
207
                 // only allow settings for this mode
191
                 do {
208
                 do {
192
                     ui_setting = (ui_setting + 1) % SETTING_NUM_MODES;
209
                     ui_setting = (ui_setting + 1) % SETTING_NUM_MODES;
193
                 } while (!allowed_settings[machine_mode][ui_setting]);
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
                 ui_redraw();
224
                 ui_redraw();
196
             }
225
             }
197
             break;
226
             break;
212
                 }
241
                 }
213
 
242
 
214
                 case MODE_MIDI: {
243
                 case MODE_MIDI: {
215
-                    if (val) {
244
+                    if (val && (btn != BTN_CLEAR)) {
216
                         usb_midi_tx(midi_tx, btn - BTN_A, 0x7F);
245
                         usb_midi_tx(midi_tx, btn - BTN_A, 0x7F);
217
                         pulse_trigger_led(btn - BTN_A, mem_data()->ch_timings[0]);
246
                         pulse_trigger_led(btn - BTN_A, mem_data()->ch_timings[0]);
218
                     }
247
                     }
297
             break;
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
         case SETTING_CH_RX: {
342
         case SETTING_CH_RX: {
301
             int32_t tmp = midi_rx + val;
343
             int32_t tmp = midi_rx + val;
302
             KEEP_IN_RANGE(tmp, 0, MIDI_MAX_CH);
344
             KEEP_IN_RANGE(tmp, 0, MIDI_MAX_CH);

+ 19
- 2
src/usb_cdc.c Просмотреть файл

1
 /*
1
 /*
2
  * Extended from TinyUSB example code.
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
  * The MIT License (MIT)
6
  * The MIT License (MIT)
7
  *
7
  *
76
         char buf[cdc_buf_len + 1];
76
         char buf[cdc_buf_len + 1];
77
         uint32_t count = tud_cdc_read(buf, cdc_buf_len);
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
         if ((count >= 1) && (buf[0] == ENTER_BOOTLOADER_MAGIC)) {
81
         if ((count >= 1) && (buf[0] == ENTER_BOOTLOADER_MAGIC)) {
80
             reset_to_bootloader();
82
             reset_to_bootloader();
81
-        } else if (reroute_cdc_debug) {
83
+        } else
84
+#endif // ENTER_BOOTLOADER_MAGIC
85
+
86
+        if (reroute_cdc_debug) {
82
             debug_handle_input((const uint8_t *)buf, count);
87
             debug_handle_input((const uint8_t *)buf, count);
83
         } else {
88
         } else {
84
             cnsl_handle_input((const uint8_t *)buf, count);
89
             cnsl_handle_input((const uint8_t *)buf, count);
108
     last_dtr = dtr;
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
 // invoked when CDC interface received data from host
128
 // invoked when CDC interface received data from host
112
 void tud_cdc_rx_cb(uint8_t itf) {
129
 void tud_cdc_rx_cb(uint8_t itf) {
113
     (void) itf;
130
     (void) itf;

Загрузка…
Отмена
Сохранить