Browse Source

add pwm led brightness control. fix led pins for prototype hardware.

Thomas B 3 months ago
parent
commit
dee2ae8cce
8 changed files with 113 additions and 32 deletions
  1. 6
    0
      docs/src/drummachine.md
  2. 1
    1
      include/config.h
  3. 6
    6
      include/led.h
  4. 63
    12
      src/led.c
  5. 3
    3
      src/main.c
  6. 5
    5
      src/pulse.c
  7. 27
    3
      src/sequence.c
  8. 2
    2
      src/ui.c

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

1
 # Drum Machine
1
 # Drum Machine
2
 
2
 
3
 The drum-machine runs through a sequence of a given length, set by the `Length` menu.
3
 The drum-machine runs through a sequence of a given length, set by the `Length` menu.
4
+
4
 The corresponding LED lights up at it's moment in the sequence.
5
 The corresponding LED lights up at it's moment in the sequence.
6
+The currently active "step" is fully bright while enabled ones are lit dimly.
7
+
5
 Change the speed the sequence is running at with the `BPM` setting.
8
 Change the speed the sequence is running at with the `BPM` setting.
9
+
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.
10
 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.
11
+
7
 Select the currently edited output channel with the `Channel` menu.
12
 Select the currently edited output channel with the `Channel` menu.
13
+
8
 Use the CLEAR button below the encoder to zero-out the currently selected channel.
14
 Use the CLEAR button below the encoder to zero-out the currently selected channel.

+ 1
- 1
include/config.h View File

20
 #define __CONFIG_H__
20
 #define __CONFIG_H__
21
 
21
 
22
 #define VERSION_MAJOR 1
22
 #define VERSION_MAJOR 1
23
-#define VERSION_MINOR 0
23
+#define VERSION_MINOR 1
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

+ 6
- 6
include/led.h View File

1
 /*
1
 /*
2
  * led.h
2
  * led.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
22
 #include <stdint.h>
22
 #include <stdint.h>
23
 #include <stdbool.h>
23
 #include <stdbool.h>
24
 
24
 
25
-#if 0
26
-#define LED_COUNT 4
27
-#else
28
-#define LED_COUNT 8
29
-#endif
25
+#define MAX_LED_COUNT 8
30
 
26
 
31
 void led_init(void);
27
 void led_init(void);
28
+uint8_t led_count(void);
29
+
32
 void led_set(uint32_t i, bool v);
30
 void led_set(uint32_t i, bool v);
31
+void led_dim(uint32_t i, uint8_t v);
32
+
33
 void ch_set(uint32_t i, bool v);
33
 void ch_set(uint32_t i, bool v);
34
 
34
 
35
 #endif // __LED_H__
35
 #endif // __LED_H__

+ 63
- 12
src/led.c View File

1
 /*
1
 /*
2
  * led.c
2
  * led.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 <stdint.h>
19
 #include <stdio.h>
20
 #include <stdio.h>
20
 #include "pico/stdlib.h"
21
 #include "pico/stdlib.h"
22
+#include "hardware/pwm.h"
21
 
23
 
22
-#include "sequence.h"
23
 #include "log.h"
24
 #include "log.h"
25
+#include "main.h"
26
+#include "sequence.h"
24
 #include "led.h"
27
 #include "led.h"
25
 
28
 
26
-#if 0
27
-static const uint led_gpio_num[LED_COUNT] = {
29
+/*
30
+ * Beware:
31
+ * PWM driving the LED outputs assumes that the pins are not on
32
+ * shared PWM slices / channels!
33
+ * For RP2040: all GPIO numbers < 16 are safe
34
+ *
35
+ * Unfortunately the output channels on 26 and 27 are in conflict
36
+ * with the LEDs on pins 10 and 11, in both hardware revisions.
37
+ * So we only use PWM for the LEDs.
38
+ */
39
+
40
+static const uint led_gpio_num_proto[MAX_LED_COUNT] = {
28
     10, 11, 13, 15,
41
     10, 11, 13, 15,
29
 };
42
 };
30
-#else
31
-static const uint led_gpio_num[LED_COUNT] = {
43
+
44
+static const uint led_gpio_num_v2[MAX_LED_COUNT] = {
32
     6, 7, 8, 9, 10, 11, 12, 13,
45
     6, 7, 8, 9, 10, 11, 12, 13,
33
 };
46
 };
34
-#endif
35
 
47
 
36
 static const uint ch_gpio_num[NUM_CHANNELS] = {
48
 static const uint ch_gpio_num[NUM_CHANNELS] = {
37
     22, 26, 27,
49
     22, 26, 27,
38
 };
50
 };
39
 
51
 
