2 Commits

Author SHA1 Message Date
  Thomas Buck 1e0c69663e add firmware workflow for gh actions 1 month ago
  Thomas Buck 125e2b4763 print bat percent 1 month ago
10 changed files with 168 additions and 33 deletions
  1. 78
    0
      .github/workflows/cmake.yml
  2. 1
    0
      README.md
  3. 1
    0
      docs/src/introduction.md
  4. 1
    0
      include/adc.h
  5. 3
    0
      include/lcd.h
  6. 28
    1
      src/adc.c
  7. 42
    30
      src/lcd.c
  8. 7
    0
      src/main.c
  9. 1
    0
      src/sequence.c
  10. 6
    2
      src/ui.c

+ 78
- 0
.github/workflows/cmake.yml View File

1
+# https://github.com/raspberrypi/pico-examples/blob/master/.github/workflows/cmake.yml
2
+
3
+name: Firmware
4
+
5
+# build for each push and pull request
6
+on: [push, pull_request]
7
+
8
+env:
9
+  BUILD_TYPE: Release
10
+
11
+jobs:
12
+  build:
13
+    runs-on: ubuntu-latest
14
+
15
+    permissions:
16
+      contents: write
17
+
18
+    steps:
19
+      - name: Install dependencies
20
+        run: sudo apt-get install -y cxxtest build-essential gcc-arm-none-eabi mtools zip
21
+
22
+      - name: Checkout repo
23
+        uses: actions/checkout@v4
24
+        with:
25
+          path: repo
26
+          fetch-depth: 0
27
+
28
+      - name: Checkout repo submodules
29
+        working-directory: ${{github.workspace}}/repo
30
+        run: git submodule update --init
31
+
32
+      - name: Checkout pico-sdk submodules
33
+        working-directory: ${{github.workspace}}/repo/pico-sdk
34
+        run: git submodule update --init
35
+
36
+      - name: Create Build Environment
37
+        working-directory: ${{github.workspace}}/repo
38
+        run:  cmake -E make_directory ${{github.workspace}}/repo/build
39
+
40
+      - name: Configure CMake
41
+        shell: bash
42
+        working-directory: ${{github.workspace}}/repo/build
43
+        run: cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
44
+
45
+      - name: Get core count
46
+        id: core_count
47
+        run : cat /proc/cpuinfo  | grep processor | wc -l
48
+
49
+      - name: Build
50
+        working-directory: ${{github.workspace}}/repo/build
51
+        shell: bash
52
+        run: cmake --build . --config $BUILD_TYPE --parallel $(nproc)
53
+
54
+      - name: Upload a Build Artifact
55
+        uses: actions/upload-artifact@v4.0.0
56
+        with:
57
+          name: firmware.uf2
58
+          path: ${{github.workspace}}/repo/build/drumkit.uf2
59
+          if-no-files-found: error
60
+
61
+      - name: Upload a Build Artifact
62
+        uses: actions/upload-artifact@v4.0.0
63
+        with:
64
+          name: update.elf
65
+          path: ${{github.workspace}}/repo/build/drumkit.elf
66
+          if-no-files-found: error
67
+
68
+      - name: Archive release files
69
+        if: startsWith(github.ref, 'refs/tags/')
70
+        run: |
71
+          cd ${{github.workspace}}/repo/build
72
+          zip -r firmware drumkit.uf2 drumkit.elf
73
+
74
+      - name: Upload release files
75
+        if: startsWith(github.ref, 'refs/tags/')
76
+        uses: softprops/action-gh-release@v1
77
+        with:
78
+          files: ${{github.workspace}}/repo/build/firmware.zip

+ 1
- 0
README.md View File

