Browse Source

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

Thomas B 6 days 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,8 +1,14 @@
1 1
 # Drum Machine
2 2
 
3 3
 The drum-machine runs through a sequence of a given length, set by the `Length` menu.
4
+
4 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 8
 Change the speed the sequence is running at with the `BPM` setting.
9
+
6 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 12
 Select the currently edited output channel with the `Channel` menu.
13
+
8 14
 Use the CLEAR button below the encoder to zero-out the currently selected channel.

+ 1
- 1
include/config.h View File

@@ -20,7 +20,7 @@
20 20
 #define __CONFIG_H__
21 21
 
22 22
 #define VERSION_MAJOR 1
23
-#define VERSION_MINOR 0
23
+#define VERSION_MINOR 1
24 24
 
25 25
 #define WATCHDOG_PERIOD_MS 100
26 26
 #define LOGO_INIT_MS 1000

+ 6
- 6
include/led.h View File

@@ -1,7 +1,7 @@
1 1
 /*
2 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 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
@@ -22,14 +22,14 @@
22 22
 #include <stdint.h>
23 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 27
 void led_init(void);
28
+uint8_t led_count(void);
29
+
32 30
 void led_set(uint32_t i, bool v);
31
+void led_dim(uint32_t i, uint8_t v);
32
+
33 33
 void ch_set(uint32_t i, bool v);
34 34
 
35 35
 #endif // __LED_H__

+ 63
- 12
src/led.c View File

@@ -1,7 +1,7 @@
1 1
 /*
2 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 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,32 +16,68 @@
16 16
  * See <http://www.gnu.org/licenses/>.
17 17
  */
18 18
 
19
+#include <stdint.h>
19 20
 #include <stdio.h>
20 21
 #include "pico/stdlib.h"
22
+#include "hardware/pwm.h"
21 23
 
22
-#include "sequence.h"
23 24
 #include "log.h"
25
+#include "main.h"
26
+#include "sequence.h"
24 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 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 45
     6, 7, 8, 9, 10, 11, 12, 13,
33 46
 };
34
-#endif
35 47
 
36 48
 static const uint ch_gpio_num[NUM_CHANNELS] = {
37 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 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 81
     for (uint i = 0; i < NUM_CHANNELS; i++) {
46 82
         gpio_init(ch_gpio_num[i]);
47 83
         gpio_set_dir(ch_gpio_num[i], GPIO_OUT);
@@ -49,8 +85,23 @@ void led_init(void) {
49 85
 }
50 86
 
51 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 105
     //debug("led %"PRIu32" now %d", i, v);
55 106
 }
56 107
 

+ 3
- 3
src/main.c View File

@@ -136,17 +136,17 @@ static void animate_boot_combos(void) {
136 136
         led_set(0, false);
137 137
     } else {
138 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 140
             watchdog_update();
141 141
             usb_run();
142 142
             cnsl_run();
143 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 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 150
         led_set(i, false);
151 151
     }
152 152
 }

+ 5
- 5
src/pulse.c View File

@@ -1,7 +1,7 @@
1 1
 /*
2 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 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
@@ -25,7 +25,7 @@
25 25
 #include "pulse.h"
26 26
 
27 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 30
 static void pulse_trigger(uint32_t i, uint32_t t_ms, bool out) {
31 31
     uint32_t off_t = t_ms + to_ms_since_boot(get_absolute_time());
@@ -39,8 +39,8 @@ static void pulse_trigger(uint32_t i, uint32_t t_ms, bool out) {
39 39
         }
40 40
     } else {
41 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 44
         } else {
45 45
             debug("skip retrigger led %"PRIu32, i);
46 46
         }
@@ -67,7 +67,7 @@ void pulse_run(void) {
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 71
         if (led_time[i] != 0) {
72 72
             if (led_time[i] <= now) {
73 73
                 led_set(i, false);

+ 27
- 3
src/sequence.c View File

@@ -37,6 +37,14 @@ static uint32_t max_banks_currently = 0;
37 37
 static uint32_t channel = 0;
38 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 49
  * 'bank' has a dual meaning here. this is a bit fugly.
42 50
  *
@@ -265,7 +273,7 @@ void sequence_handle_button_drummachine(enum buttons btn) {
265 273
         case BTN_F:
266 274
         case BTN_G:
267 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 277
             bool val = !sequence_get(beat, 1 << channel, 0);
270 278
             sequence_set(beat, 1 << channel, val, 0);
271 279
             break;
@@ -309,8 +317,24 @@ void sequence_run(void) {
309 317
         if (i >= beats) i = 0;
310 318
 
311 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 340
         for (uint ch = 0; ch < NUM_CHANNELS; ch++) {

+ 2
- 2
src/ui.c View File

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

Loading…
Cancel
Save