52
+uint8_t led_count(void) {
53
+    if (hw_type == HW_PROTOTYPE) {
54
+        return 4;
55
+    }
56
+    return MAX_LED_COUNT;
57
+}
58
+
40
 void led_init(void) {
59
 void led_init(void) {
41
-    for (uint i = 0; i < LED_COUNT; i++) {
42
-        gpio_init(led_gpio_num[i]);
43
-        gpio_set_dir(led_gpio_num[i], GPIO_OUT);
60
+    for (uint i = 0; i < led_count(); i++) {
61
+        uint p = 0xFF;
62
+        if (hw_type == HW_PROTOTYPE) {
63
+            p = led_gpio_num_proto[i];
64
+        } else if (hw_type == HW_V2) {
65
+            p = led_gpio_num_v2[i];
66
+        }
67
+        if (p >= 0xFF) {
68
+            continue;
69
+        }
70
+
71
+        gpio_init(p);
72
+        gpio_set_dir(p, GPIO_OUT);
73
+        gpio_set_function(p, GPIO_FUNC_PWM);
74
+
75
+        uint slice_num = pwm_gpio_to_slice_num(p);
76
+        pwm_set_wrap(slice_num, UINT8_MAX);
77
+        pwm_set_chan_level(slice_num, pwm_gpio_to_channel(p), 0);
78
+        pwm_set_enabled(slice_num, true);
44
     }
79
     }
80
+
45
     for (uint i = 0; i < NUM_CHANNELS; i++) {
81
     for (uint i = 0; i < NUM_CHANNELS; i++) {
46
         gpio_init(ch_gpio_num[i]);
82
         gpio_init(ch_gpio_num[i]);
47
         gpio_set_dir(ch_gpio_num[i], GPIO_OUT);
83
         gpio_set_dir(ch_gpio_num[i], GPIO_OUT);
49
 }
85
 }
50
 
86
 
51
 void led_set(uint32_t i, bool v) {
87
 void led_set(uint32_t i, bool v) {
52
-    i %= LED_COUNT;
53
-    gpio_put(led_gpio_num[i], v);
88
+    led_dim(i, v ? UINT8_MAX : 0);
89
+}
90
+
91
+void led_dim(uint32_t i, uint8_t v) {
92
+    i %= led_count();
93
+
94
+    uint p = 0xFF;
95
+    if (hw_type == HW_PROTOTYPE) {
96
+        p = led_gpio_num_proto[i];
97
+    } else if (hw_type == HW_V2) {
98
+        p = led_gpio_num_v2[i];
99
+    }
100
+    if (p >= 0xFF) {
101
+        return;
102
+    }
103
+
104
+    pwm_set_chan_level(pwm_gpio_to_slice_num(p), pwm_gpio_to_channel(p), v);
54
     //debug("led %"PRIu32" now %d", i, v);
105
     //debug("led %"PRIu32" now %d", i, v);
55
 }
106
 }
56
 
107
 

+ 3
- 3
src/main.c View File

136
         led_set(0, false);
136
         led_set(0, false);
137
     } else {
137
     } else {
138
         // show splash for a bit and animate LEDs
138
         // show splash for a bit and animate LEDs
139
-        for (uint i = 0; i < LED_COUNT; i++) {
139
+        for (uint i = 0; i < led_count(); i++) {
140
             watchdog_update();
140
             watchdog_update();
141
             usb_run();
141
             usb_run();
142
             cnsl_run();
142
             cnsl_run();
143
             led_set(i, true);
143
             led_set(i, true);
144
-            sleep_ms_wd(mem_data()->boot_anim_ms / LED_COUNT);
144
+            sleep_ms_wd(mem_data()->boot_anim_ms / led_count());
145
         }
145
         }
146
     }
146
     }
147
 
147
 
148
     // turn off LEDs at end of init
148
     // turn off LEDs at end of init
149
-    for (uint i = 0; i < LED_COUNT; i++) {
149
+    for (uint i = 0; i < led_count(); i++) {
150
         led_set(i, false);
150
         led_set(i, false);
151
     }
151
     }
152
 }
152
 }

+ 5
- 5
src/pulse.c View File

