Browse Source

start work on firmware

Thomas Buck 1 week ago
parent
commit
646031a34a

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

@@ -0,0 +1,65 @@
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: dispensy.uf2
58
+          path: ${{github.workspace}}/repo/build/dispensy.uf2
59
+          if-no-files-found: error
60
+
61
+      - name: Upload release files
62
+        if: startsWith(github.ref, 'refs/tags/')
63
+        uses: softprops/action-gh-release@v1
64
+        with:
65
+          files: ${{github.workspace}}/repo/build/dispensy.uf2

+ 3
- 0
.gitmodules View File

@@ -1,3 +1,6 @@
1 1
 [submodule "docs/svg-pan-zoom"]
2 2
 	path = docs/svg-pan-zoom
3 3
 	url = https://github.com/WebSVG/svg-pan-zoom
4
+[submodule "firmware/pico-sdk"]
5
+	path = firmware/pico-sdk
6
+	url = https://github.com/raspberrypi/pico-sdk

+ 1
- 0
README.md View File

@@ -2,6 +2,7 @@
2 2
 
3 3
 ![PCB](https://github.com/drinkrobotics/dispensy/actions/workflows/kicad.yml/badge.svg)
4 4
 ![Docs](https://github.com/drinkrobotics/dispensy/actions/workflows/docs.yml/badge.svg)
5
+![Firmware](https://github.com/drinkrobotics/dispensy/actions/workflows/cmake.yml/badge.svg)
5 6
 
6 7
 One day aims to be a new hardware base for the [DrinkRobotics UbaBot](https://www.xythobuz.de/drinkrobotics.html).
7 8
 

+ 2
- 0
firmware/.gitignore View File

@@ -0,0 +1,2 @@
1
+build
2
+.cache

+ 78
- 0
firmware/CMakeLists.txt View File

@@ -0,0 +1,78 @@
1
+# ----------------------------------------------------------------------------
2
+# Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
3
+#
4
+# This program is free software: you can redistribute it and/or modify
5
+# it under the terms of the GNU General Public License as published by
6
+# the Free Software Foundation, either version 3 of the License, or
7
+# (at your option) any later version.
8
+#
9
+# This program is distributed in the hope that it will be useful,
10
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+# GNU General Public License for more details.
13
+#
14
+# See <http://www.gnu.org/licenses/>.
15
+# ----------------------------------------------------------------------------
16
+
17
+cmake_minimum_required(VERSION 3.5)
18
+
19
+# initialize pico-sdk from submodule
20
+include(pico-sdk/pico_sdk_init.cmake)
21
+
22
+project(dispensy C CXX)
23
+set(CMAKE_C_STANDARD 11)
24
+set(CMAKE_CXX_STANDARD 17)
25
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
26
+
27
+# initialize the Raspberry Pi Pico SDK
28
+pico_sdk_init()
29
+
30
+add_executable(dispensy)
31
+
32
+target_sources(dispensy PUBLIC
33
+    ${CMAKE_CURRENT_LIST_DIR}/src/main.c
34
+    ${CMAKE_CURRENT_LIST_DIR}/src/console.c
35
+    ${CMAKE_CURRENT_LIST_DIR}/src/log.c
36
+    ${CMAKE_CURRENT_LIST_DIR}/src/util.c
37
+    ${CMAKE_CURRENT_LIST_DIR}/src/usb.c
38
+    ${CMAKE_CURRENT_LIST_DIR}/src/usb_cdc.c
39
+    ${CMAKE_CURRENT_LIST_DIR}/src/usb_descriptors.c
40
+    ${CMAKE_CURRENT_LIST_DIR}/src/buttons.c
41
+    ${CMAKE_CURRENT_LIST_DIR}/src/lcd.c
42
+    ${CMAKE_CURRENT_LIST_DIR}/src/ring.c
43
+)
44
+
45
+target_include_directories(dispensy PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
46
+
47
+# enable generous warnings
48
+target_compile_options(dispensy PUBLIC
49
+    -Wall
50
+    -Wextra
51
+    -Werror
52
+    -Wshadow
53
+)
54
+
55
+# suppress some warnings for borked 3rd party files in Pico SDK
56
+set_source_files_properties(pico-sdk/lib/btstack/src/ble/sm.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
57
+set_source_files_properties(pico-sdk/lib/btstack/src/btstack_hid_parser.c PROPERTIES COMPILE_FLAGS -Wno-maybe-uninitialized)
58
+set_source_files_properties(pico-sdk/src/rp2_common/pico_cyw43_driver/cyw43_driver.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
59
+set_source_files_properties(pico-sdk/lib/btstack/src/classic/avdtp_util.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
60
+set_source_files_properties(pico-sdk/lib/btstack/src/classic/goep_client.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
61
+set_source_files_properties(pico-sdk/lib/btstack/src/classic/goep_server.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
62
+set_source_files_properties(pico-sdk/src/rp2_common/hardware_flash/flash.c PROPERTIES COMPILE_FLAGS -Wno-shadow)
63
+
64
+# pull in common dependencies
65
+target_link_libraries(dispensy
66
+    pico_stdlib
67
+    pico_unique_id
68
+    tinyusb_device
69
+    tinyusb_board
70
+    hardware_adc
71
+    hardware_gpio
72
+    hardware_pwm
73
+)
74
+
75
+# fix for Errata RP2040-E5 (the fix requires use of GPIO 15)
76
+target_compile_definitions(dispensy PUBLIC PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1)
77
+
78
+pico_add_extra_outputs(dispensy)

+ 120
- 0
firmware/README.md View File

@@ -0,0 +1,120 @@
1
+# Dispensy Firmware
2
+
3
+TODO
4
+
5
+## Quick Start
6
+
7
+When compiling for the first time, check out the required git submodules.
8
+
9
+    git submodule update --init
10
+    cd pico-sdk
11
+    git submodule update --init
12
+
13
+Then do this to build.
14
+
15
+    mkdir build
16
+    cmake -B build
17
+    make -C build -j4
18
+
19
+And flash the resulting `dispensy.uf2` file to your Pico as usual.
20
+
21
+For convenience you can use the included `flash.sh`, as long as you flashed the binary manually once before.
22
+
23
+    make -C build -j4
24
+    ./flash.sh build/dispensy.uf2
25
+
26
+This will use the mass storage bootloader to upload a new uf2 image.
27
+
28
+For old-school debugging a serial port will be presented by the firmware.
29
+Open it using eg. `picocom`, or with the included `debug.sh` script.
30
+
31
+For dependencies to compile, on Arch install these.
32
+
33
+    sudo pacman -S arm-none-eabi-gcc arm-none-eabi-newlib picocom cmake cxxtest
34
+
35
+## Proper Debugging
36
+
37
+You can also use the SWD interface for proper hardware debugging.
38
+
39
+This follows the instructions from the [RP2040 Getting Started document](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf) from chapter 5 and 6.
40
+
41
+For ease of reading the disassembly, create a debug build.
42
+
43
+    mkdir build_debug
44
+    cmake -B build_debug -DCMAKE_BUILD_TYPE=Debug
45
+    make -C build -j4
46
+
47
+You need a hardware SWD probe.
48
+This can be made from another Pico, see Appendix A in the document linked above.
49
+For this you need to compile the `picoprobe` firmware, like this.
50
+
51
+    git clone https://github.com/raspberrypi/picoprobe.git
52
+    cd picoprobe
53
+
54
+    git submodule update --init
55
+    mkdir build
56
+    cd build
57
+
58
+    PICO_SDK_PATH=../../../pico-sdk cmake ..
59
+    make -j4
60
+
61
+    cd ../.. # back to build_debug directory from before
62
+
63
+And flash the resulting `picoprobe.uf2` to your probe.
64
+Connect `GP2` of the probe to `SWCLK` of the target and `GP3` of the probe to `SWDIO` of the target.
65
+Of course you also need to connect GND between both.
66
+
67
+You need some dependencies, mainly `gdb-multiarch` and the RP2040 fork of `OpenOCD`.
68
+
69
+    sudo apt install gdb-multiarch   # Debian / Ubuntu
70
+    sudo pacman -S arm-none-eabi-gdb # Arch Linux
71
+
72
+    git clone https://github.com/raspberrypi/openocd.git --branch rp2040 --recursive --depth=1
73
+    cd openocd
74
+
75
+    # install udev rules
76
+    sudo cp contrib/60-openocd.rules /etc/udev/rules.d
77
+    sudo udevadm control --reload-rules && sudo udevadm trigger
78
+
79
+    ./bootstrap
80
+    ./configure --enable-ftdi --enable-sysfsgpio --enable-bcm2835gpio
81
+    make -j4
82
+
83
+    cd .. # back to build_debug directory from before
84
+
85
+Now we can flash a firmware image via OpenOCD.
86
+
87
+    ./openocd/src/openocd -s openocd/tcl -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "cmsis_dap_vid_pid 0x2e8a 0x000c" -c "program gadget.elf verify reset exit"
88
+
89
+And also start a GDB debugging session.
90
+
91
+    ./openocd/src/openocd -s openocd/tcl -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "cmsis_dap_vid_pid 0x2e8a 0x000c"
92
+    arm-none-eabi-gdb gadget.elf
93
+    target extended-remote localhost:3333
94
+
95
+    load # program elf into flash
96
+    monitor reset init # put into clean initial state
97
+    continue # start program
98
+
99
+These commands have also been put in the `flash_swd.sh` and `debug_swd.sh` scripts, respectively.
100
+They require the `build_debug` folder where you checked out and built OpenOCD.
101
+Here are some [general GDB tips](https://beej.us/guide/bggdb/).
102
+
103
+## License
104
+
105
+The firmware itself is licensed as GPLv3.
106
+I initially adapted it from my own [Trackball](https://git.xythobuz.de/thomas/Trackball) and [Volcano Remote](https://git.xythobuz.de/thomas/sb-py) projects.
107
+It uses the [Pi Pico SDK](https://github.com/raspberrypi/pico-sdk), licensed as BSD 3-clause, and therefore also [TinyUSB](https://github.com/hathach/tinyusb), licensed under the MIT license.
108
+Some code is adapted from the TinyUSB examples.
109
+
110
+    This program is free software: you can redistribute it and/or modify
111
+    it under the terms of the GNU General Public License as published by
112
+    the Free Software Foundation, either version 3 of the License, or
113
+    (at your option) any later version.
114
+
115
+    This program is distributed in the hope that it will be useful,
116
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
117
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
118
+    GNU General Public License for more details.
119
+
120
+    See <http://www.gnu.org/licenses/>.

+ 35
- 0
firmware/debug.sh View File

@@ -0,0 +1,35 @@
1
+#!/bin/bash
2
+
3
+# ----------------------------------------------------------------------------
4
+# Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+#
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
8
+# the Free Software Foundation, either version 3 of the License, or
9
+# (at your option) any later version.
10
+#
11
+# This program is distributed in the hope that it will be useful,
12
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+# GNU General Public License for more details.
15
+#
16
+# See <http://www.gnu.org/licenses/>.
17
+# ----------------------------------------------------------------------------
18
+
19
+set -euo pipefail
20
+
21
+SERIAL=/dev/serial/by-id/usb-xythobuz_Dispensy_*
22
+
23
+echo -n Waiting for serial port to appear
24
+until [ -e $SERIAL ]
25
+do
26
+    echo -n .
27
+    sleep 1
28
+done
29
+echo
30
+
31
+echo Opening picocom terminal
32
+echo "[C-a] [C-x] to exit"
33
+echo
34
+
35
+picocom -q --omap crcrlf --imap lfcrlf $SERIAL

+ 48
- 0
firmware/debug_swd.sh View File

@@ -0,0 +1,48 @@
1
+#!/bin/bash
2
+
3
+# ----------------------------------------------------------------------------
4
+# Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+#
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
8
+# the Free Software Foundation, either version 3 of the License, or
9
+# (at your option) any later version.
10
+#
11
+# This program is distributed in the hope that it will be useful,
12
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+# GNU General Public License for more details.
15
+#
16
+# See <http://www.gnu.org/licenses/>.
17
+# ----------------------------------------------------------------------------
18
+
19
+set -euo pipefail
20
+
21
+cd "$(dirname "$0")"
22
+
23
+echo Starting OpenOCD in background
24
+echo "\n\nstarting new openocd" >> openocd.log
25
+./build_debug/openocd/src/openocd -s build_debug/openocd/tcl -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "cmsis_dap_vid_pid 0x2e8a 0x000c" >> openocd.log 2>&1 &
26
+OPENOCD_PID=$!
27
+
28
+# give OpenOCD some time to output stuff
29
+sleep 1
30
+
31
+echo -n Waiting for debugger to appear
32
+while ! netstat -tna | grep 'LISTEN\>' | grep -q ':3333\>'; do
33
+    echo -n .
34
+    sleep 1
35
+done
36
+
37
+echo Starting GDB
38
+arm-none-eabi-gdb \
39
+-ex "set history save" \
40
+-ex "show print pretty" \
41
+-ex "target extended-remote localhost:3333" \
42
+-ex "tui new-layout default src 1 status 1 cmd 2" \
43
+-ex "tui layout default" \
44
+-ex "tui enable" \
45
+$1
46
+
47
+echo Killing OpenOCD instance in background
48
+kill $OPENOCD_PID

+ 45
- 0
firmware/flash.sh View File

@@ -0,0 +1,45 @@
1
+#!/bin/bash
2
+
3
+# ----------------------------------------------------------------------------
4
+# Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+#
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
8
+# the Free Software Foundation, either version 3 of the License, or
9
+# (at your option) any later version.
10
+#
11
+# This program is distributed in the hope that it will be useful,
12
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+# GNU General Public License for more details.
15
+#
16
+# See <http://www.gnu.org/licenses/>.
17
+# ----------------------------------------------------------------------------
18
+
19
+set -euo pipefail
20
+
21
+SERIAL=/dev/serial/by-id/usb-xythobuz_Dispensy_*
22
+DISK=/dev/disk/by-label/RPI-RP2
23
+MOUNT=/mnt/tmp
24
+
25
+if [ ! -e $DISK ]
26
+then
27
+    echo Resetting Raspberry Pi Pico
28
+    echo -n -e "\\x18" > $SERIAL
29
+fi
30
+
31
+echo -n Waiting for disk to appear
32
+until [ -e $DISK ]
33
+do
34
+    echo -n .
35
+    sleep 1
36
+done
37
+echo
38
+
39
+echo Mounting bootloader disk
40
+sudo mount $DISK $MOUNT
41
+
42
+echo Copying binary
43
+sudo cp $1 $MOUNT
44
+
45
+echo Done

+ 23
- 0
firmware/flash_swd.sh View File

@@ -0,0 +1,23 @@
1
+#!/bin/bash
2
+
3
+# ----------------------------------------------------------------------------
4
+# Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+#
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
8
+# the Free Software Foundation, either version 3 of the License, or
9
+# (at your option) any later version.
10
+#
11
+# This program is distributed in the hope that it will be useful,
12
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+# GNU General Public License for more details.
15
+#
16
+# See <http://www.gnu.org/licenses/>.
17
+# ----------------------------------------------------------------------------
18
+
19
+set -euo pipefail
20
+
21
+cd "$(dirname "$0")"
22
+
23
+./build_debug/openocd/src/openocd -s build_debug/openocd/tcl -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "cmsis_dap_vid_pid 0x2e8a 0x000c" -c "program $1 verify reset exit"

+ 42
- 0
firmware/include/buttons.h View File

@@ -0,0 +1,42 @@
1
+/*
2
+ * buttons.h
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __BUTTONS_H__
20
+#define __BUTTONS_H__
21
+
22
+#include <stdbool.h>
23
+
24
+enum buttons {
25
+    BTN_A = 0,
26
+    BTN_B,
27
+    BTN_X,
28
+    BTN_Y,
29
+    BTN_UP,
30
+    BTN_DOWN,
31
+    BTN_LEFT,
32
+    BTN_RIGHT,
33
+    BTN_ENTER,
34
+    NUM_BTNS // count
35
+};
36
+
37
+void buttons_init(void);
38
+void buttons_callback(void (*fp)(enum buttons, bool));
39
+void buttons_run(void);
40
+
41
+#endif // __BUTTONS_H__
42
+

+ 57
- 0
firmware/include/config.h View File

@@ -0,0 +1,57 @@
1
+/*
2
+ * config.h
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __CONFIG_H__
20
+#define __CONFIG_H__
21
+
22
+#define APP_VERSION_MAJOR 0
23
+#define APP_VERSION_MINOR 3
24
+
25
+#define MENU_PREFER_VOLCANO
26
+//#define MENU_PREFER_CRAFTY
27
+
28
+#define VOLCANO_AUTO_CONNECT_TIMEOUT_MS 2000
29
+#define VOLCANO_AUTO_CONNECT_WITHIN_MS 10000
30
+
31
+#define COUNTRY_CODE CYW43_COUNTRY_GERMANY
32
+
33
+#ifdef NDEBUG
34
+// Release build
35
+#define AUTO_MOUNT_MASS_STORAGE
36
+#define AUTO_LOG_ON_MASS_STORAGE
37
+#endif // NDEBUG
38
+
39
+#define WATCHDOG_PERIOD_MS 1000
40
+#define FLASH_LOCK_TIMEOUT_MS 500
41
+
42
+// ASCII 0x18 = CAN (cancel)
43
+#define ENTER_BOOTLOADER_MAGIC 0x18
44
+
45
+//#define DISABLE_CDC_DTR_CHECK
46
+#define DEBOUNCE_DELAY_MS 5
47
+
48
+#define SERIAL_WRITES_BLOCK_WHEN_BUFFER_FULL
49
+
50
+// TODO needs to be the same as in pack_data.sh
51
+#define DISK_BLOCK_SIZE 512
52
+#define DISK_BLOCK_COUNT (256 + 128) // 384 * 512 = 196608
53
+
54
+#define TEST_VOLCANO_AUTO_CONNECT "EA:06:75:A7:D1:15 1"
55
+#define TEST_CRAFTY_AUTO_CONNECT "60:B6:E1:BB:61:36 0"
56
+
57
+#endif // __CONFIG_H__

+ 26
- 0
firmware/include/console.h View File

@@ -0,0 +1,26 @@
1
+/*
2
+ * console.h
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __CONSOLE_H__
20
+#define __CONSOLE_H__
21
+
22
+void cnsl_init(void);
23
+void cnsl_run(void);
24
+void cnsl_handle_input(const void *buf, size_t len);
25
+
26
+#endif // __CONSOLE_H__

+ 27
- 0
firmware/include/lcd.h View File

@@ -0,0 +1,27 @@
1
+/*
2
+ * lipo.h
3
+ *
4
+ * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __LCD_H__
20
+#define __LCD_H__
21
+
22
+#define LCD_WIDTH 128
23
+#define LCD_HEIGHT 64
24
+
25
+void lcd_init(void);
26
+
27
+#endif // __LCD_H__

+ 58
- 0
firmware/include/log.h View File

@@ -0,0 +1,58 @@
1
+/*
2
+ * log.h
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __LOG_H__
20
+#define __LOG_H__
21
+
22
+#include <stdarg.h>
23
+#include <stdbool.h>
24
+#include <inttypes.h>
25
+#include "pico/stdlib.h"
26
+
27
+// for output that is stored in the debug log.
28
+// will be re-played from buffer when terminal connects
29
+#ifndef PICOWOTA
30
+#define debug(fmt, ...) debug_log(true, \
31
+        "%08" PRIu32 " %s:%d: " fmt "\r\n", \
32
+        to_ms_since_boot(get_absolute_time()), \
33
+        __func__, __LINE__, \
34
+        ##__VA_ARGS__)
35
+#else // PICOWOTA
36
+#define debug(fmt, ...) debug_log(true, \
37
+        fmt "\r\n", \
38
+        ##__VA_ARGS__)
39
+#endif // PICOWOTA
40
+
41
+// for interactive output. is not stored or re-played.
42
+#define print(fmt, ...) debug_log(false, fmt, ##__VA_ARGS__)
43
+#define println(fmt, ...) debug_log(false, fmt "\r\n", ##__VA_ARGS__)
44
+
45
+void debug_log(bool log, const char *format, ...) __attribute__((format(printf, 2, 3)));
46
+void debug_wait_input(const char *format, ...) __attribute__((format(printf, 1, 2)));
47
+void debug_log_va(bool log, const char *format, va_list args);
48
+
49
+void log_dump_to_usb(void);
50
+void log_dump_to_uart(void);
51
+void log_dump_to_disk(void);
52
+
53
+void debug_handle_input(const void *buff, size_t len);
54
+
55
+#include "ring.h"
56
+struct ring_buffer *log_get(void);
57
+
58
+#endif // __LOG_H__

+ 28
- 0
firmware/include/main.h View File

@@ -0,0 +1,28 @@
1
+/*
2
+ * main.h
3
+ *
4
+ * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __MAIN_H__
20
+#define __MAIN_H__
21
+
22
+void main_loop_hw(void);
23
+
24
+void networking_init(void);
25
+void networking_deinit(void);
26
+void networking_run(void);
27
+
28
+#endif // __MAIN_H__

+ 45
- 0
firmware/include/ring.h View File

@@ -0,0 +1,45 @@
1
+/*
2
+ * ring.h
3
+ *
4
+ * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __RING_BUFFER_H__
20
+#define __RING_BUFFER_H__
21
+
22
+#include <stddef.h>
23
+#include <stdint.h>
24
+#include <stdbool.h>
25
+
26
+struct ring_buffer {
27
+    void *buffer;
28
+    size_t size;
29
+    size_t el_len;
30
+    size_t head, tail;
31
+    bool full;
32
+};
33
+#define RB_INIT(b, s, e) { .buffer = b, .size = s, .el_len = e, .head = 0, .tail = 0, .full = false }
34
+
35
+void rb_add(struct ring_buffer *rb, const void *data, size_t length);
36
+#define rb_push(rb, v) rb_add(rb, v, 1)
37
+size_t rb_len(struct ring_buffer *rb);
38
+#define rb_space(rb) ((rb)->size - rb_len(rb))
39
+void rb_dump(struct ring_buffer *rb, void (*write)(const void *, size_t), size_t skip);
40
+void rb_move(struct ring_buffer *rb, void (*write)(const void *, size_t));
41
+void rb_peek(struct ring_buffer *rb, void *buf);
42
+void rb_pop(struct ring_buffer *rb, void *buf);
43
+size_t rb_get(struct ring_buffer *rb, void *data, size_t length);
44
+
45
+#endif // __RING_BUFFER_H__

+ 123
- 0
firmware/include/tusb_config.h View File

@@ -0,0 +1,123 @@
1
+/*
2
+ * Extended from TinyUSB example code.
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ * THE SOFTWARE.
27
+ *
28
+ */
29
+
30
+#ifndef _TUSB_CONFIG_H_
31
+#define _TUSB_CONFIG_H_
32
+
33
+#ifdef __cplusplus
34
+ extern "C" {
35
+#endif
36
+
37
+//--------------------------------------------------------------------
38
+// COMMON CONFIGURATION
39
+//--------------------------------------------------------------------
40
+
41
+// defined by board.mk
42
+#ifndef CFG_TUSB_MCU
43
+  #error CFG_TUSB_MCU must be defined
44
+#endif
45
+
46
+// RHPort number used for device can be defined by board.mk, default to port 0
47
+#ifndef BOARD_DEVICE_RHPORT_NUM
48
+  #define BOARD_DEVICE_RHPORT_NUM     0
49
+#endif
50
+
51
+// RHPort max operational speed can defined by board.mk
52
+// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
53
+#ifndef BOARD_DEVICE_RHPORT_SPEED
54
+  #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \
55
+       CFG_TUSB_MCU == OPT_MCU_NUC505  || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X)
56
+    #define BOARD_DEVICE_RHPORT_SPEED   OPT_MODE_HIGH_SPEED
57
+  #else
58
+    #define BOARD_DEVICE_RHPORT_SPEED   OPT_MODE_FULL_SPEED
59
+  #endif
60
+#endif
61
+
62
+// Device mode with rhport and speed defined by board.mk
63
+#if   BOARD_DEVICE_RHPORT_NUM == 0
64
+  #define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
65
+#elif BOARD_DEVICE_RHPORT_NUM == 1
66
+  #define CFG_TUSB_RHPORT1_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
67
+#else
68
+  #error "Incorrect RHPort configuration"
69
+#endif
70
+
71
+#ifndef CFG_TUSB_OS
72
+#define CFG_TUSB_OS               OPT_OS_NONE
73
+#endif
74
+
75
+// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
76
+// #define CFG_TUSB_DEBUG           0
77
+
78
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
79
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
80
+ * into those specific section.
81
+ * e.g
82
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
83
+ * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
84
+ */
85
+#ifndef CFG_TUSB_MEM_SECTION
86
+#define CFG_TUSB_MEM_SECTION
87
+#endif
88
+
89
+#ifndef CFG_TUSB_MEM_ALIGN
90
+#define CFG_TUSB_MEM_ALIGN          __attribute__ ((aligned(4)))
91
+#endif
92
+
93
+//--------------------------------------------------------------------
94
+// DEVICE CONFIGURATION
95
+//--------------------------------------------------------------------
96
+
97
+#ifndef CFG_TUD_ENDPOINT0_SIZE
98
+#define CFG_TUD_ENDPOINT0_SIZE    64
99
+#endif
100
+
101
+//------------- CLASS -------------//
102
+#define CFG_TUD_HID               0
103
+#define CFG_TUD_CDC               1
104
+#define CFG_TUD_MSC               0
105
+#define CFG_TUD_MIDI              0
106
+#define CFG_TUD_VENDOR            0
107
+
108
+// CDC FIFO size of TX and RX
109
+#define CFG_TUD_CDC_RX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
110
+#define CFG_TUD_CDC_TX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
111
+
112
+// CDC Endpoint transfer buffer size, more is faster
113
+#define CFG_TUD_CDC_EP_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
114
+
115
+// MIDI FIFO size of TX and RX
116
+#define CFG_TUD_MIDI_RX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
117
+#define CFG_TUD_MIDI_TX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
118
+
119
+#ifdef __cplusplus
120
+ }
121
+#endif
122
+
123
+#endif /* _TUSB_CONFIG_H_ */

+ 25
- 0
firmware/include/usb.h View File

@@ -0,0 +1,25 @@
1
+/*
2
+ * usb.h
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __USB_H__
20
+#define __USB_H__
21
+
22
+void usb_init(void);
23
+void usb_run(void);
24
+
25
+#endif // __USB_H__

+ 29
- 0
firmware/include/usb_cdc.h View File

@@ -0,0 +1,29 @@
1
+/*
2
+ * usb_cdc.h
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __USB_CDC_H__
20
+#define __USB_CDC_H__
21
+
22
+#include <stddef.h>
23
+#include <stdint.h>
24
+#include <stdbool.h>
25
+
26
+void usb_cdc_write(const void *buf, size_t count);
27
+void usb_cdc_set_reroute(bool reroute);
28
+
29
+#endif // __USB_CDC_H__

+ 28
- 0
firmware/include/usb_descriptors.h View File

@@ -0,0 +1,28 @@
1
+/*
2
+ * usb_descriptors.h
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef USB_DESCRIPTORS_H_
20
+#define USB_DESCRIPTORS_H_
21
+
22
+#include "pico/unique_id.h"
23
+
24
+extern char string_pico_serial[2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1];
25
+
26
+void usb_descriptor_init_id(void);
27
+
28
+#endif /* USB_DESCRIPTORS_H_ */

+ 36
- 0
firmware/include/util.h View File

@@ -0,0 +1,36 @@
1
+/*
2
+ * util.h
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __UTIL_H__
20
+#define __UTIL_H__
21
+
22
+void heartbeat_init(void);
23
+void heartbeat_run(void);
24
+
25
+int32_t convert_two_complement(int32_t b);
26
+
27
+bool str_startswith(const char *str, const char *start);
28
+
29
+void reset_to_bootloader(void);
30
+void reset_to_main(void);
31
+
32
+void hexdump(const uint8_t *buff, size_t len);
33
+
34
+float map(float value, float leftMin, float leftMax, float rightMin, float rightMax);
35
+
36
+#endif // __UTIL_H__

+ 1
- 0
firmware/pico-sdk

@@ -0,0 +1 @@
1
+Subproject commit 6a7db34ff63345a7badec79ebea3aaef1712f374

+ 83
- 0
firmware/src/buttons.c View File

@@ -0,0 +1,83 @@
1
+/*
2
+ * buttons.c
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "pico/stdlib.h"
20
+
21
+#include "config.h"
22
+#include "log.h"
23
+#include "buttons.h"
24
+
25
+static const uint gpio_num[NUM_BTNS] = {
26
+    15, // BTN_A
27
+    17, // BTN_B
28
+    19, // BTN_X
29
+    21, // BTN_Y
30
+     2, // BTN_UP
31
+    18, // BTN_DOWN
32
+    16, // BTN_LEFT
33
+    20, // BTN_RIGHT
34
+     3, // BTN_ENTER
35
+};
36
+
37
+struct button_state {
38
+    uint32_t last_time;
39
+    bool current_state, last_state;
40
+};
41
+
42
+static struct button_state buttons[NUM_BTNS];
43
+static void (*callback)(enum buttons, bool) = NULL;
44
+
45
+void buttons_init(void) {
46
+    for (uint i = 0; i < NUM_BTNS; i++) {
47
+        gpio_init(gpio_num[i]);
48
+        gpio_set_dir(gpio_num[i], GPIO_IN);
49
+        gpio_pull_up(gpio_num[i]);
50
+
51
+        buttons[i].last_time = 0;
52
+        buttons[i].current_state = false;
53
+        buttons[i].last_state = false;
54
+    }
55
+}
56
+
57
+void buttons_callback(void (*fp)(enum buttons, bool)) {
58
+    callback = fp;
59
+}
60
+
61
+void buttons_run(void) {
62
+    for (uint i = 0; i < NUM_BTNS; i++) {
63
+        bool state = !gpio_get(gpio_num[i]);
64
+        uint32_t now = to_ms_since_boot(get_absolute_time());
65
+
66
+        if (state != buttons[i].last_state) {
67
+            buttons[i].last_time = now;
68
+        }
69
+
70
+        if ((now - buttons[i].last_time) > DEBOUNCE_DELAY_MS) {
71
+            if (state != buttons[i].current_state) {
72
+                //debug("btn %d now %s", i, state ? "pressed" : "released");
73
+
74
+                buttons[i].current_state = state;
75
+                if (callback) {
76
+                    callback(i, state);
77
+                }
78
+            }
79
+        }
80
+
81
+        buttons[i].last_state = state;
82
+    }
83
+}

+ 202
- 0
firmware/src/console.c View File

@@ -0,0 +1,202 @@
1
+/*
2
+ * console.c
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include <inttypes.h>
20
+#include <string.h>
21
+#include <unistd.h>
22
+#include <stdio.h>
23
+
24
+#include "pico/stdlib.h"
25
+#include "hardware/watchdog.h"
26
+
27
+#include "config.h"
28
+#include "log.h"
29
+#include "util.h"
30
+#include "usb_cdc.h"
31
+#include "lcd.h"
32
+#include "main.h"
33
+#include "console.h"
34
+
35
+#define CNSL_BUFF_SIZE 64
36
+#define CNSL_REPEAT_MS 500
37
+
38
+#define DEV_AUTO_CONNECT(s) {                                     \
39
+    ble_scan(BLE_SCAN_OFF);                                       \
40
+    bd_addr_t addr;                                               \
41
+    bd_addr_type_t type;                                          \
42
+    const char *foo = s;                                          \
43
+    sscanf(foo, "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX %hhu", \
44
+            &addr[0], &addr[1], &addr[2], &addr[3],               \
45
+            &addr[4], &addr[5], &type);                           \
46
+    ble_connect(addr, type);                                      \
47
+    while (!ble_is_connected()) {                                 \
48
+        sleep_ms(1);                                              \
49
+    }                                                             \
50
+}
51
+
52
+static char cnsl_line_buff[CNSL_BUFF_SIZE + 1];
53
+static uint32_t cnsl_buff_pos = 0;
54
+
55
+static char cnsl_last_command[CNSL_BUFF_SIZE + 1];
56
+
57
+static char cnsl_repeated_command[CNSL_BUFF_SIZE + 1];
58
+static bool repeat_command = false;
59
+static uint32_t last_repeat_time = 0;
60
+
61
+static void cnsl_interpret(const char *line) {
62
+    if (strlen(line) == 0) {
63
+        if ((strlen(cnsl_last_command) > 0) && (strcmp(cnsl_last_command, "repeat") != 0)) {
64
+            // repeat last command once
65
+            println("repeating command \"%s\"", cnsl_last_command);
66
+            cnsl_interpret(cnsl_last_command);
67
+            println();
68
+        }
69
+        return;
70
+    } else if (strcmp(line, "repeat") == 0) {
71
+        if (!repeat_command) {
72
+            // mark last command to be repeated multiple times
73
+            strncpy(cnsl_repeated_command, cnsl_last_command, CNSL_BUFF_SIZE + 1);
74
+            last_repeat_time = to_ms_since_boot(get_absolute_time()) - 1001;
75
+            repeat_command = true;
76
+        } else {
77
+            // stop repeating
78
+            repeat_command = false;
79
+        }
80
+    } else if ((strcmp(line, "help") == 0)
81
+            || (strcmp(line, "h") == 0)
82
+            || (strcmp(line, "?") == 0)) {
83
+        println("Dispensy Firmware Usage:");
84
+        println("");
85
+        println("  reset - reset back into this firmware");
86
+        println("   \\x18 - reset to bootloader");
87
+        println("");
88
+        println("Press Enter with no input to repeat last command.");
89
+        println("Use repeat to continuously execute last command.");
90
+        println("Stop this by calling repeat again.");
91
+    } else if (strcmp(line, "reset") == 0) {
92
+        reset_to_main();
93
+    } else {
94
+        println("unknown command \"%s\"", line);
95
+    }
96
+
97
+    println();
98
+}
99
+
100
+void cnsl_init(void) {
101
+    cnsl_buff_pos = 0;
102
+    for (int i = 0; i < CNSL_BUFF_SIZE + 1; i++) {
103
+        cnsl_line_buff[i] = '\0';
104
+        cnsl_last_command[i] = '\0';
105
+        cnsl_repeated_command[i] = '\0';
106
+    }
107
+}
108
+
109
+static int32_t cnsl_find_line_end(void) {
110
+    for (uint32_t i = 0; i < cnsl_buff_pos; i++) {
111
+        if ((cnsl_line_buff[i] == '\r') || (cnsl_line_buff[i] == '\n')) {
112
+            return i;
113
+        }
114
+    }
115
+    return -1;
116
+}
117
+
118
+void cnsl_run(void) {
119
+    if (repeat_command && (strlen(cnsl_repeated_command) > 0)
120
+            && (strcmp(cnsl_repeated_command, "repeat") != 0)) {
121
+        uint32_t now = to_ms_since_boot(get_absolute_time());
122
+        if (now >= (last_repeat_time + CNSL_REPEAT_MS)) {
123
+            println("repeating command \"%s\"", cnsl_repeated_command);
124
+            cnsl_interpret(cnsl_repeated_command);
125
+            println();
126
+
127
+            last_repeat_time = now;
128
+        }
129
+    } else {
130
+        if (repeat_command) {
131
+            println("nothing to repeat");
132
+        }
133
+        repeat_command = false;
134
+    }
135
+}
136
+
137
+void cnsl_handle_input(const void *buf, size_t len) {
138
+    if ((cnsl_buff_pos + len) > CNSL_BUFF_SIZE) {
139
+        debug("error: console input buffer overflow! %lu > %u", cnsl_buff_pos + len, CNSL_BUFF_SIZE);
140
+        cnsl_init();
141
+    }
142
+
143
+    memcpy(cnsl_line_buff + cnsl_buff_pos, buf, len);
144
+    cnsl_buff_pos += len;
145
+
146
+    // handle backspace and local echo
147
+    for (ssize_t i = cnsl_buff_pos - len; i < (ssize_t)cnsl_buff_pos; i++) {
148
+        if ((cnsl_line_buff[i] == '\b') || (cnsl_line_buff[i] == 0x7F)) {
149
+            if (i > 0) {
150
+                // overwrite previous character and backspace
151
+                for (ssize_t j = i; j < (ssize_t)cnsl_buff_pos - 1; j++) {
152
+                    cnsl_line_buff[j - 1] = cnsl_line_buff[j + 1];
153
+                }
154
+                cnsl_buff_pos -= 2;
155
+            } else {
156
+                // just remove the backspace
157
+                for (ssize_t j = i; j < (ssize_t)cnsl_buff_pos - 1; j++) {
158
+                    cnsl_line_buff[j] = cnsl_line_buff[j + 1];
159
+                }
160
+                cnsl_buff_pos -= 1;
161
+            }
162
+
163
+            usb_cdc_write((const uint8_t *)"\b \b", 3);
164
+
165
+#ifndef NDEBUG
166
+            serial_write((const uint8_t *)"\b \b", 3);
167
+#endif
168
+
169
+            // check for another backspace in this space
170
+            i--;
171
+        } else {
172
+            usb_cdc_write((const uint8_t *)(cnsl_line_buff + i), 1);
173
+
174
+#ifndef NDEBUG
175
+            serial_write((const uint8_t *)(cnsl_line_buff + i), 1);
176
+#endif
177
+        }
178
+    }
179
+
180
+    int32_t line_len = cnsl_find_line_end();
181
+    if (line_len < 0) {
182
+        // user has not pressed enter yet
183
+        return;
184
+    }
185
+
186
+    // convert line to C-style string
187
+    cnsl_line_buff[line_len] = '\0';
188
+
189
+    cnsl_interpret(cnsl_line_buff);
190
+
191
+    // store command for eventual repeats
192
+    strncpy(cnsl_last_command, cnsl_line_buff, CNSL_BUFF_SIZE + 1);
193
+
194
+    // clear string and move following data over
195
+    uint32_t cnt = line_len + 1;
196
+    if (cnsl_line_buff[line_len + 1] == '\n') {
197
+        cnt++;
198
+    }
199
+    memset(cnsl_line_buff, '\0', cnt);
200
+    memmove(cnsl_line_buff, cnsl_line_buff + cnt, sizeof(cnsl_line_buff) - cnt);
201
+    cnsl_buff_pos -= cnt;
202
+}

+ 26
- 0
firmware/src/lcd.c View File

@@ -0,0 +1,26 @@
1
+/*
2
+ * lcd.c
3
+ *
4
+ * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "pico/stdlib.h"
20
+
21
+#include "config.h"
22
+#include "log.h"
23
+#include "lcd.h"
24
+
25
+void lcd_init(void) {
26
+}

+ 114
- 0
firmware/src/log.c View File

@@ -0,0 +1,114 @@
1
+/*
2
+ * log.c
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include <stdio.h>
20
+#include <string.h>
21
+
22
+#include "hardware/watchdog.h"
23
+
24
+#include "config.h"
25
+#include "main.h"
26
+#include "usb_cdc.h"
27
+#include "ring.h"
28
+#include "log.h"
29
+
30
+static uint8_t log_buff[4096] = {0};
31
+static struct ring_buffer log_rb = RB_INIT(log_buff, sizeof(log_buff), 1);
32
+
33
+static uint8_t line_buff[256] = {0};
34
+static volatile bool got_input = false;
35
+
36
+static void add_to_log(const void *buff, size_t len) {
37
+    rb_add(&log_rb, buff, len);
38
+}
39
+
40
+struct ring_buffer *log_get(void) {
41
+    return &log_rb;
42
+}
43
+
44
+static void log_dump_to_x(void (*write)(const void *, size_t)) {
45
+    if (rb_len(&log_rb) == 0) {
46
+        return;
47
+    }
48
+
49
+    int l = snprintf((char *)line_buff, sizeof(line_buff), "\r\n\r\nbuffered log output:\r\n");
50
+    if ((l > 0) && (l <= (int)sizeof(line_buff))) {
51
+        write(line_buff, l);
52
+    }
53
+
54
+    rb_dump(&log_rb, write, 0);
55
+
56
+    l = snprintf((char *)line_buff, sizeof(line_buff), "\r\n\r\nlive log:\r\n");
57
+    if ((l > 0) && (l <= (int)sizeof(line_buff))) {
58
+        write(line_buff, l);
59
+    }
60
+}
61
+
62
+void log_dump_to_usb(void) {
63
+    log_dump_to_x(usb_cdc_write);
64
+}
65
+
66
+void debug_log_va(bool do_log, const char *format, va_list args) {
67
+    int l = vsnprintf((char *)line_buff, sizeof(line_buff), format, args);
68
+
69
+    if (l < 0) {
70
+        // encoding error
71
+        l = snprintf((char *)line_buff, sizeof(line_buff), "%s: encoding error\r\n", __func__);
72
+    } else if (l >= (ssize_t)sizeof(line_buff)) {
73
+        // not enough space for string
74
+        l = snprintf((char *)line_buff, sizeof(line_buff), "%s: message too long (%d)\r\n", __func__, l);
75
+    }
76
+    if ((l > 0) && (l <= (int)sizeof(line_buff))) {
77
+        usb_cdc_write(line_buff, l);
78
+
79
+        if (do_log) {
80
+            add_to_log(line_buff, l);
81
+        }
82
+    }
83
+}
84
+
85
+void debug_log(bool do_log, const char* format, ...) {
86
+    va_list args;
87
+    va_start(args, format);
88
+    debug_log_va(do_log, format, args);
89
+    va_end(args);
90
+}
91
+
92
+void debug_handle_input(const void *buff, size_t len) {
93
+    (void)buff;
94
+
95
+    if (len > 0) {
96
+        got_input = true;
97
+    }
98
+}
99
+
100
+void debug_wait_input(const char *format, ...) {
101
+    va_list args;
102
+    va_start(args, format);
103
+    debug_log_va(false, format, args);
104
+    va_end(args);
105
+
106
+    got_input = false;
107
+    usb_cdc_set_reroute(true);
108
+
109
+    while (!got_input) {
110
+        main_loop_hw();
111
+    }
112
+
113
+    usb_cdc_set_reroute(false);
114
+}

+ 63
- 0
firmware/src/main.c View File

@@ -0,0 +1,63 @@
1
+/*
2
+ * main.c
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "pico/stdlib.h"
20
+#include "hardware/watchdog.h"
21
+
22
+#include "config.h"
23
+#include "util.h"
24
+#include "console.h"
25
+#include "log.h"
26
+#include "usb.h"
27
+#include "buttons.h"
28
+#include "lcd.h"
29
+#include "main.h"
30
+
31
+void main_loop_hw(void) {
32
+    watchdog_update();
33
+    usb_run();
34
+    heartbeat_run();
35
+}
36
+
37
+int main(void) {
38
+    watchdog_enable(WATCHDOG_PERIOD_MS, 1);
39
+
40
+    // detect hardware type
41
+    // TODO
42
+
43
+    // required for debug console
44
+    cnsl_init();
45
+    usb_init();
46
+    debug("init");
47
+
48
+    if (watchdog_caused_reboot()) {
49
+        debug("reset by watchdog");
50
+    }
51
+
52
+    buttons_init();
53
+
54
+    debug("go");
55
+
56
+    while (1) {
57
+        main_loop_hw();
58
+        buttons_run();
59
+        cnsl_run();
60
+    }
61
+
62
+    return 0;
63
+}

+ 112
- 0
firmware/src/ring.c View File

@@ -0,0 +1,112 @@
1
+/*
2
+ * ring.c
3
+ *
4
+ * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#define MIN(x, y) ((x < y) ? x : y)
20
+
21
+#include <string.h>
22
+
23
+#include "config.h"
24
+#include "ring.h"
25
+
26
+void rb_add(struct ring_buffer *rb, const void *data, size_t length) {
27
+    for (size_t i = 0; i < length; i++) {
28
+        memcpy(rb->buffer + rb->head * rb->el_len, data + i * rb->el_len, rb->el_len);
29
+
30
+        if (rb->full && (++(rb->tail) == rb->size)) {
31
+            rb->tail = 0;
32
+        }
33
+
34
+        if (++(rb->head) == rb->size) {
35
+            rb->head = 0;
36
+        }
37
+
38
+        rb->full = ((rb->head) == (rb->tail));
39
+    }
40
+}
41
+
42
+size_t rb_len(struct ring_buffer *rb) {
43
+    if (rb->head == rb->tail) {
44
+        if (rb->full) {
45
+            return rb->size;
46
+        } else {
47
+            return 0;
48
+        }
49
+    } else if (rb->head > rb->tail) {
50
+        return rb->head - rb->tail;
51
+    } else {
52
+        return rb->size - rb->tail + rb->head;
53
+    }
54
+}
55
+
56
+void rb_dump(struct ring_buffer *rb, void (*write)(const void *, size_t), size_t skip) {
57
+    if (rb_len(rb) <= skip) {
58
+        return;
59
+    }
60
+
61
+    if (rb->head > rb->tail) {
62
+        if ((rb->head - rb->tail) > skip) {
63
+            write(rb->buffer + ((rb->tail + skip) * rb->el_len), rb->head - rb->tail - skip);
64
+        }
65
+    } else {
66
+        if ((rb->size - rb->tail) > skip) {
67
+            write(rb->buffer + ((rb->tail + skip) * rb->el_len), rb->size - rb->tail - skip);
68
+        }
69
+
70
+        skip -= MIN(skip, rb->size - rb->tail);
71
+        if (rb->head > skip) {
72
+            write(rb->buffer + (skip + rb->el_len), rb->head - skip);
73
+        }
74
+    }
75
+}
76
+
77
+void rb_move(struct ring_buffer *rb, void (*write)(const void *, size_t)) {
78
+    rb_dump(rb, write, 0);
79
+    rb->head = 0;
80
+    rb->tail = 0;
81
+    rb->full = false;
82
+}
83
+
84
+void rb_peek(struct ring_buffer *rb, void *buf) {
85
+    if (rb_len(rb) == 0) {
86
+        return;
87
+    }
88
+
89
+    memcpy(buf, rb->buffer + rb->tail * rb->el_len, rb->el_len);
90
+}
91
+
92
+void rb_pop(struct ring_buffer *rb, void *buf) {
93
+    if (rb_len(rb) == 0) {
94
+        return;
95
+    }
96
+
97
+    memcpy(buf, rb->buffer + rb->tail * rb->el_len, rb->el_len);
98
+    rb->tail++;
99
+    if (rb->tail >= rb->size) {
100
+        rb->tail = 0;
101
+    }
102
+}
103
+
104
+size_t rb_get(struct ring_buffer *rb, void *data, size_t length) {
105
+    size_t count = 0;
106
+    while ((length > 0) && (rb_len(rb) > 0)) {
107
+        rb_pop(rb, data + count * rb->el_len);
108
+        count++;
109
+        length--;
110
+    }
111
+    return count;
112
+}

+ 70
- 0
firmware/src/usb.c View File

@@ -0,0 +1,70 @@
1
+/*
2
+ * Extended from TinyUSB example code.
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ * THE SOFTWARE.
27
+ *
28
+ */
29
+
30
+#include "bsp/board.h"
31
+#include "tusb.h"
32
+
33
+#include "config.h"
34
+#include "log.h"
35
+#include "usb_descriptors.h"
36
+#include "usb_cdc.h"
37
+#include "usb.h"
38
+
39
+void usb_init(void) {
40
+    usb_descriptor_init_id();
41
+
42
+    board_init();
43
+    tusb_init();
44
+}
45
+
46
+void usb_run(void) {
47
+    tud_task();
48
+}
49
+
50
+// Invoked when device is mounted
51
+void tud_mount_cb(void) {
52
+    debug("device mounted");
53
+}
54
+
55
+// Invoked when device is unmounted
56
+void tud_umount_cb(void) {
57
+    debug("device unmounted");
58
+}
59
+
60
+// Invoked when usb bus is suspended
61
+// remote_wakeup_en : if host allow us  to perform remote wakeup
62
+// Within 7ms, device must draw an average of current less than 2.5 mA from bus
63
+void tud_suspend_cb(bool remote_wakeup_en) {
64
+    debug("device suspended wakeup=%d", remote_wakeup_en);
65
+}
66
+
67
+// Invoked when usb bus is resumed
68
+void tud_resume_cb(void) {
69
+    debug("device resumed");
70
+}

+ 115
- 0
firmware/src/usb_cdc.c View File

@@ -0,0 +1,115 @@
1
+/*
2
+ * Extended from TinyUSB example code.
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ * THE SOFTWARE.
27
+ *
28
+ */
29
+
30
+#include "bsp/board.h"
31
+#include "tusb.h"
32
+
33
+#include "config.h"
34
+#include "console.h"
35
+#include "log.h"
36
+#include "util.h"
37
+#include "usb_descriptors.h"
38
+#include "usb_cdc.h"
39
+
40
+static bool reroute_cdc_debug = false;
41
+
42
+void usb_cdc_write(const void *buf, size_t count) {
43
+#ifndef DISABLE_CDC_DTR_CHECK
44
+    if (!tud_cdc_connected()) {
45
+        return;
46
+    }
47
+#endif // DISABLE_CDC_DTR_CHECK
48
+
49
+    // implemented similar to Pico SDK stdio usb
50
+    uint32_t len = 0;
51
+    while (len < count) {
52
+        uint32_t n = count - len;
53
+        uint32_t available = tud_cdc_write_available();
54
+
55
+        // only write as much as possible
56
+        if (n > available) {
57
+            n = available;
58
+        }
59
+
60
+        len += tud_cdc_write(buf + len, n);
61
+
62
+        // run tud_task to actually move stuff from FIFO
63
+        tud_task();
64
+        tud_cdc_write_flush();
65
+    }
66
+}
67
+
68
+void usb_cdc_set_reroute(bool reroute) {
69
+    reroute_cdc_debug = reroute;
70
+}
71
+
72
+static void cdc_task(void) {
73
+    const uint32_t cdc_buf_len = 64;
74
+
75
+    if (tud_cdc_available()) {
76
+        char buf[cdc_buf_len + 1];
77
+        uint32_t count = tud_cdc_read(buf, cdc_buf_len);
78
+
79
+        if ((count >= 1) && (buf[0] == ENTER_BOOTLOADER_MAGIC)) {
80
+            reset_to_bootloader();
81
+        } else if (reroute_cdc_debug) {
82
+            debug_handle_input((const uint8_t *)buf, count);
83
+        } else {
84
+            cnsl_handle_input((const uint8_t *)buf, count);
85
+        }
86
+    }
87
+}
88
+
89
+// invoked when cdc when line state changed e.g connected/disconnected
90
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
91
+    (void) itf;
92
+    (void) rts;
93
+
94
+    static bool last_dtr = false;
95
+
96
+    if (dtr && !last_dtr) {
97
+        // clear left-over console input
98
+        cnsl_init();
99
+
100
+        // show past history
101
+        log_dump_to_usb();
102
+
103
+        debug("terminal connected");
104
+    } else if (!dtr && last_dtr) {
105
+        debug("terminal disconnected");
106
+    }
107
+
108
+    last_dtr = dtr;
109
+}
110
+
111
+// invoked when CDC interface received data from host
112
+void tud_cdc_rx_cb(uint8_t itf) {
113
+    (void) itf;
114
+    cdc_task();
115
+}

+ 229
- 0
firmware/src/usb_descriptors.c View File

@@ -0,0 +1,229 @@
1
+/*
2
+ * Extended from TinyUSB example code.
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ * THE SOFTWARE.
27
+ *
28
+ */
29
+
30
+#include "tusb.h"
31
+
32
+#include "config.h"
33
+#include "usb_descriptors.h"
34
+
35
+/*
36
+ * A combination of interfaces must have a unique product id,
37
+ * since PC will save device driver after the first plug.
38
+ * Same VID/PID with different interface e.g MSC (first),
39
+ * then CDC (later) will possibly cause system error on PC.
40
+ *
41
+ * Auto ProductID layout's Bitmap:
42
+ *   [MSB]         HID | MSC | CDC          [LSB]
43
+ */
44
+#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
45
+#define USB_PID (0x2300 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) \
46
+    | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
47
+
48
+#define USB_VID   0xCafe
49
+#define USB_BCD   0x0200
50
+
51
+//--------------------------------------------------------------------+
52
+// Device Descriptors
53
+//--------------------------------------------------------------------+
54
+
55
+tusb_desc_device_t const desc_device =
56
+{
57
+    .bLength            = sizeof(tusb_desc_device_t),
58
+    .bDescriptorType    = TUSB_DESC_DEVICE,
59
+    .bcdUSB             = USB_BCD,
60
+
61
+    /*
62
+     * Use Interface Association Descriptor (IAD) for CDC
63
+     * As required by USB Specs IAD's subclass must be common class (2)
64
+     * and protocol must be IAD (1)
65
+     */
66
+    .bDeviceClass       = TUSB_CLASS_MISC,
67
+    .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
68
+    .bDeviceProtocol    = MISC_PROTOCOL_IAD,
69
+
70
+    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
71
+
72
+    .idVendor           = USB_VID,
73
+    .idProduct          = USB_PID,
74
+    .bcdDevice          = 0x0100,
75
+
76
+    .iManufacturer      = 0x01,
77
+    .iProduct           = 0x02,
78
+    .iSerialNumber      = 0x03,
79
+
80
+    .bNumConfigurations = 0x01
81
+};
82
+
83
+// Invoked when received GET DEVICE DESCRIPTOR
84
+// Application return pointer to descriptor
85
+uint8_t const * tud_descriptor_device_cb(void) {
86
+    return (uint8_t const *) &desc_device;
87
+}
88
+
89
+//--------------------------------------------------------------------+
90
+// Configuration Descriptor
91
+//--------------------------------------------------------------------+
92
+
93
+enum {
94
+    ITF_NUM_CDC = 0,
95
+    ITF_NUM_CDC_DATA,
96
+    //ITF_NUM_MIDI,
97
+    //ITF_NUM_MIDI_STREAMING,
98
+    ITF_NUM_TOTAL
99
+};
100
+
101
+#define  CONFIG_TOTAL_LEN  (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN /*+ TUD_MIDI_DESC_LEN*/)
102
+
103
+#define EPNUM_CDC_NOTIF 0x82
104
+#define EPNUM_CDC_OUT   0x02
105
+#define EPNUM_CDC_IN    0x83
106
+#define EPNUM_MIDI_OUT  0x03
107
+#define EPNUM_MIDI_IN   0x84
108
+
109
+uint8_t const desc_configuration[] = {
110
+    // Config number, interface count, string index, total length, attribute, power in mA
111
+    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
112
+
113
+    // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
114
+    TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
115
+
116
+    // Interface number, string index, EP Out & EP In address, EP size
117
+    //TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 5, EPNUM_MIDI_OUT, EPNUM_MIDI_IN, 64),
118
+};
119
+
120
+#if TUD_OPT_HIGH_SPEED
121
+// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
122
+
123
+// other speed configuration
124
+uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
125
+
126
+// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
127
+tusb_desc_device_qualifier_t const desc_device_qualifier = {
128
+    .bLength            = sizeof(tusb_desc_device_qualifier_t),
129
+    .bDescriptorType    = TUSB_DESC_DEVICE_QUALIFIER,
130
+    .bcdUSB             = USB_BCD,
131
+
132
+    .bDeviceClass       = TUSB_CLASS_MISC,
133
+    .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
134
+    .bDeviceProtocol    = MISC_PROTOCOL_IAD,
135
+
136
+    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
137
+    .bNumConfigurations = 0x01,
138
+    .bReserved          = 0x00
139
+};
140
+
141
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
142
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
143
+// device_qualifier descriptor describes information about a high-speed capable device that would
144
+// change if the device were operating at the other speed. If not highspeed capable stall this request.
145
+uint8_t const* tud_descriptor_device_qualifier_cb(void) {
146
+    return (uint8_t const*) &desc_device_qualifier;
147
+}
148
+
149
+// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
150
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
151
+// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
152
+uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
153
+    (void) index; // for multiple configurations
154
+
155
+    // other speed config is basically configuration with type = OHER_SPEED_CONFIG
156
+    memcpy(desc_other_speed_config, desc_configuration, CONFIG_TOTAL_LEN);
157
+    desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
158
+
159
+    // this example use the same configuration for both high and full speed mode
160
+    return desc_other_speed_config;
161
+}
162
+
163
+#endif // highspeed
164
+
165
+// Invoked when received GET CONFIGURATION DESCRIPTOR
166
+// Application return pointer to descriptor
167
+// Descriptor contents must exist long enough for transfer to complete
168
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
169
+    (void) index; // for multiple configurations
170
+
171
+    // This example use the same configuration for both high and full speed mode
172
+    return desc_configuration;
173
+}
174
+
175
+//--------------------------------------------------------------------+
176
+// String Descriptors
177
+//--------------------------------------------------------------------+
178
+
179
+char string_pico_serial[2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1];
180
+
181
+void usb_descriptor_init_id(void) {
182
+    pico_get_unique_board_id_string(string_pico_serial, sizeof(string_pico_serial));
183
+}
184
+
185
+// array of pointer to string descriptors
186
+char const* string_desc_arr [] = {
187
+    (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
188
+    "xythobuz",                           // 1: Manufacturer
189
+    "Dispensy",                           // 2: Product
190
+    string_pico_serial,                   // 3: Serials, should use chip ID
191
+    "Debug Serial",                       // 4: CDC Interface
192
+    //"LARS Midi",                          // 5: MIDI Interface
193
+};
194
+
195
+static uint16_t _desc_str[32];
196
+
197
+// Invoked when received GET STRING DESCRIPTOR request
198
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
199
+uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
200
+    (void) langid;
201
+
202
+    uint8_t chr_count;
203
+
204
+    if ( index == 0) {
205
+        memcpy(&_desc_str[1], string_desc_arr[0], 2);
206
+        chr_count = 1;
207
+    } else {
208
+        // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
209
+        // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
210
+
211
+        if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
212
+
213
+        const char* str = string_desc_arr[index];
214
+
215
+        // Cap at max char
216
+        chr_count = strlen(str);
217
+        if ( chr_count > 31 ) chr_count = 31;
218
+
219
+        // Convert ASCII string into UTF-16
220
+        for(uint8_t i=0; i<chr_count; i++) {
221
+            _desc_str[1+i] = str[i];
222
+        }
223
+    }
224
+
225
+    // first byte is length (including header), second byte is string type
226
+    _desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
227
+
228
+    return _desc_str;
229
+}

+ 106
- 0
firmware/src/util.c View File

@@ -0,0 +1,106 @@
1
+/*
2
+ * util.c
3
+ *
4
+ * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
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
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include <string.h>
20
+#include "pico/stdlib.h"
21
+#include "pico/bootrom.h"
22
+#include "hardware/watchdog.h"
23
+
24
+#ifdef CYW43_WL_GPIO_LED_PIN
25
+#include "pico/cyw43_arch.h"
26
+#endif // CYW43_WL_GPIO_LED_PIN
27
+
28
+#include "config.h"
29
+#include "log.h"
30
+#include "util.h"
31
+
32
+#define HEARTBEAT_INTERVAL_MS 500
33
+
34
+void heartbeat_init(void) {
35
+#ifdef PICO_DEFAULT_LED_PIN
36
+    gpio_init(PICO_DEFAULT_LED_PIN);
37
+    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
38
+    gpio_put(PICO_DEFAULT_LED_PIN, 1);
39
+#endif // PICO_DEFAULT_LED_PIN
40
+#ifdef CYW43_WL_GPIO_LED_PIN
41
+    cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
42
+#endif // CYW43_WL_GPIO_LED_PIN
43
+}
44
+
45
+void heartbeat_run(void) {
46
+#if defined(PICO_DEFAULT_LED_PIN) || defined(CYW43_WL_GPIO_LED_PIN)
47
+    static uint32_t last_heartbeat = 0;
48
+    uint32_t now = to_ms_since_boot(get_absolute_time());
49
+    if (now >= (last_heartbeat + HEARTBEAT_INTERVAL_MS)) {
50
+        last_heartbeat = now;
51
+#ifdef PICO_DEFAULT_LED_PIN
52
+        gpio_xor_mask(1 << PICO_DEFAULT_LED_PIN);
53
+#endif // PICO_DEFAULT_LED_PIN
54
+#ifdef CYW43_WL_GPIO_LED_PIN
55
+        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, !cyw43_arch_gpio_get(CYW43_WL_GPIO_LED_PIN));
56
+#endif // CYW43_WL_GPIO_LED_PIN
57
+    }
58
+#endif // defined(PICO_DEFAULT_LED_PIN) || defined(CYW43_WL_GPIO_LED_PIN)
59
+}
60
+
61
+bool str_startswith(const char *str, const char *start) {
62
+    size_t l = strlen(start);
63
+    if (l > strlen(str)) {
64
+        return false;
65
+    }
66
+    return (strncmp(str, start, l) == 0);
67
+}
68
+
69
+int32_t convert_two_complement(int32_t b) {
70
+    if (b & 0x8000) {
71
+        b = -1 * ((b ^ 0xffff) + 1);
72
+    }
73
+    return b;
74
+}
75
+
76
+void reset_to_bootloader(void) {
77
+#ifdef PICO_DEFAULT_LED_PIN
78
+    reset_usb_boot(1 << PICO_DEFAULT_LED_PIN, 0);
79
+#else // ! PICO_DEFAULT_LED_PIN
80
+    reset_usb_boot(0, 0);
81
+#endif // PICO_DEFAULT_LED_PIN
82
+}
83
+
84
+void reset_to_main(void) {
85
+    watchdog_enable(1, false);
86
+    while (1);
87
+}
88
+
89
+void hexdump(const uint8_t *buff, size_t len) {
90
+    for (size_t i = 0; i < len; i += 16) {
91
+        for (size_t j = 0; (j < 16) && ((i + j) < len); j++) {
92
+            print("0x%02X", buff[i + j]);
93
+            if ((j < 15) && ((i + j) < (len - 1))) {
94
+                print(" ");
95
+            }
96
+        }
97
+        println();
98
+    }
99
+}
100
+
101
+float map(float value, float leftMin, float leftMax, float rightMin, float rightMax) {
102
+    float leftSpan = leftMax - leftMin;
103
+    float rightSpan = rightMax - rightMin;
104
+    float valueScaled = (value - leftMin) / leftSpan;
105
+    return rightMin + (valueScaled * rightSpan);
106
+}

+ 1
- 0
hardware/.gitignore View File

@@ -7,3 +7,4 @@ replicate_layout.log
7 7
 fabrication
8 8
 plot
9 9
 fab.zip
10
+jlcpcb

+ 1
- 1
hardware/README.md View File

@@ -1,6 +1,6 @@
1 1
 # Hardware
2 2
 
3
-This is a KiCAD 7 hierarchical schematic and layout.
3
+This is a KiCAD 8 hierarchical schematic and layout.
4 4
 Take a look at the [docs](https://docs.kicad.org/7.0/en/eeschema/eeschema.html#hierarchical-schematics).
5 5
 
6 6
 The repeated schematic sheets can also be replicated in the layout using [this plugin](https://github.com/MitjaNemec/ReplicateLayout).

Loading…
Cancel
Save