3
 ![PCB](https://github.com/xythobuz/lars/actions/workflows/kicad.yml/badge.svg)
3
 ![PCB](https://github.com/xythobuz/lars/actions/workflows/kicad.yml/badge.svg)
4
 ![Docs](https://github.com/xythobuz/lars/actions/workflows/docs.yml/badge.svg)
4
 ![Docs](https://github.com/xythobuz/lars/actions/workflows/docs.yml/badge.svg)
5
 ![STLs](https://github.com/xythobuz/lars/actions/workflows/scad.yml/badge.svg)
5
 ![STLs](https://github.com/xythobuz/lars/actions/workflows/scad.yml/badge.svg)
6
+![Firmware](https://github.com/xythobuz/lars/actions/workflows/cmake.yml/badge.svg)
6
 
7
 
7
 This is a simple drum machine / loopstation.
8
 This is a simple drum machine / loopstation.
8
 It's made for three hand-wound solenoids mounted to a tambourine.
9
 It's made for three hand-wound solenoids mounted to a tambourine.

+ 1
- 0
docs/src/introduction.md View File

3
 ![PCB](https://github.com/xythobuz/lars/actions/workflows/kicad.yml/badge.svg)
3
 ![PCB](https://github.com/xythobuz/lars/actions/workflows/kicad.yml/badge.svg)
4
 ![Docs](https://github.com/xythobuz/lars/actions/workflows/docs.yml/badge.svg)
4
 ![Docs](https://github.com/xythobuz/lars/actions/workflows/docs.yml/badge.svg)
5
 ![STLs](https://github.com/xythobuz/lars/actions/workflows/scad.yml/badge.svg)
5
 ![STLs](https://github.com/xythobuz/lars/actions/workflows/scad.yml/badge.svg)
6
+![Firmware](https://github.com/xythobuz/lars/actions/workflows/cmake.yml/badge.svg)
6
 
7
 
7
 This is a simple drum machine / loopstation.
8
 This is a simple drum machine / loopstation.
8
 It's made for three hand-wound solenoids mounted to a tambourine.
9
 It's made for three hand-wound solenoids mounted to a tambourine.

+ 1
- 0
include/adc.h View File

21
 
21
 
22
 void bat_init(void);
22
 void bat_init(void);
23
 float bat_get(void);
23
 float bat_get(void);
24
+float bat_to_percent(float voltage);
24
 
25
 
25
 #endif // __ADC_H__
26
 #endif // __ADC_H__

+ 3
- 0
include/lcd.h View File

19
 #ifndef __LCD_H__
19
 #ifndef __LCD_H__
20
 #define __LCD_H__
20
 #define __LCD_H__
21
 
21
 
22
+#include <stdint.h>
23
+
22
 #define LCD_WIDTH 128
24
 #define LCD_WIDTH 128
23
 #define LCD_HEIGHT 64
25
 #define LCD_HEIGHT 64
24
 
26
 
25
 void lcd_init(void);
27
 void lcd_init(void);
26
 void lcd_draw(const char *mode, const char *val, const char *bat);
28
 void lcd_draw(const char *mode, const char *val, const char *bat);
27
 void lcd_draw_bye(void);
29
 void lcd_draw_bye(void);
30
+void lcd_draw_bitmap(uint8_t *data, int width, int height, int x_off, int y_off);
28
 
31
 
29
 void lcd_debug_buttons(void);
32
 void lcd_debug_buttons(void);
30
 
33
 

+ 28
- 1
src/adc.c View File

17
  */
17
  */
18
 
18
 
19
 #include <stdio.h>
19
 #include <stdio.h>
20
+#include <math.h>
20
 #include "pico/stdlib.h"
21
 #include "pico/stdlib.h"
21
 #include "hardware/adc.h"
22
 #include "hardware/adc.h"
22
 
23
 
23
 #include "adc.h"
24
 #include "adc.h"
24
 
25
 
26
+#define LIPO_USE_PERCENTAGE_CURVE
27
+
25
 #define ADC_NUM 2
28
 #define ADC_NUM 2
26
 #define ADC_PIN (26 + ADC_NUM)
29
 #define ADC_PIN (26 + ADC_NUM)
27
 
30
 
32
 #define BAT_R1 10000.0f
35
 #define BAT_R1 10000.0f
33
 #define BAT_R2 18000.0f
36
 #define BAT_R2 18000.0f
34
 
37
 
35
-#define FILTER_OLD 0.75f
38
+#define FILTER_OLD 0.95f
36
 #define FILTER_NEW (1.0f - FILTER_OLD)
39
 #define FILTER_NEW (1.0f - FILTER_OLD)
37
 
40
 
41
+#ifndef LIPO_USE_PERCENTAGE_CURVE
42
+static const float full_battery = 4.1f;
43
+static const float empty_battery = 3.2f;
44
+#endif // ! LIPO_USE_PERCENTAGE_CURVE
45
+
38
 static float filtered = 0.0f;
46
 static float filtered = 0.0f;
39
 
47
 
40
 static float bat_read(void) {
48
 static float bat_read(void) {
56
     filtered = (filtered * FILTER_OLD) + (bat_read() * FILTER_NEW);
64
     filtered = (filtered * FILTER_OLD) + (bat_read() * FILTER_NEW);
57
     return filtered;
65
     return filtered;
58
 }
66
 }
67
+
68
+float bat_to_percent(float voltage) {
69
+    float percentage = 0.0f;
70
+
71
+#ifdef LIPO_USE_PERCENTAGE_CURVE
72
+    /*
73
+     * Try to linearize the LiPo discharge curve.
74
+     * https://electronics.stackexchange.com/a/551667
75
+     *
76
+     * Seems to work relatively well, although
77
+     * "stopping" at 3.5V feels a bit high to me.
78
+     */
79
+    percentage = 123.0f - (123.0f / powf(1.0f + powf(voltage / 3.7f, 80.0f), 0.165f));
80
+#else // LIPO_USE_PERCENTAGE_CURVE
81
+    percentage = 100.0f * ((voltage - empty_battery) / (full_battery - empty_battery));
82
+#endif // LIPO_USE_PERCENTAGE_CURVE
83
+
84
+    return MIN(MAX(percentage, 0.0f), 100.0f);
85
+}

+ 42
- 30
src/lcd.c View File

16
  * See <http://www.gnu.org/licenses/>.
16
  * See <http://www.gnu.org/licenses/>.
17
  */
17
  */
18
 
18
 
19
-#include <stdio.h>
20
-#include <stdint.h>
21
-#include <string.h>
22
-#include "pico/stdlib.h"
23
 #include "hardware/i2c.h"
19
 #include "hardware/i2c.h"
24
 
20
 
25
 #include "ssd1306.h"
21
 #include "ssd1306.h"
26
 
22
 
27
 #include "buttons.h"
23
 #include "buttons.h"
28
-#include "logo.h"
29
 #include "main.h"
24
 #include "main.h"
30
 #include "lcd.h"
25
 #include "lcd.h"
31
 
26
 
37
 
32
 
38
 #define LCD_ADDR 0x3C
33
 #define LCD_ADDR 0x3C
39
 
34
 
40
-static ssd1306_t disp;
35
+static ssd1306_t disp = {0};
41
 static bool buttons[NUM_BTNS] = {0};
36
 static bool buttons[NUM_BTNS] = {0};
42
 static bool changed = true;
37
 static bool changed = true;
43
 
38
 
48
 
43
 
49
 void lcd_debug_buttons(void) {
44
 void lcd_debug_buttons(void) {
50
     buttons_callback(lcd_debug_buttons_callback);
45
     buttons_callback(lcd_debug_buttons_callback);
46
+
51
     while (1) {
47
     while (1) {
52
         buttons_run();
48
         buttons_run();
53
         handle_serial_input();
49
         handle_serial_input();
50
+
54
         if (changed) {
51
         if (changed) {
55
             changed = false;
52
             changed = false;
53
+
56
             ssd1306_clear(&disp);
54
             ssd1306_clear(&disp);
57
             ssd1306_draw_string(&disp, 0, 0, 3, "Buttons");
55
             ssd1306_draw_string(&disp, 0, 0, 3, "Buttons");
56
+
58
             for (uint i = 0; i < NUM_BTNS; i++) {
57
             for (uint i = 0; i < NUM_BTNS; i++) {
58
+                if ((hw_type == HW_PROTOTYPE) && (i >= BTN_D) && (i <= BTN_H)) {
59
+                    continue;
60
+                }
61
+
59
                 ssd1306_draw_char(&disp,
62
                 ssd1306_draw_char(&disp,
60
-                                    i * 12, LCD_HEIGHT - 20 - 16 - 1,
61
-                                    2, '0' + i);
63
+                                  i * 12, LCD_HEIGHT - 20 - 16 - 1,
64
+                                  2, '0' + i);
65
+
62
                 if (buttons[i]) {
66
                 if (buttons[i]) {
63
                     ssd1306_draw_square(&disp,
67
                     ssd1306_draw_square(&disp,
64
                                         i * 12, LCD_HEIGHT - 20 - 1,
68
                                         i * 12, LCD_HEIGHT - 20 - 1,
65
                                         10, 20);
69
                                         10, 20);
66
                 } else {
70
                 } else {
67
                     ssd1306_draw_empty_square(&disp,
71
                     ssd1306_draw_empty_square(&disp,
68
-                                                i * 12, LCD_HEIGHT - 20 - 1,
69
-                                                10, 20);
72
+                                              i * 12, LCD_HEIGHT - 20 - 1,
73
+                                              10, 20);
70
                 }
74
                 }
71
             }
75
             }
76
+
72
             ssd1306_show(&disp);
77
             ssd1306_show(&disp);
73
         }
78
         }
74
     }
79
     }
75
 }
80
 }
76
 
81
 
82
+void lcd_draw_bitmap(uint8_t *data, int width, int height, int x_off, int y_off) {
83
+    ssd1306_clear(&disp);
84
+
85
+    for (int y = 0; y < height; y++) {
86
+        for (int x = 0; x < width; x++) {
87
+            const uint pos = y * width + x;
88
+            const uint bit = 7 - (pos % 8);
89
+            if (data[pos / 8] & (1 << bit)) {
90
+                ssd1306_draw_pixel(&disp, x + x_off, y + y_off);
91
+            }
92
+        }
93
+    }
94
+
95
+    ssd1306_show(&disp);
96
+}
97
+
77
 void lcd_init(void) {
98
 void lcd_init(void) {
78
     if (hw_type == HW_PROTOTYPE) {
99
     if (hw_type == HW_PROTOTYPE) {
79
-        i2c_init(gpio_i2c_proto, 1000 * 1000);
100
+        i2c_init(gpio_i2c_proto, 2UL * 1000UL * 1000UL);
101
+
80
         for (uint i = 0; i < sizeof(gpio_num_proto) / sizeof(gpio_num_proto[0]); i++) {
102
         for (uint i = 0; i < sizeof(gpio_num_proto) / sizeof(gpio_num_proto[0]); i++) {
81
             gpio_set_function(gpio_num_proto[i], GPIO_FUNC_I2C);
103
             gpio_set_function(gpio_num_proto[i], GPIO_FUNC_I2C);
82
             gpio_pull_up(gpio_num_proto[i]);
104
             gpio_pull_up(gpio_num_proto[i]);
83
         }
105
         }
84
 
106
 
85
-        disp.external_vcc = false;
86
-        ssd1306_init(&disp, LCD_WIDTH, LCD_HEIGHT, LCD_ADDR, gpio_i2c_proto);
107
+        ssd1306_init(&disp,
108
+                     LCD_WIDTH, LCD_HEIGHT,
109
+                     LCD_ADDR, gpio_i2c_proto);
87
     } else if (hw_type == HW_V2) {
110
     } else if (hw_type == HW_V2) {
88
-        i2c_init(gpio_i2c_v2, 1000 * 1000);
111
+        i2c_init(gpio_i2c_v2, 2UL * 1000UL * 1000UL);
112
+
89
         for (uint i = 0; i < sizeof(gpio_num_v2) / sizeof(gpio_num_v2[0]); i++) {
113
         for (uint i = 0; i < sizeof(gpio_num_v2) / sizeof(gpio_num_v2[0]); i++) {
90
             gpio_set_function(gpio_num_v2[i], GPIO_FUNC_I2C);
114
             gpio_set_function(gpio_num_v2[i], GPIO_FUNC_I2C);
91
             gpio_pull_up(gpio_num_v2[i]);
115
             gpio_pull_up(gpio_num_v2[i]);
92
         }
116
         }
93
 
117
 
94
-        disp.external_vcc = false;
95
-        ssd1306_init(&disp, LCD_WIDTH, LCD_HEIGHT, LCD_ADDR, gpio_i2c_v2);
118
+        ssd1306_init(&disp,
119
+                     LCD_WIDTH, LCD_HEIGHT,
120
+                     LCD_ADDR, gpio_i2c_v2);
96
     }
121
     }
97
-
98
-    // show logo
99
-    ssd1306_clear(&disp);
100
-    for (uint y = 0; y < LOGO_HEIGHT; y++) {
101
-        for (uint x = 0; x < LOGO_WIDTH; x++) {
102
-            const uint pos = y * LOGO_WIDTH + x;
103
-            const uint bit = 7 - (pos % 8);
104
-            if (logo_data[pos / 8] & (1 << bit)) {
105
-                ssd1306_draw_pixel(&disp, x, y);
106
-            }
107
-        }
108
-    }
109
-    ssd1306_show(&disp);
110
 }
122
 }
111
 
123
 
112
 void lcd_draw(const char *mode, const char *val, const char *bat) {
124
 void lcd_draw(const char *mode, const char *val, const char *bat) {
113
     ssd1306_clear(&disp);
125
     ssd1306_clear(&disp);
114
     ssd1306_draw_string(&disp, 0, 0, 2, mode);
126
     ssd1306_draw_string(&disp, 0, 0, 2, mode);
115
     ssd1306_draw_string(&disp, 0, 20, 4, val);
127
     ssd1306_draw_string(&disp, 0, 20, 4, val);
116
-    ssd1306_draw_string(&disp, 0, LCD_HEIGHT - 1 - 10, 1, bat);
128
+    ssd1306_draw_string(&disp, 0, LCD_HEIGHT - 8, 1, bat);
117
     ssd1306_show(&disp);
129
     ssd1306_show(&disp);
118
 }
130
 }
119
 
131
 

+ 7
- 0
src/main.c View File

26
 #include "encoder.h"
26
 #include "encoder.h"
27
 #include "lcd.h"
27
 #include "lcd.h"
28
 #include "led.h"
28
 #include "led.h"
29
+#include "logo.h"
29
 #include "pulse.h"
30
 #include "pulse.h"
30
 #include "sequence.h"
31
 #include "sequence.h"
31
 #include "ui.h"
32
 #include "ui.h"
81
     lcd_init();
82
     lcd_init();
82
     led_init();
83
     led_init();
83
 
84
 
85
+    // show logo
86
+    lcd_draw_bitmap(logo_data,
87
+                    LOGO_WIDTH, LOGO_HEIGHT,
88
+                    0, 0);
89
+
84
     // read out button state for debug options
90
     // read out button state for debug options
85
     buttons_callback(debug_buttons_callback);
91
     buttons_callback(debug_buttons_callback);
86
     for (uint i = 0; i < (DEBOUNCE_DELAY_MS + 5); i++) {
92
     for (uint i = 0; i < (DEBOUNCE_DELAY_MS + 5); i++) {
89
         sleep_ms(1);
95
         sleep_ms(1);
90
     }
96
     }
91
 
97
 
98
+    // handle special button combos on boot
92
     if (debug_buttons[BTN_REC] && debug_buttons[BTN_CLICK]) {
99
     if (debug_buttons[BTN_REC] && debug_buttons[BTN_CLICK]) {
93
         lcd_debug_buttons();
100
         lcd_debug_buttons();
94
     } else if (debug_buttons[BTN_REC] && (!debug_buttons[BTN_CLICK])) {
101
     } else if (debug_buttons[BTN_REC] && (!debug_buttons[BTN_CLICK])) {

+ 1
- 0
src/sequence.c View File

220
                 pulse_trigger_out(ch, channel_times[ch]);
220
                 pulse_trigger_out(ch, channel_times[ch]);
221
                 if (ui_get_machinemode() == MODE_LOOPSTATION) {
221
                 if (ui_get_machinemode() == MODE_LOOPSTATION) {
222
                     pulse_trigger_led(ch, channel_times[ch]);
222
                     pulse_trigger_led(ch, channel_times[ch]);
223
+                    pulse_trigger_led(ch + 4, channel_times[ch]);
223
                 }
224
                 }
224
             }
225
             }
225
         }
226
         }

+ 6
- 2
src/ui.c View File

35
 static enum machine_modes machine_mode = 0;
35
 static enum machine_modes machine_mode = 0;
36
 static uint32_t last_bat_fetch = 0;
36
 static uint32_t last_bat_fetch = 0;
37
 static float last_voltage = 0.0f;
37
 static float last_voltage = 0.0f;
38
+static float last_percentage = 0.0f;
38
 
39
 
39
 enum machine_modes ui_get_machinemode(void) {
40
 enum machine_modes ui_get_machinemode(void) {
40
     return machine_mode;
41
     return machine_mode;
99
         }
100
         }
100
     }
101
     }
101
 
102
 
102
-    snprintf(bat, sizeof(bat) - 1, "Bat: %.2fV", last_voltage);
103
+    snprintf(bat, sizeof(bat) - 1, "Bat: %.1f%% (%.2fV)", last_percentage, last_voltage);
103
     lcd_draw(mode, val, bat);
104
     lcd_draw(mode, val, bat);
104
 }
105
 }
105
 
106
 
286
         last_bat_fetch = now;
287
         last_bat_fetch = now;
287
 
288
 
288
         float volt = bat_get();
289
         float volt = bat_get();
289
-        if (fabsf(volt - last_voltage) >= 0.01f) {
290
+        float percentage = bat_to_percent(volt);
291
+        if ((fabsf(volt - last_voltage) >= 0.01f)
292
+                || (fabsf(percentage - last_percentage) >= 0.1f)) {
290
             last_voltage = volt;
293
             last_voltage = volt;
294
+            last_percentage = percentage;
291
             ui_redraw();
295
             ui_redraw();
292
         }
296
         }
293
     }
297
     }

Loading…
Cancel
Save