1
 /*
1
 /*
2
  * pulse.c
2
  * pulse.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
25
 #include "pulse.h"
25
 #include "pulse.h"
26
 
26
 
27
 static uint32_t out_time[NUM_CHANNELS] = {0};
27
 static uint32_t out_time[NUM_CHANNELS] = {0};
28
-static uint32_t led_time[LED_COUNT] = {0};
28
+static uint32_t led_time[MAX_LED_COUNT] = {0};
29
 
29
 
30
 static void pulse_trigger(uint32_t i, uint32_t t_ms, bool out) {
30
 static void pulse_trigger(uint32_t i, uint32_t t_ms, bool out) {
31
     uint32_t off_t = t_ms + to_ms_since_boot(get_absolute_time());
31
     uint32_t off_t = t_ms + to_ms_since_boot(get_absolute_time());
39
         }
39
         }
40
     } else {
40
     } else {
41
         led_set(i, true);
41
         led_set(i, true);
42
-        if (led_time[i % LED_COUNT] == 0) {
43
-            led_time[i % LED_COUNT] = off_t;
42
+        if (led_time[i % MAX_LED_COUNT] == 0) {
43
+            led_time[i % MAX_LED_COUNT] = off_t;
44
         } else {
44
         } else {
45
             debug("skip retrigger led %"PRIu32, i);
45
             debug("skip retrigger led %"PRIu32, i);
46
         }
46
         }
67
         }
67
         }
68
     }
68
     }
69
 
69
 
70
-    for (uint i = 0; i < LED_COUNT; i++) {
70
+    for (uint i = 0; i < MAX_LED_COUNT; i++) {
71
         if (led_time[i] != 0) {
71
         if (led_time[i] != 0) {
72
             if (led_time[i] <= now) {
72
             if (led_time[i] <= now) {
73
                 led_set(i, false);
73
                 led_set(i, false);

+ 27
- 3
src/sequence.c View File

37
 static uint32_t channel = 0;
37
 static uint32_t channel = 0;
38
 static bool button_held[NUM_BTNS] = {0};
38
 static bool button_held[NUM_BTNS] = {0};
39
 
39
 
40
+// LED PWM brightness
41
+enum led_dimmer_types {
42
+    DIM_HIGH = 255, // currently active beat
43
+    DIM_MID = 80, // beat active in current channel
44
+    DIM_LOW = 16, // beat active in another channel
45
+    DIM_OFF = 0, // beat inactive
46
+};
47
+
40
 /*
48
 /*
41
  * 'bank' has a dual meaning here. this is a bit fugly.
49
  * 'bank' has a dual meaning here. this is a bit fugly.
42
  *
50
  *
265
         case BTN_F:
273
         case BTN_F:
266
         case BTN_G:
274
         case BTN_G:
267
         case BTN_H: {
275
         case BTN_H: {
268
-            uint32_t beat = (btn - BTN_A) + bank * LED_COUNT;
276
+            uint32_t beat = (btn - BTN_A) + bank * led_count();
269
             bool val = !sequence_get(beat, 1 << channel, 0);
277
             bool val = !sequence_get(beat, 1 << channel, 0);
270
             sequence_set(beat, 1 << channel, val, 0);
278
             sequence_set(beat, 1 << channel, val, 0);
271
             break;
279
             break;
309
         if (i >= beats) i = 0;
317
         if (i >= beats) i = 0;
310
 
318
 
311
         if (ui_get_machinemode() == MODE_DRUMMACHINE) {
319
         if (ui_get_machinemode() == MODE_DRUMMACHINE) {
312
-            led_set(last_i, false);
313
-            led_set(i, true);
320
+            // turn off "previous" led, or dim it, if it is set in another bank
321
+            enum led_dimmer_types dim = DIM_OFF;
322
+            for (uint b = 0; b < (MAX_BEATS / led_count()); b++) {
323
+                uint n = b * led_count();
324
+                for (uint ch = 0; ch < NUM_CHANNELS; ch++) {
325
+                    if (sequence_get(n + (last_i % led_count()), (1 << ch), 0)) {
326
+                        if (ch == channel) {
327
+                            dim = DIM_MID;
328
+                            break;
329
+                        }
330
+                        dim = DIM_LOW;
331
+                    }
332
+                }
333
+            }
334
+            led_dim(last_i, dim);
335
+
336
+            // always turn on "currrent" led
337
+            led_dim(i, DIM_HIGH);
314
         }
338
         }
315
 
339
 
316
         for (uint ch = 0; ch < NUM_CHANNELS; ch++) {
340
         for (uint ch = 0; ch < NUM_CHANNELS; ch++) {

+ 2
- 2
src/ui.c View File

283
                 sequence_init();
283
                 sequence_init();
284
 
284
 
285
                 // turn off all LEDs
285
                 // turn off all LEDs
286
-                for (uint i = 0; i < LED_COUNT; i++) {
286
+                for (uint i = 0; i < led_count(); i++) {
287
                     led_set(i, false);
287
                     led_set(i, false);
288
                 }
288
                 }
289
 
289
 
294
                     led_set(NUM_CHANNELS, true);
294
                     led_set(NUM_CHANNELS, true);
295
                     led_set(NUM_CHANNELS + 4, true);
295
                     led_set(NUM_CHANNELS + 4, true);
296
                 } else if (machine_mode == MODE_DRUMMACHINE) {
296
                 } else if (machine_mode == MODE_DRUMMACHINE) {
297
-                    sequence_set_beats(LED_COUNT);
297
+                    sequence_set_beats(led_count());
298
                     sequence_set_bpm(120);
298
                     sequence_set_bpm(120);
299
                     sequence_set_bank(0);
299
                     sequence_set_bank(0);
300
                     sequence_set_channel(0);
300
                     sequence_set_channel(0);

Loading…
Cancel
Save