Browse Source

first building but not running state of C Pico SDK version

Thomas Buck 6 months ago
parent
commit
0b7fcb080b
39 changed files with 2835 additions and 3 deletions
  1. 7
    0
      .gitignore
  2. 12
    0
      .gitmodules
  3. 112
    0
      CMakeLists.txt
  4. 106
    3
      README.md
  5. 33
    0
      case/case.scad
  6. 245
    0
      case/pico_case.scad
  7. 18
    0
      debug.sh
  8. 21
    0
      debug_swd.sh
  9. 1
    0
      fatfs
  10. 28
    0
      flash.sh
  11. 4
    0
      flash_swd.sh
  12. 58
    0
      include/btstack_config.h
  13. 39
    0
      include/buttons.h
  14. 25
    0
      include/config.h
  15. 26
    0
      include/console.h
  16. 28
    0
      include/debug.h
  17. 24
    0
      include/fat_disk.h
  18. 296
    0
      include/ffconf.h
  19. 44
    0
      include/log.h
  20. 125
    0
      include/tusb_config.h
  21. 25
    0
      include/usb.h
  22. 25
    0
      include/usb_cdc.h
  23. 24
    0
      include/usb_descriptors.h
  24. 25
    0
      include/usb_msc.h
  25. 34
    0
      include/util.h
  26. 1
    0
      mcufont
  27. 1
    0
      pico-sdk
  28. 72
    0
      src/buttons.c
  29. 192
    0
      src/console.c
  30. 61
    0
      src/debug.c
  31. 170
    0
      src/fat_disk.c
  32. 169
    0
      src/log.c
  33. 59
    0
      src/main.c
  34. 70
    0
      src/usb.c
  35. 117
    0
      src/usb_cdc.c
  36. 229
    0
      src/usb_descriptors.c
  37. 215
    0
      src/usb_msc.c
  38. 93
    0
      src/util.c
  39. 1
    0
      st7789

+ 7
- 0
.gitignore View File

@@ -1,2 +1,9 @@
1 1
 web-app/*.js
2
+web-app/*/*.js
2 3
 __pycache__
4
+build
5
+build_debug
6
+*.stl
7
+*.sl1
8
+*.wow
9
+.directory

+ 12
- 0
.gitmodules View File

@@ -0,0 +1,12 @@
1
+[submodule "pico-sdk"]
2
+	path = pico-sdk
3
+	url = https://github.com/raspberrypi/pico-sdk
4
+[submodule "fatfs"]
5
+	path = fatfs
6
+	url = https://github.com/abbrev/fatfs
7
+[submodule "mcufont"]
8
+	path = mcufont
9
+	url = https://github.com/mcufont/mcufont
10
+[submodule "st7789"]
11
+	path = st7789
12
+	url = https://github.com/hepingood/st7789

+ 112
- 0
CMakeLists.txt View File

@@ -0,0 +1,112 @@
1
+cmake_minimum_required(VERSION 3.13)
2
+
3
+# build MCUFont encoder host tool and convert included example fonts
4
+execute_process(COMMAND make
5
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/mcufont/encoder
6
+)
7
+execute_process(COMMAND make
8
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/mcufont/fonts
9
+)
10
+
11
+# initialize pico-sdk from submodule
12
+include(pico-sdk/pico_sdk_init.cmake)
13
+
14
+project(gadget C CXX)
15
+set(CMAKE_C_STANDARD 11)
16
+set(CMAKE_CXX_STANDARD 17)
17
+
18
+# initialize the Raspberry Pi Pico SDK
19
+pico_sdk_init()
20
+
21
+# copy FatFS source files to build dir, so we can use our own ffconf.h
22
+configure_file(
23
+    ${CMAKE_CURRENT_SOURCE_DIR}/fatfs/source/ff.c
24
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ff.c
25
+    COPYONLY
26
+)
27
+configure_file(
28
+    ${CMAKE_CURRENT_SOURCE_DIR}/fatfs/source/ff.h
29
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ff.h
30
+    COPYONLY
31
+)
32
+configure_file(
33
+    ${CMAKE_CURRENT_SOURCE_DIR}/fatfs/source/diskio.h
34
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/diskio.h
35
+    COPYONLY
36
+)
37
+configure_file(
38
+    ${CMAKE_CURRENT_SOURCE_DIR}/fatfs/source/ffunicode.c
39
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ffunicode.c
40
+    COPYONLY
41
+)
42
+
43
+add_executable(gadget)
44
+
45
+target_sources(gadget PUBLIC
46
+    src/main.c
47
+    src/console.c
48
+    src/log.c
49
+    src/util.c
50
+    src/usb.c
51
+    src/usb_cdc.c
52
+    src/usb_descriptors.c
53
+    src/usb_msc.c
54
+    src/fat_disk.c
55
+    src/debug.c
56
+    src/buttons.c
57
+
58
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ff.c
59
+    ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ffunicode.c
60
+
61
+    st7789/src/driver_st7789.c
62
+
63
+    mcufont/decoder/mf_encoding.c
64
+    mcufont/decoder/mf_font.c
65
+    mcufont/decoder/mf_justify.c
66
+    mcufont/decoder/mf_kerning.c
67
+    mcufont/decoder/mf_rlefont.c
68
+    mcufont/decoder/mf_bwfont.c
69
+    mcufont/decoder/mf_scaledfont.c
70
+    mcufont/decoder/mf_wordwrap.c
71
+)
72
+
73
+# external dependency include directories
74
+target_include_directories(gadget PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
75
+target_include_directories(gadget PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/fatfs)
76
+target_include_directories(gadget PUBLIC ${CMAKE_CURRENT_LIST_DIR}/st7789/src)
77
+target_include_directories(gadget PUBLIC ${CMAKE_CURRENT_LIST_DIR}/mcufont/decoder)
78
+target_include_directories(gadget PUBLIC ${CMAKE_CURRENT_LIST_DIR}/mcufont/fonts)
79
+
80
+# enable generous warnings
81
+target_compile_options(gadget PUBLIC
82
+    -Wall
83
+    -Wextra
84
+    -Werror
85
+)
86
+
87
+# suppress some warnings for borked 3rd party files in Pico SDK
88
+set_source_files_properties(pico-sdk/lib/btstack/src/ble/sm.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
89
+set_source_files_properties(pico-sdk/lib/btstack/src/btstack_hid_parser.c PROPERTIES COMPILE_FLAGS -Wno-maybe-uninitialized)
90
+set_source_files_properties(pico-sdk/src/rp2_common/pico_cyw43_driver/cyw43_driver.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
91
+
92
+# pull in common dependencies
93
+target_link_libraries(gadget
94
+    pico_stdlib
95
+    pico_unique_id
96
+    tinyusb_device
97
+    tinyusb_board
98
+    hardware_spi
99
+    pico_btstack_ble
100
+    pico_btstack_cyw43
101
+    pico_cyw43_arch_none
102
+)
103
+
104
+target_compile_definitions(gadget PUBLIC
105
+    RUNNING_AS_CLIENT=1
106
+)
107
+
108
+# fix for Errata RP2040-E5 (the fix requires use of GPIO 15)
109
+target_compile_definitions(gadget PUBLIC PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1)
110
+
111
+# create map/bin/hex/uf2 file etc.
112
+pico_add_extra_outputs(gadget)

+ 106
- 3
README.md View File

@@ -1,12 +1,115 @@
1 1
 # Pi Pico Volcano Remote Control Gadget
2 2
 
3
-https://www.waveshare.com/wiki/Pico-LCD-1.3
3
+For use with Raspberry Pi Pico W boards with the [Waveshare Pico LCD 1.3](https://www.waveshare.com/wiki/Pico-LCD-1.3) and the [Pimoroni Pico Lipo Shim](https://shop.pimoroni.com/products/pico-lipo-shim).
4 4
 
5
-https://shop.pimoroni.com/products/pico-lipo-shim
5
+Adapted from the [tinyusb-cdc-example](https://github.com/hathach/tinyusb/blob/master/examples/device/cdc_msc/src/main.c).
6
+
7
+TODO other examples used
8
+
9
+## Quick Start
10
+
11
+When compiling for the first time, check out the required git submodules.
12
+
13
+    git submodule update --init
14
+    cd pico-sdk
15
+    git submodule update --init
16
+
17
+Then do this to build.
18
+
19
+    mkdir build
20
+    cd build
21
+    cmake -DPICO_BOARD=pico_w ..
22
+    make -j4 gadget
23
+
24
+And flash the resulting `gadget.uf2` file to your Pico as usual.
25
+
26
+For convenience you can use the included `flash.sh`, as long as you flashed the binary manually once before.
27
+
28
+    make -j4 gadget
29
+    ../flash.sh gadget.uf2
30
+
31
+This will use the mass storage bootloader to upload a new uf2 image.
32
+
33
+For old-school debugging a serial port will be presented by the firmware.
34
+Open it using eg. `picocom`, or with the included `debug.sh` script.
35
+
36
+For dependencies to compile, on Arch install these.
37
+
38
+    sudo pacman -S arm-none-eabi-gcc arm-none-eabi-newlib picocom cmake
39
+
40
+## Proper Debugging
41
+
42
+You can also use the SWD interface for proper hardware debugging.
43
+
44
+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.
45
+
46
+For ease of reading the disassembly, create a debug build.
47
+
48
+    mkdir build_debug
49
+    cd build_debug
50
+    cmake -DPICO_BOARD=pico_w -DCMAKE_BUILD_TYPE=Debug ..
51
+    make -j4 gadget
52
+
53
+You need a hardware SWD probe.
54
+This can be made from another Pico, see Appendix A in the document linked above.
55
+For this you need to compile the `picoprobe` firmware, like this.
56
+
57
+    git clone https://github.com/raspberrypi/picoprobe.git
58
+    cd picoprobe
59
+    git submodule update --init
60
+    mkdir build
61
+    cd build
62
+    PICO_SDK_PATH=../../../pico-sdk cmake ..
63
+    make -j4
64
+
65
+And flash the resulting `picoprobe.uf2` to your probe.
66
+Connect `GP2` of the probe to `SWCLK` of the target and `GP3` of the probe to `SWDIO` of the target.
67
+Of course you also need to connect GND between both.
68
+
69
+You need some dependencies, mainly `gdb-multiarch` and the RP2040 fork of `OpenOCD`.
70
+
71
+    sudo apt install gdb-multiarch   # Debian / Ubuntu
72
+    sudo pacman -S arm-none-eabi-gdb # Arch Linux
73
+
74
+    cd ../.. # back to build_debug directory from before
75
+
76
+    git clone https://github.com/raspberrypi/openocd.git --branch rp2040 --recursive --depth=1
77
+    cd openocd
78
+
79
+    # install udev rules
80
+    sudo cp contrib/60-openocd.rules /etc/udev/rules.d
81
+    sudo udevadm control --reload-rules && sudo udevadm trigger
82
+
83
+    ./bootstrap
84
+    ./configure --enable-ftdi --enable-sysfsgpio --enable-bcm2835gpio
85
+    make -j4
86
+
87
+Now we can flash a firmware image via OpenOCD.
88
+
89
+    ./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"
90
+
91
+And also start a GDB debugging session.
92
+
93
+    ./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"
94
+    arm-none-eabi-gdb gadget.elf
95
+    target extended-remote localhost:3333
96
+
97
+These commands have also been put in the `flash_swd.sh` and `debug_swd.sh` scripts, respectively.
98
+Call them from the `build_debug` folder where you checked out and built OpenOCD.
6 99
 
7 100
 ## License
8 101
 
9
-This project is licensed as GPLv3.
102
+The firmware itself is licensed as GPLv3.
103
+I initially adapted it from my own [Trackball](https://git.xythobuz.de/thomas/Trackball) project.
104
+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.
105
+Some code is adapted from the TinyUSB examples.
106
+And the project uses the [FatFS library](https://github.com/abbrev/fatfs), licensed as BSD 1-clause.
107
+Also included are the [MCUFont library](https://github.com/mcufont/mcufont) and the [st7789 library](https://github.com/hepingood/st7789), both licensed under the MIT license.
108
+It also uses the [BTstack](https://github.com/bluekitchen/btstack/blob/master/LICENSE) included with the Pico SDK, following their [license terms](https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_btstack/LICENSE.RP).
109
+
110
+The case design is also licensed as GPLv3.
111
+It uses a [Pi Pico case model](https://www.printables.com/model/210898-raspberry-pi-pico-case) licensed as CC-BY-NC-SA.
112
+But this is only used for visualization purposes and doesn't influence the 3D model at all.
10 113
 
11 114
     This program is free software: you can redistribute it and/or modify
12 115
     it under the terms of the GNU General Public License as published by

+ 33
- 0
case/case.scad View File

@@ -0,0 +1,33 @@
1
+// https://www.waveshare.com/wiki/Pico-LCD-1.3
2
+lcd_w = 52.0;
3
+lcd_h = 26.5;
4
+
5
+lcd_d = 1.0; // todo
6
+
7
+// https://www.printables.com/model/210898-raspberry-pi-pico-case
8
+use <pico_case.scad>
9
+p_w = 21;
10
+p_h = 51;
11
+p_t = 1.0;
12
+
13
+pico_header_h = 2.0; // todo
14
+lcd_header_h = 10.0; // todo
15
+
16
+header_h = pico_header_h + lcd_header_h;
17
+
18
+module lcd() {
19
+    color("blue")
20
+    translate([-lcd_h / 2, -lcd_w / 2, 0])
21
+    cube([lcd_h, lcd_w, lcd_d]);
22
+}
23
+
24
+module hw() {
25
+    translate([0, 0, header_h])
26
+    lcd();
27
+    
28
+    translate([p_w / 2, -p_h / 2, 0])
29
+    rotate([0, 180, 0])
30
+    pico();
31
+}
32
+
33
+hw();

+ 245
- 0
case/pico_case.scad View File

@@ -0,0 +1,245 @@
1
+resolution = 20; //[10, 20, 30, 50, 100]
2
+$fn = resolution;
3
+
4
+show_frame = false; // [true, false]
5
+show_pico = true; // [true, false]
6
+show_lid = false; // [true, false]
7
+show_button = false; // [true, false]
8
+
9
+module rounded_plate(w, h, t, d) {
10
+    translate([0, (d/2), 0])
11
+    cube([w, (h-d), t]);
12
+    translate([(d/2), 0, 0])
13
+    cube([(w-d), h, t]);
14
+    translate([(d/2), (d/2), 0])
15
+    cylinder(h=t, d=d);
16
+    translate([w-(d/2), (d/2), 0])
17
+    cylinder(h=t, d=d);
18
+    translate([(d/2), h-(d/2), 0])
19
+    cylinder(h=t, d=d);
20
+    translate([w-(d/2), h-(d/2), 0])
21
+    cylinder(h=t, d=d);
22
+}
23
+
24
+module usb_port() {
25
+    translate([0.3, 0, 0])
26
+    color("grey")
27
+    difference() {
28
+        union() {
29
+            cube([7.4, 6, 2.7]);
30
+            translate([-0.3, 5.4, -0.1])
31
+            cube([8, 0.6, 2.9]);
32
+        }
33
+    }
34
+}
35
+
36
+module switch() {
37
+    color("grey")
38
+    cube([3.5, 4.5, 1]);
39
+    color("white")
40
+    translate([(3.5/2), (4.5/2), 0])
41
+    cylinder(h=1.5, d=3);
42
+}
43
+
44
+p_w = 21;
45
+p_h = 51;
46
+p_t = 1.0;
47
+os = 0.35;
48
+b = 2;
49
+
50
+module pico() {
51
+    translate([((p_w-8)/2), ((p_h+1.3)-6), p_t])
52
+    usb_port();
53
+
54
+    translate([5.25, 36.6, 0.6])
55
+    switch();
56
+
57
+    difference() {
58
+        color("green")
59
+        cube([p_w, p_h, 1]);
60
+
61
+        translate([((p_w/2)-5.7), 2, -0.01])
62
+        cylinder(h=p_t+0.02, d=2.1);
63
+       
64
+        translate([(p_w-((p_w/2)-5.7)), 2, -0.01])
65
+        cylinder(h=p_t+0.02, d=2.1);
66
+
67
+        translate([((p_w/2)-5.7), (p_h-2), -0.01])
68
+        cylinder(h=p_t+0.02, d=2.1);
69
+
70
+        translate([(p_w-((p_w/2)-5.7)), (p_h-2), -0.01])
71
+        cylinder(h=p_t+0.02, d=2.1);
72
+    }
73
+}
74
+
75
+if (show_pico) {
76
+    translate([(os+b), (os+b), 3.4])
77
+    pico();
78
+}
79
+
80
+if (show_frame) {
81
+    union() {
82
+        difference() {
83
+            // Outer frame
84
+            rounded_plate(p_w+(2*(os+b)), p_h+(2*(os+b)), 10, (2*(os+b)));
85
+            
86
+            // Board cut-out
87
+            translate([b, b, -1])
88
+            cube([p_w+(2*os), p_h+(2*os), 12]);
89
+            
90
+            // USB port cut-out
91
+//            translate([b+os+((p_w-10)/2), b+os+((p_h+1)-6), 3.4])
92
+//            cube([10, 8, 4.7]);
93
+            translate([b+os+((p_w-10)/2), b+(2*os)+p_h-1, 8.1])
94
+            rotate([-90, 0, 0])
95
+            rounded_plate(10, 4.7, 4.7, 2);
96
+            
97
+            // Lid release cut-out
98
+            difference() {
99
+                union() {
100
+                    translate([b+os+((p_w-10)/2), -1, 10-1])
101
+                    cube([10, 8, 2]);
102
+                    translate([b+os+((p_w-6)/2), -1, 10-1])
103
+                    rotate([-90, 0, 0])
104
+                    cylinder(h=4, d=2); 
105
+                    translate([b+os+((p_w-6)/2), -1, 10-2])
106
+                    cube([6, 8, 3]);
107
+                    translate([b+os+((p_w+6)/2), -1, 10-1])
108
+                    rotate([-90, 0, 0])
109
+                    cylinder(h=4, d=2); 
110
+                }
111
+                translate([b+os+((p_w+10)/2), -1, 10-1])
112
+                rotate([-90, 0, 0])
113
+                cylinder(h=4, d=2); 
114
+                translate([b+os+((p_w-10)/2), -1, 10-1])
115
+                rotate([-90, 0, 0])
116
+                cylinder(h=4, d=2); 
117
+            }
118
+            
119
+            // Outer frame
120
+            translate([1, 1, 10-1.2])
121
+            rounded_plate(p_w+(2*(os+b))-2, p_h+(2*(os+b))-2, 2, (2*(os+b))-2);
122
+
123
+            // Left top retainer cut-out
124
+            translate([b+os+(p_w/2)-(3.2/2)-7.1, b+p_h+(2*os), 10-3+0.2])
125
+            cube([3.2, 0.7, 1.0]);
126
+
127
+            // Right top retainer cut-out
128
+            translate([b+os+(p_w/2)-(3.2/2)+7.1, b+p_h+(2*os), 10-3+0.2])
129
+            cube([3.2, 0.7, 1.0]);
130
+
131
+            // Left bottom retainer cut-out
132
+            translate([b+os+(p_w/2)-(4/2)-7.1, b+os-0.7, 10-3+0.2])
133
+            cube([4, 0.7, 1.0]);
134
+
135
+            // Right bottom retainer cut-out
136
+            translate([b+os+(p_w/2)-(4/2)+7.1, b+os-0.7, 10-3+0.2])
137
+            cube([4, 0.7, 1.0]);
138
+
139
+            // Left retainer cut-out
140
+            translate([b-0.7, b+os+(p_h/2)-2, 10-3+0.2])
141
+            cube([0.7, 4, 1.0]);
142
+
143
+            // Right retainer cut-out
144
+            translate([b+(2*os)+p_w, b+os+(p_h/2)-2, 10-3+0.2])
145
+            cube([0.7, 4, 1.0]);
146
+        }
147
+        
148
+        // Left support rail
149
+        translate([b+os+(p_w/2)-(3/2)-5.5, 0, 0])
150
+        cube([3, p_h+(2*(os+b)), 3.4]);
151
+       
152
+        // Right support rail
153
+        translate([b+os+(p_w/2)-(3/2)+5.5, 0, 0])
154
+        cube([3, p_h+(2*(os+b)), 3.4]);
155
+
156
+        // Left top retainer
157
+        translate([b+os+(p_w/2)-(2.2/2)-6.1, b+p_h+(2*os)-0.6, 3.4+1+0.5])
158
+        cube([2.2, 0.6, 0.8]);
159
+
160
+        // Right top retainer
161
+        translate([b+os+(p_w/2)-(2.2/2)+6.1, b+p_h+(2*os)-0.6, 3.4+1+0.5])
162
+        cube([2.2, 0.6, 0.8]);
163
+
164
+        // Left bottom retainer
165
+        translate([b+os+(p_w/2)-(3/2)-5.7, b, 3.4+1+0.])
166
+        cube([3, 0.6, 0.8]);
167
+
168
+        // Right bottom retainer
169
+        translate([b+os+(p_w/2)-(3/2)+5.7, b, 3.4+1+0.5])
170
+        cube([3, 0.6, 0.8]);
171
+    }
172
+}
173
+
174
+if (show_lid) {
175
+    difference() {
176
+        union() {
177
+            translate([1+os, 1+os, 10-1.2])
178
+            rounded_plate(p_w+(2*b)-2, p_h+(2*b)-2, 1, (2*b)-2);
179
+            translate([(os+b), (os+b), 10-3])
180
+            rounded_plate(p_w, p_h, 2, (2*b)-3);
181
+            
182
+            // Left top retainer
183
+            translate([b+os+(p_w/2)-(2.2/2)-7.1, b+p_h+os, 10-3+0.4])
184
+            cube([2.2, 0.6, 0.6]);
185
+
186
+            // Right top retainer
187
+            translate([b+os+(p_w/2)-(2.2/2)+7.1, b+p_h+os, 10-3+0.4])
188
+            cube([2.2, 0.6, 0.6]);
189
+
190
+            // Left bottom retainer
191
+            translate([b+os+(p_w/2)-(3/2)-7.1, b+os-0.5, 10-3+0.3])
192
+            cube([3, 0.6, 0.6]);
193
+
194
+            // Right bottom retainer
195
+            translate([b+os+(p_w/2)-(3/2)+7.1, b+os-0.5, 10-3+0.3])
196
+            cube([3, 0.6, 0.6]);
197
+
198
+            // Left retainer
199
+            translate([b+os-0.6, b+os+(p_h/2)-1.5, 10-3+0.3])
200
+            cube([0.6, 3, 0.6]);            
201
+
202
+            // Right retainer
203
+            translate([b+os+p_w, b+os+(p_h/2)-1.5, 10-3+0.3])
204
+            cube([0.6, 3, 0.6]);            
205
+        }
206
+
207
+        // USB port cut-out
208
+        translate([b+os+((p_w-10)/2), b+p_h+(2*os)-2, 10-4.2])
209
+        cube([10, 8, 2.4]);
210
+
211
+        // Lid release cut-out
212
+        translate([b+os+((p_w-10)/2), -1, 10-4.2])
213
+        cube([10, 8, 3]);
214
+        
215
+        // Switch cut-out
216
+        translate([(os+b)+7, (os+b)+38.85, 0])
217
+        cylinder(h=10, d=5);
218
+        
219
+        // Inset
220
+        translate([(os+b)+1, (os+b)+1, 10-4])
221
+        cube([p_w-2, p_h-2, 3]);
222
+    }
223
+    // Switch surround
224
+    translate([(os+b), (os+b)+38.85-6, 10-2])
225
+    cube([p_w, 1, 1]);    
226
+
227
+    translate([(os+b), (os+b)+38.85+5, 10-2])
228
+    cube([p_w, 1, 1]);    
229
+
230
+}
231
+
232
+
233
+if (show_button) {
234
+    color("red")
235
+    union() {
236
+        translate([(os+b)+7, (os+b)+38.85, 7.5])
237
+        cylinder(h=2.3, d=4.4);
238
+        difference() {
239
+            translate([(os+b)+7, (os+b)+38.85, 6.5])
240
+            cylinder(h=2, d=7.6);
241
+            translate([(os+b)+7, (os+b)+38.85, 6.5])
242
+            cylinder(h=1, d=6.6);
243
+        }
244
+    }
245
+}

+ 18
- 0
debug.sh View File

@@ -0,0 +1,18 @@
1
+#!/bin/bash
2
+set -euo pipefail
3
+
4
+SERIAL=/dev/serial/by-id/usb-xythobuz_Trackball_*
5
+
6
+echo -n Waiting for serial port to appear
7
+until [ -e $SERIAL ]
8
+do
9
+    echo -n .
10
+    sleep 1
11
+done
12
+echo
13
+
14
+echo Opening picocom terminal
15
+echo "[C-a] [C-x] to exit"
16
+echo
17
+
18
+picocom -q --omap crcrlf $SERIAL

+ 21
- 0
debug_swd.sh View File

@@ -0,0 +1,21 @@
1
+#!/bin/bash
2
+set -euo pipefail
3
+
4
+echo Starting OpenOCD in background
5
+./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" &
6
+OPENOCD_PID=$!
7
+
8
+# give OpenOCD some time to output stuff
9
+sleep 1
10
+
11
+echo -n Waiting for debugger to appear
12
+while ! netstat -tna | grep 'LISTEN\>' | grep -q ':3333\>'; do
13
+    echo -n .
14
+    sleep 1
15
+done
16
+
17
+echo Starting GDB
18
+arm-none-eabi-gdb -ex "set history save" -ex "show print pretty" -ex "target extended-remote localhost:3333" $1
19
+
20
+echo Killing OpenOCD instance in background
21
+kill $OPENOCD_PID

+ 1
- 0
fatfs

@@ -0,0 +1 @@
1
+Subproject commit b11f08931929e5f2f1fe8a3a2c0bd16d222b5625

+ 28
- 0
flash.sh View File

@@ -0,0 +1,28 @@
1
+#!/bin/bash
2
+set -euo pipefail
3
+
4
+SERIAL=/dev/serial/by-id/usb-xythobuz_Trackball_*
5
+DISK=/dev/disk/by-label/RPI-RP2
6
+MOUNT=/mnt/tmp
7
+
8
+if [ ! -e $DISK ]
9
+then
10
+    echo Resetting Raspberry Pi Pico
11
+    echo -n -e "\\x18" > $SERIAL
12
+fi
13
+
14
+echo -n Waiting for disk to appear
15
+until [ -e $DISK ]
16
+do
17
+    echo -n .
18
+    sleep 1
19
+done
20
+echo
21
+
22
+echo Mounting bootloader disk
23
+sudo mount $DISK $MOUNT
24
+
25
+echo Copying binary
26
+sudo cp $1 $MOUNT
27
+
28
+echo Done

+ 4
- 0
flash_swd.sh View File

@@ -0,0 +1,4 @@
1
+#!/bin/bash
2
+set -euo pipefail
3
+
4
+./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 $1 verify reset exit"

+ 58
- 0
include/btstack_config.h View File

@@ -0,0 +1,58 @@
1
+#ifndef _PICO_BTSTACK_BTSTACK_CONFIG_H
2
+#define _PICO_BTSTACK_BTSTACK_CONFIG_H
3
+
4
+#ifndef ENABLE_BLE
5
+#error Please link to pico_btstack_ble
6
+#endif
7
+
8
+// BTstack features that can be enabled
9
+#define ENABLE_LE_PERIPHERAL
10
+#define ENABLE_LOG_INFO
11
+#define ENABLE_LOG_ERROR
12
+#define ENABLE_PRINTF_HEXDUMP
13
+
14
+// for the client
15
+#if RUNNING_AS_CLIENT
16
+#define ENABLE_LE_CENTRAL
17
+#define MAX_NR_GATT_CLIENTS 1
18
+#else
19
+#define MAX_NR_GATT_CLIENTS 0
20
+#endif
21
+
22
+// BTstack configuration. buffers, sizes, ...
23
+#define HCI_OUTGOING_PRE_BUFFER_SIZE 4
24
+#define HCI_ACL_PAYLOAD_SIZE (255 + 4)
25
+#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4
26
+#define MAX_NR_HCI_CONNECTIONS 1
27
+#define MAX_NR_SM_LOOKUP_ENTRIES 3
28
+#define MAX_NR_WHITELIST_ENTRIES 16
29
+#define MAX_NR_LE_DEVICE_DB_ENTRIES 16
30
+
31
+// Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun
32
+#define MAX_NR_CONTROLLER_ACL_BUFFERS 3
33
+#define MAX_NR_CONTROLLER_SCO_PACKETS 3
34
+
35
+// Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun
36
+#define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL
37
+#define HCI_HOST_ACL_PACKET_LEN (255+4)
38
+#define HCI_HOST_ACL_PACKET_NUM 3
39
+#define HCI_HOST_SCO_PACKET_LEN 120
40
+#define HCI_HOST_SCO_PACKET_NUM 3
41
+
42
+// Link Key DB and LE Device DB using TLV on top of Flash Sector interface
43
+#define NVM_NUM_DEVICE_DB_ENTRIES 16
44
+#define NVM_NUM_LINK_KEYS 16
45
+
46
+// We don't give btstack a malloc, so use a fixed-size ATT DB.
47
+#define MAX_ATT_DB_SIZE 512
48
+
49
+// BTstack HAL configuration
50
+#define HAVE_EMBEDDED_TIME_MS
51
+// map btstack_assert onto Pico SDK assert()
52
+#define HAVE_ASSERT
53
+// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
54
+#define HCI_RESET_RESEND_TIMEOUT_MS 1000
55
+#define ENABLE_SOFTWARE_AES128
56
+#define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS
57
+
58
+#endif // _PICO_BTSTACK_BTSTACK_CONFIG_H

+ 39
- 0
include/buttons.h View File

@@ -0,0 +1,39 @@
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
+enum buttons {
23
+    BTN_A,
24
+    BTN_B,
25
+    BTN_X,
26
+    BTN_Y,
27
+    BTN_UP,
28
+    BTN_DOWN,
29
+    BTN_LEFT,
30
+    BTN_RIGHT,
31
+    BTN_ENTER,
32
+    NUM_BTNS // count
33
+};
34
+
35
+void buttons_init(void);
36
+void buttons_run(void);
37
+
38
+#endif // __BUTTONS_H__
39
+

+ 25
- 0
include/config.h View File

@@ -0,0 +1,25 @@
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 DISABLE_CDC_DTR_CHECK
23
+#define DEBOUNCE_DELAY_MS 5
24
+
25
+#endif // __CONFIG_H__

+ 26
- 0
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 char *buf, uint32_t len);
25
+
26
+#endif // __CONSOLE_H__

+ 28
- 0
include/debug.h View File

@@ -0,0 +1,28 @@
1
+/*
2
+ * debug.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 __DEBUG_H__
20
+#define __DEBUG_H__
21
+
22
+int debug_msc_mount(void);
23
+int debug_msc_unmount(void);
24
+
25
+void debug_msc_stats(void);
26
+void debug_msc_pmw3360(void);
27
+
28
+#endif // __DEBUG_H__

+ 24
- 0
include/fat_disk.h View File

@@ -0,0 +1,24 @@
1
+/* 
2
+ * fat_disk.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
+#define DISK_BLOCK_COUNT 256
20
+#define DISK_BLOCK_SIZE 512
21
+
22
+void fat_disk_init(void);
23
+
24
+uint8_t *fat_disk_get_sector(uint32_t sector);

+ 296
- 0
include/ffconf.h View File

@@ -0,0 +1,296 @@
1
+/*---------------------------------------------------------------------------/
2
+/  Configurations of FatFs Module
3
+/---------------------------------------------------------------------------*/
4
+
5
+#define FFCONF_DEF	80286	/* Revision ID */
6
+
7
+/*---------------------------------------------------------------------------/
8
+/ Function Configurations
9
+/---------------------------------------------------------------------------*/
10
+
11
+#define FF_FS_READONLY	0
12
+/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
13
+/  Read-only configuration removes writing API functions, f_write(), f_sync(),
14
+/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
15
+/  and optional writing functions as well. */
16
+
17
+
18
+#define FF_FS_MINIMIZE	0
19
+/* This option defines minimization level to remove some basic API functions.
20
+/
21
+/   0: Basic functions are fully enabled.
22
+/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
23
+/      are removed.
24
+/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
25
+/   3: f_lseek() function is removed in addition to 2. */
26
+
27
+
28
+#define FF_USE_FIND		0
29
+/* This option switches filtered directory read functions, f_findfirst() and
30
+/  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
31
+
32
+
33
+#define FF_USE_MKFS		1
34
+/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
35
+
36
+
37
+#define FF_USE_FASTSEEK	0
38
+/* This option switches fast seek function. (0:Disable or 1:Enable) */
39
+
40
+
41
+#define FF_USE_EXPAND	0
42
+/* This option switches f_expand function. (0:Disable or 1:Enable) */
43
+
44
+
45
+#define FF_USE_CHMOD	0
46
+/* This option switches attribute manipulation functions, f_chmod() and f_utime().
47
+/  (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
48
+
49
+
50
+#define FF_USE_LABEL	1
51
+/* This option switches volume label functions, f_getlabel() and f_setlabel().
52
+/  (0:Disable or 1:Enable) */
53
+
54
+
55
+#define FF_USE_FORWARD	0
56
+/* This option switches f_forward() function. (0:Disable or 1:Enable) */
57
+
58
+
59
+#define FF_USE_STRFUNC	0
60
+#define FF_PRINT_LLI	1
61
+#define FF_PRINT_FLOAT	1
62
+#define FF_STRF_ENCODE	3
63
+/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
64
+/  f_printf().
65
+/
66
+/   0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
67
+/   1: Enable without LF-CRLF conversion.
68
+/   2: Enable with LF-CRLF conversion.
69
+/
70
+/  FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
71
+/  makes f_printf() support floating point argument. These features want C99 or later.
72
+/  When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
73
+/  encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
74
+/  to be read/written via those functions.
75
+/
76
+/   0: ANSI/OEM in current CP
77
+/   1: Unicode in UTF-16LE
78
+/   2: Unicode in UTF-16BE
79
+/   3: Unicode in UTF-8
80
+*/
81
+
82
+
83
+/*---------------------------------------------------------------------------/
84
+/ Locale and Namespace Configurations
85
+/---------------------------------------------------------------------------*/
86
+
87
+#define FF_CODE_PAGE	850
88
+/* This option specifies the OEM code page to be used on the target system.
89
+/  Incorrect code page setting can cause a file open failure.
90
+/
91
+/   437 - U.S.
92
+/   720 - Arabic
93
+/   737 - Greek
94
+/   771 - KBL
95
+/   775 - Baltic
96
+/   850 - Latin 1
97
+/   852 - Latin 2
98
+/   855 - Cyrillic
99
+/   857 - Turkish
100
+/   860 - Portuguese
101
+/   861 - Icelandic
102
+/   862 - Hebrew
103
+/   863 - Canadian French
104
+/   864 - Arabic
105
+/   865 - Nordic
106
+/   866 - Russian
107
+/   869 - Greek 2
108
+/   932 - Japanese (DBCS)
109
+/   936 - Simplified Chinese (DBCS)
110
+/   949 - Korean (DBCS)
111
+/   950 - Traditional Chinese (DBCS)
112
+/     0 - Include all code pages above and configured by f_setcp()
113
+*/
114
+
115
+
116
+#define FF_USE_LFN		3
117
+#define FF_MAX_LFN		255
118
+/* The FF_USE_LFN switches the support for LFN (long file name).
119
+/
120
+/   0: Disable LFN. FF_MAX_LFN has no effect.
121
+/   1: Enable LFN with static  working buffer on the BSS. Always NOT thread-safe.
122
+/   2: Enable LFN with dynamic working buffer on the STACK.
123
+/   3: Enable LFN with dynamic working buffer on the HEAP.
124
+/
125
+/  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
126
+/  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
127
+/  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
128
+/  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
129
+/  be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
130
+/  specification.
131
+/  When use stack for the working buffer, take care on stack overflow. When use heap
132
+/  memory for the working buffer, memory management functions, ff_memalloc() and
133
+/  ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
134
+
135
+
136
+#define FF_LFN_UNICODE	2
137
+/* This option switches the character encoding on the API when LFN is enabled.
138
+/
139
+/   0: ANSI/OEM in current CP (TCHAR = char)
140
+/   1: Unicode in UTF-16 (TCHAR = WCHAR)
141
+/   2: Unicode in UTF-8 (TCHAR = char)
142
+/   3: Unicode in UTF-32 (TCHAR = DWORD)
143
+/
144
+/  Also behavior of string I/O functions will be affected by this option.
145
+/  When LFN is not enabled, this option has no effect. */
146
+
147
+
148
+#define FF_LFN_BUF		255
149
+#define FF_SFN_BUF		12
150
+/* This set of options defines size of file name members in the FILINFO structure
151
+/  which is used to read out directory items. These values should be suffcient for
152
+/  the file names to read. The maximum possible length of the read file name depends
153
+/  on character encoding. When LFN is not enabled, these options have no effect. */
154
+
155
+
156
+#define FF_FS_RPATH		0
157
+/* This option configures support for relative path.
158
+/
159
+/   0: Disable relative path and remove related functions.
160
+/   1: Enable relative path. f_chdir() and f_chdrive() are available.
161
+/   2: f_getcwd() function is available in addition to 1.
162
+*/
163
+
164
+
165
+/*---------------------------------------------------------------------------/
166
+/ Drive/Volume Configurations
167
+/---------------------------------------------------------------------------*/
168
+
169
+#define FF_VOLUMES		1
170
+/* Number of volumes (logical drives) to be used. (1-10) */
171
+
172
+
173
+#define FF_STR_VOLUME_ID	0
174
+#define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
175
+/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
176
+/  When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
177
+/  number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
178
+/  logical drives. Number of items must not be less than FF_VOLUMES. Valid
179
+/  characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
180
+/  compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
181
+/  not defined, a user defined volume string table is needed as:
182
+/
183
+/  const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
184
+*/
185
+
186
+
187
+#define FF_MULTI_PARTITION	0
188
+/* This option switches support for multiple volumes on the physical drive.
189
+/  By default (0), each logical drive number is bound to the same physical drive
190
+/  number and only an FAT volume found on the physical drive will be mounted.
191
+/  When this function is enabled (1), each logical drive number can be bound to
192
+/  arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
193
+/  function will be available. */
194
+
195
+
196
+#define FF_MIN_SS		512
197
+#define FF_MAX_SS		512
198
+/* This set of options configures the range of sector size to be supported. (512,
199
+/  1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
200
+/  harddisk, but a larger value may be required for on-board flash memory and some
201
+/  type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
202
+/  for variable sector size mode and disk_ioctl() function needs to implement
203
+/  GET_SECTOR_SIZE command. */
204
+
205
+
206
+#define FF_LBA64		0
207
+/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
208
+/  To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
209
+
210
+
211
+#define FF_MIN_GPT		0x10000000
212
+/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
213
+/  f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
214
+
215
+
216
+#define FF_USE_TRIM		0
217
+/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
218
+/  To enable Trim function, also CTRL_TRIM command should be implemented to the
219
+/  disk_ioctl() function. */
220
+
221
+
222
+
223
+/*---------------------------------------------------------------------------/
224
+/ System Configurations
225
+/---------------------------------------------------------------------------*/
226
+
227
+#define FF_FS_TINY		0
228
+/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
229
+/  At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
230
+/  Instead of private sector buffer eliminated from the file object, common sector
231
+/  buffer in the filesystem object (FATFS) is used for the file data transfer. */
232
+
233
+
234
+#define FF_FS_EXFAT		0
235
+/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
236
+/  To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
237
+/  Note that enabling exFAT discards ANSI C (C89) compatibility. */
238
+
239
+
240
+#define FF_FS_NORTC		1
241
+#define FF_NORTC_MON	1
242
+#define FF_NORTC_MDAY	1
243
+#define FF_NORTC_YEAR	2022
244
+/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
245
+/  an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
246
+/  timestamp feature. Every object modified by FatFs will have a fixed timestamp
247
+/  defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
248
+/  To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
249
+/  added to the project to read current time form real-time clock. FF_NORTC_MON,
250
+/  FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
251
+/  These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
252
+
253
+
254
+#define FF_FS_NOFSINFO	0
255
+/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
256
+/  option, and f_getfree() function at the first time after volume mount will force
257
+/  a full FAT scan. Bit 1 controls the use of last allocated cluster number.
258
+/
259
+/  bit0=0: Use free cluster count in the FSINFO if available.
260
+/  bit0=1: Do not trust free cluster count in the FSINFO.
261
+/  bit1=0: Use last allocated cluster number in the FSINFO if available.
262
+/  bit1=1: Do not trust last allocated cluster number in the FSINFO.
263
+*/
264
+
265
+
266
+#define FF_FS_LOCK		0
267
+/* The option FF_FS_LOCK switches file lock function to control duplicated file open
268
+/  and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
269
+/  is 1.
270
+/
271
+/  0:  Disable file lock function. To avoid volume corruption, application program
272
+/      should avoid illegal open, remove and rename to the open objects.
273
+/  >0: Enable file lock function. The value defines how many files/sub-directories
274
+/      can be opened simultaneously under file lock control. Note that the file
275
+/      lock control is independent of re-entrancy. */
276
+
277
+
278
+#define FF_FS_REENTRANT	0
279
+#define FF_FS_TIMEOUT	1000
280
+/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
281
+/  module itself. Note that regardless of this option, file access to different
282
+/  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
283
+/  and f_fdisk() function, are always not re-entrant. Only file/directory access
284
+/  to the same volume is under control of this featuer.
285
+/
286
+/   0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
287
+/   1: Enable re-entrancy. Also user provided synchronization handlers,
288
+/      ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give()
289
+/      function, must be added to the project. Samples are available in ffsystem.c.
290
+/
291
+/  The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
292
+*/
293
+
294
+
295
+
296
+/*--- End of configuration options ---*/

+ 44
- 0
include/log.h View File

@@ -0,0 +1,44 @@
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 <stdbool.h>
23
+
24
+// for output that is stored in the debug log.
25
+// will be re-played from buffer when terminal connects
26
+#define debug(fmt, ...) debug_log(true, \
27
+        "%08lu %s: " fmt "\r\n", \
28
+        to_ms_since_boot(get_absolute_time()), \
29
+        __func__, \
30
+        ##__VA_ARGS__)
31
+
32
+// for interactive output. is not stored or re-played.
33
+#define print(fmt, ...) debug_log(false, fmt, ##__VA_ARGS__)
34
+#define println(fmt, ...) debug_log(false, fmt "\r\n", ##__VA_ARGS__)
35
+
36
+void debug_log(bool log, const char *format, ...) __attribute__((format(printf, 2, 3)));
37
+void debug_wait_input(const char *format, ...) __attribute__((format(printf, 1, 2)));
38
+
39
+void log_dump_to_usb(void);
40
+void log_dump_to_disk(void);
41
+
42
+void debug_handle_input(char *buff, uint32_t len);
43
+
44
+#endif // __LOG_H__

+ 125
- 0
include/tusb_config.h View File

@@ -0,0 +1,125 @@
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               1
105
+#define CFG_TUD_MIDI              0
106
+#define CFG_TUD_VENDOR            0
107
+
108
+// HID buffer size Should be sufficient to hold ID (if any) + Data
109
+#define CFG_TUD_HID_EP_BUFSIZE    16
110
+
111
+// CDC FIFO size of TX and RX
112
+#define CFG_TUD_CDC_RX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
113
+#define CFG_TUD_CDC_TX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
114
+
115
+// CDC Endpoint transfer buffer size, more is faster
116
+#define CFG_TUD_CDC_EP_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
117
+
118
+// MSC Buffer size of Device Mass storage
119
+#define CFG_TUD_MSC_EP_BUFSIZE   512
120
+
121
+#ifdef __cplusplus
122
+ }
123
+#endif
124
+
125
+#endif /* _TUSB_CONFIG_H_ */

+ 25
- 0
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__

+ 25
- 0
include/usb_cdc.h View File

@@ -0,0 +1,25 @@
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
+void usb_cdc_write(const char *buf, uint32_t count);
23
+void usb_cdc_set_reroute(bool reroute);
24
+
25
+#endif // __USB_CDC_H__

+ 24
- 0
include/usb_descriptors.h View File

@@ -0,0 +1,24 @@
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
+void usb_descriptor_init_id(void);
23
+
24
+#endif /* USB_DESCRIPTORS_H_ */

+ 25
- 0
include/usb_msc.h View File

@@ -0,0 +1,25 @@
1
+/*
2
+ * usb_msc.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_MSC_H__
20
+#define __USB_MSC_H__
21
+
22
+bool msc_is_medium_available(void);
23
+void msc_set_medium_available(bool state);
24
+
25
+#endif // __USB_MSC_H__

+ 34
- 0
include/util.h View File

@@ -0,0 +1,34 @@
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(uint8_t *buff, size_t len);
33
+
34
+#endif // __UTIL_H__

+ 1
- 0
mcufont

@@ -0,0 +1 @@
1
+Subproject commit 9f3aa41b231195e7b2b59f78d8f01d06460b6d35

+ 1
- 0
pico-sdk

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

+ 72
- 0
src/buttons.c View File

@@ -0,0 +1,72 @@
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 "buttons.h"
23
+
24
+static const uint gpio_num[NUM_BTNS] = {
25
+    15, // BTN_A
26
+    17, // BTN_B
27
+    19, // BTN_X
28
+    21, // BTN_Y
29
+     2, // BTN_UP
30
+    18, // BTN_DOWN
31
+    16, // BTN_LEFT
32
+    20, // BTN_RIGHT
33
+     3, // BTN_ENTER
34
+};
35
+
36
+struct button_state {
37
+    uint32_t last_time;
38
+    bool current_state, last_state;
39
+};
40
+
41
+static struct button_state buttons[NUM_BTNS];
42
+
43
+void buttons_init(void) {
44
+    for (uint i = 0; i < NUM_BTNS; i++) {
45
+        gpio_init(gpio_num[i]);
46
+        gpio_set_dir(gpio_num[i], GPIO_IN);
47
+        gpio_pull_up(gpio_num[i]);
48
+
49
+        buttons[i].last_time = 0;
50
+        buttons[i].current_state = false;
51
+        buttons[i].last_state = false;
52
+    }
53
+}
54
+
55
+void buttons_run(void) {
56
+    for (uint i = 0; i < NUM_BTNS; i++) {
57
+        bool state = !gpio_get(gpio_num[i]);
58
+        uint32_t now = to_ms_since_boot(get_absolute_time());
59
+
60
+        if (state != buttons[i].last_state) {
61
+            buttons[i].last_time = now;
62
+        }
63
+
64
+        if ((now - buttons[i].last_time) > DEBOUNCE_DELAY_MS) {
65
+            if (state != buttons[i].current_state) {
66
+                buttons[i].current_state = state;
67
+            }
68
+        }
69
+
70
+        buttons[i].last_state = state;
71
+    }
72
+}

+ 192
- 0
src/console.c View File

@@ -0,0 +1,192 @@
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 "pico/stdlib.h"
22
+#include <unistd.h>
23
+
24
+#include "config.h"
25
+#include "log.h"
26
+#include "util.h"
27
+#include "usb_cdc.h"
28
+#include "usb_msc.h"
29
+#include "debug.h"
30
+#include "console.h"
31
+
32
+#define CNSL_BUFF_SIZE 1024
33
+#define CNSL_REPEAT_MS 500
34
+
35
+//#define CNSL_REPEAT_PMW_STATUS_BY_DEFAULT
36
+
37
+static char cnsl_line_buff[CNSL_BUFF_SIZE + 1];
38
+static uint32_t cnsl_buff_pos = 0;
39
+
40
+static char cnsl_last_command[CNSL_BUFF_SIZE + 1];
41
+
42
+static char cnsl_repeated_command[CNSL_BUFF_SIZE + 1];
43
+static bool repeat_command = false;
44
+static uint32_t last_repeat_time = 0;
45
+
46
+static void cnsl_interpret(const char *line) {
47
+    if (strlen(line) == 0) {
48
+        if ((strlen(cnsl_last_command) > 0) && (strcmp(cnsl_last_command, "repeat") != 0)) {
49
+            // repeat last command once
50
+            println("repeating command \"%s\"", cnsl_last_command);
51
+            cnsl_interpret(cnsl_last_command);
52
+            println();
53
+        }
54
+        return;
55
+    } else if (strcmp(line, "repeat") == 0) {
56
+        if (!repeat_command) {
57
+            // mark last command to be repeated multiple times
58
+            strncpy(cnsl_repeated_command, cnsl_last_command, CNSL_BUFF_SIZE + 1);
59
+            last_repeat_time = to_ms_since_boot(get_absolute_time()) - 1001;
60
+            repeat_command = true;
61
+        } else {
62
+            // stop repeating
63
+            repeat_command = false;
64
+        }
65
+    } else if ((strcmp(line, "help") == 0)
66
+            || (strcmp(line, "h") == 0)
67
+            || (strcmp(line, "?") == 0)) {
68
+        println("Trackball Firmware Usage:");
69
+        println("  reset - reset back into this firmware");
70
+        println("   \\x18 - reset to bootloader");
71
+        println(" repeat - repeat last command every %d milliseconds", CNSL_REPEAT_MS);
72
+        println("   help - print this message");
73
+        println("  mount - make mass storage medium (un)available");
74
+        //println("    foo - bar");
75
+        println("Press Enter with no input to repeat last command.");
76
+        println("Use repeat to continuously execute last command.");
77
+        println("Stop this by calling repeat again.");
78
+    } else if (strcmp(line, "reset") == 0) {
79
+        reset_to_main();
80
+    } else if (strcmp(line, "mount") == 0) {
81
+        bool state = msc_is_medium_available();
82
+        println("Currently %s. %s now.",
83
+                state ? "mounted" : "unmounted",
84
+                state ? "Unplugging" : "Plugging in");
85
+        msc_set_medium_available(!state);
86
+    } else {
87
+        println("unknown command \"%s\"", line);
88
+    }
89
+
90
+    println();
91
+}
92
+
93
+void cnsl_init(void) {
94
+    cnsl_buff_pos = 0;
95
+    for (int i = 0; i < CNSL_BUFF_SIZE + 1; i++) {
96
+        cnsl_line_buff[i] = '\0';
97
+        cnsl_last_command[i] = '\0';
98
+        cnsl_repeated_command[i] = '\0';
99
+    }
100
+
101
+#ifdef CNSL_REPEAT_PMW_STATUS_BY_DEFAULT
102
+    strcpy(cnsl_repeated_command, "pmws");
103
+    repeat_command = true;
104
+#endif // CNSL_REPEAT_PMW_STATUS_BY_DEFAULT
105
+}
106
+
107
+static int32_t cnsl_find_line_end(void) {
108
+    for (uint32_t i = 0; i < cnsl_buff_pos; i++) {
109
+        if ((cnsl_line_buff[i] == '\r') || (cnsl_line_buff[i] == '\n')) {
110
+            return i;
111
+        }
112
+    }
113
+    return -1;
114
+}
115
+
116
+void cnsl_run(void) {
117
+    if (repeat_command && (strlen(cnsl_repeated_command) > 0)
118
+            && (strcmp(cnsl_repeated_command, "repeat") != 0)) {
119
+        uint32_t now = to_ms_since_boot(get_absolute_time());
120
+        if (now >= (last_repeat_time + CNSL_REPEAT_MS)) {
121
+            println("repeating command \"%s\"", cnsl_repeated_command);
122
+            cnsl_interpret(cnsl_repeated_command);
123
+            println();
124
+
125
+            last_repeat_time = now;
126
+        }
127
+    } else {
128
+        if (repeat_command) {
129
+            println("nothing to repeat");
130
+        }
131
+        repeat_command = false;
132
+    }
133
+}
134
+
135
+void cnsl_handle_input(const char *buf, uint32_t len) {
136
+    if ((cnsl_buff_pos + len) > CNSL_BUFF_SIZE) {
137
+        debug("error: console input buffer overflow! %lu > %u", cnsl_buff_pos + len, CNSL_BUFF_SIZE);
138
+        cnsl_init();
139
+    }
140
+
141
+    memcpy(cnsl_line_buff + cnsl_buff_pos, buf, len);
142
+    cnsl_buff_pos += len;
143
+
144
+    // handle backspace
145
+    for (ssize_t i = cnsl_buff_pos - len; i < (ssize_t)cnsl_buff_pos; i++) {
146
+        if ((cnsl_line_buff[i] == '\b') || (cnsl_line_buff[i] == 0x7F)) {
147
+            if (i > 0) {
148
+                // overwrite previous character and backspace
149
+                for (ssize_t j = i; j < (ssize_t)cnsl_buff_pos - 1; j++) {
150
+                    cnsl_line_buff[j - 1] = cnsl_line_buff[j + 1];
151
+                }
152
+                cnsl_buff_pos -= 2;
153
+            } else {
154
+                // just remove the backspace
155
+                for (ssize_t j = i; j < (ssize_t)cnsl_buff_pos - 1; j++) {
156
+                    cnsl_line_buff[j] = cnsl_line_buff[j + 1];
157
+                }
158
+                cnsl_buff_pos -= 1;
159
+            }
160
+
161
+            usb_cdc_write("\b \b", 3);
162
+
163
+            // check for another backspace in this space
164
+            i--;
165
+        } else {
166
+            usb_cdc_write(cnsl_line_buff + i, 1);
167
+        }
168
+    }
169
+
170
+    int32_t line_len = cnsl_find_line_end();
171
+    if (line_len < 0) {
172
+        // user has not pressed enter yet
173
+        return;
174
+    }
175
+
176
+    // convert line to C-style string
177
+    cnsl_line_buff[line_len] = '\0';
178
+
179
+    cnsl_interpret(cnsl_line_buff);
180
+
181
+    // store command for eventual repeats
182
+    strncpy(cnsl_last_command, cnsl_line_buff, CNSL_BUFF_SIZE + 1);
183
+
184
+    // clear string and move following data over
185
+    uint32_t cnt = line_len + 1;
186
+    if (cnsl_line_buff[line_len + 1] == '\n') {
187
+        cnt++;
188
+    }
189
+    memset(cnsl_line_buff, '\0', cnt);
190
+    memmove(cnsl_line_buff, cnsl_line_buff + cnt, sizeof(cnsl_line_buff) - cnt);
191
+    cnsl_buff_pos -= cnt;
192
+}

+ 61
- 0
src/debug.c View File

@@ -0,0 +1,61 @@
1
+/*
2
+ * debug.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 "ff.h"
22
+
23
+#include "config.h"
24
+#include "log.h"
25
+#include "debug.h"
26
+
27
+static FATFS fs;
28
+static bool mounted = false;
29
+
30
+int debug_msc_mount(void) {
31
+    if (mounted) {
32
+        debug("already mounted");
33
+        return 0;
34
+    }
35
+
36
+    FRESULT res = f_mount(&fs, "", 0);
37
+    if (res != FR_OK) {
38
+        debug("error: f_mount returned %d", res);
39
+        mounted = false;
40
+        return -1;
41
+    }
42
+
43
+    mounted = true;
44
+    return 0;
45
+}
46
+
47
+int debug_msc_unmount(void) {
48
+    if (!mounted) {
49
+        debug("already unmounted");
50
+        return 0;
51
+    }
52
+
53
+    FRESULT res = f_mount(0, "", 0);
54
+    if (res != FR_OK) {
55
+        debug("error: f_mount returned %d", res);
56
+        return -1;
57
+    }
58
+
59
+    mounted = false;
60
+    return 0;
61
+}

+ 170
- 0
src/fat_disk.c View File

@@ -0,0 +1,170 @@
1
+/* 
2
+ * fat_disk.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 <stdlib.h>
21
+#include <stdio.h>
22
+
23
+#include "pico/stdlib.h"
24
+#include "ff.h"
25
+#include "diskio.h"
26
+
27
+#include "config.h"
28
+#include "log.h"
29
+#include "debug.h"
30
+#include "fat_disk.h"
31
+
32
+static uint8_t disk[DISK_BLOCK_COUNT * DISK_BLOCK_SIZE];
33
+
34
+void fat_disk_init(void) {
35
+    BYTE work[FF_MAX_SS];
36
+    FRESULT res = f_mkfs("", 0, work, sizeof(work));
37
+    if (res != FR_OK) {
38
+        debug("error: f_mkfs returned %d", res);
39
+        return;
40
+    }
41
+
42
+    if (debug_msc_mount() != 0) {
43
+        debug("error mounting disk");
44
+        return;
45
+    }
46
+
47
+    // maximum length: 11 bytes
48
+    f_setlabel("DEBUG DISK");
49
+
50
+    FIL file;
51
+    res = f_open(&file, "README.md", FA_CREATE_ALWAYS | FA_WRITE);
52
+    if (res != FR_OK) {
53
+        debug("error: f_open returned %d", res);
54
+    } else {
55
+        char readme[1024];
56
+        size_t pos = 0;
57
+        pos += snprintf(readme + pos, 1024 - pos, "# Volcano Remote Control Gadget\r\n");
58
+        pos += snprintf(readme + pos, 1024 - pos, "\r\n");
59
+        pos += snprintf(readme + pos, 1024 - pos, "Project by Thomas Buck <thomas@xythobuz.de>\r\n");
60
+        pos += snprintf(readme + pos, 1024 - pos, "Licensed under GPLv3.\r\n");
61
+        pos += snprintf(readme + pos, 1024 - pos, "Source at https://git.xythobuz.de/thomas/sb-py\r\n");
62
+
63
+        size_t len = strlen(readme);
64
+        UINT bw;
65
+        res = f_write(&file, readme, len, &bw);
66
+        if ((res != FR_OK) || (bw != len)) {
67
+            debug("error: f_write returned %d", res);
68
+        }
69
+
70
+        res = f_close(&file);
71
+        if (res != FR_OK) {
72
+            debug("error: f_close returned %d", res);
73
+        }
74
+    }
75
+
76
+    if (debug_msc_unmount() != 0) {
77
+        debug("error unmounting disk");
78
+    }
79
+}
80
+
81
+uint8_t *fat_disk_get_sector(uint32_t sector) {
82
+    return disk + (sector * DISK_BLOCK_SIZE);
83
+}
84
+
85
+/*
86
+ * FatFS ffsystem.c
87
+ */
88
+
89
+void* ff_memalloc(UINT msize) {
90
+    return malloc((size_t)msize);
91
+}
92
+
93
+void ff_memfree(void* mblock) {
94
+    free(mblock);
95
+}
96
+
97
+/*
98
+ * FatFS diskio.c
99
+ */
100
+
101
+DSTATUS disk_status(BYTE pdrv) {
102
+    if (pdrv != 0) {
103
+        debug("invalid drive number %d", pdrv);
104
+        return STA_NODISK;
105
+    }
106
+
107
+    return 0;
108
+}
109
+
110
+DSTATUS disk_initialize(BYTE pdrv) {
111
+    if (pdrv != 0) {
112
+        debug("invalid drive number %d", pdrv);
113
+        return STA_NODISK;
114
+    }
115
+
116
+    return 0;
117
+}
118
+
119
+DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
120
+    if (pdrv != 0) {
121
+        debug("invalid drive number %d", pdrv);
122
+        return RES_PARERR;
123
+    }
124
+
125
+    if ((sector + count) > DISK_BLOCK_COUNT) {
126
+        debug("invalid read ((%lu + %u) > %u)", sector, count, DISK_BLOCK_COUNT);
127
+        return RES_ERROR;
128
+    }
129
+
130
+    memcpy(buff, disk + (sector * DISK_BLOCK_SIZE), count * DISK_BLOCK_SIZE);
131
+    return RES_OK;
132
+}
133
+
134
+DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
135
+    if (pdrv != 0) {
136
+        debug("invalid drive number %d", pdrv);
137
+        return RES_PARERR;
138
+    }
139
+
140
+    if ((sector + count) > DISK_BLOCK_COUNT) {
141
+        debug("invalid read ((%lu + %u) > %u)", sector, count, DISK_BLOCK_COUNT);
142
+        return RES_ERROR;
143
+    }
144
+
145
+    memcpy(disk + (sector * DISK_BLOCK_SIZE), buff, count * DISK_BLOCK_SIZE);
146
+    return RES_OK;
147
+}
148
+
149
+DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
150
+    if (pdrv != 0) {
151
+        debug("invalid drive number %d", pdrv);
152
+        return RES_PARERR;
153
+    }
154
+
155
+    switch (cmd) {
156
+        case GET_SECTOR_COUNT:
157
+            *((LBA_t *)buff) = DISK_BLOCK_COUNT;
158
+            break;
159
+
160
+        case GET_SECTOR_SIZE:
161
+            *((WORD *)buff) = DISK_BLOCK_SIZE;
162
+            break;
163
+
164
+        case GET_BLOCK_SIZE:
165
+            *((DWORD *)buff) = 1; // non flash memory media
166
+            break;
167
+    }
168
+
169
+    return RES_OK;
170
+}

+ 169
- 0
src/log.c View File

@@ -0,0 +1,169 @@
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 <stdarg.h>
20
+#include <stdio.h>
21
+
22
+#include "pico/stdlib.h"
23
+#include "hardware/watchdog.h"
24
+#include "ff.h"
25
+
26
+#include "config.h"
27
+#include "usb.h"
28
+#include "usb_cdc.h"
29
+#include "log.h"
30
+
31
+static char log_buff[4096];
32
+static size_t head = 0, tail = 0;
33
+static bool full = false;
34
+static bool got_input = false;
35
+
36
+static void add_to_log(const char *buff, int len) {
37
+    for (int i = 0; i < len; i++) {
38
+        log_buff[head] = buff[i];
39
+
40
+        if (full && (++tail == sizeof(log_buff))) {
41
+            tail = 0;
42
+        }
43
+
44
+        if (++(head) == sizeof(log_buff)) {
45
+            head = 0;
46
+        }
47
+
48
+        full = (head == tail);
49
+    }
50
+}
51
+
52
+void log_dump_to_usb(void) {
53
+    if (head == tail) {
54
+        return;
55
+    }
56
+
57
+    char buff[32];
58
+    int l = snprintf(buff, sizeof(buff), "\r\n\r\nbuffered log output:\r\n");
59
+    if ((l > 0) && (l <= (int)sizeof(buff))) {
60
+        usb_cdc_write(buff, l);
61
+    }
62
+
63
+    if (head > tail) {
64
+        usb_cdc_write(log_buff + tail, head - tail);
65
+    } else {
66
+        usb_cdc_write(log_buff + tail, sizeof(log_buff) - tail);
67
+        usb_cdc_write(log_buff, head);
68
+    }
69
+
70
+    l = snprintf(buff, sizeof(buff), "\r\n\r\nlive log:\r\n");
71
+    if ((l > 0) && (l <= (int)sizeof(buff))) {
72
+        usb_cdc_write(buff, l);
73
+    }
74
+}
75
+
76
+void log_dump_to_disk(void) {
77
+    FIL file;
78
+    FRESULT res = f_open(&file, "log.txt", FA_CREATE_ALWAYS | FA_WRITE);
79
+    if (res != FR_OK) {
80
+        debug("error: f_open returned %d", res);
81
+        return;
82
+    }
83
+
84
+    UINT bw;
85
+
86
+    if (head > tail) {
87
+        res = f_write(&file, log_buff + tail, head - tail, &bw);
88
+        if ((res != FR_OK) || (bw != head - tail)) {
89
+            debug("error: f_write (A) returned %d", res);
90
+        }
91
+    } else if (head < tail) {
92
+        res = f_write(&file, log_buff + tail, sizeof(log_buff) - tail, &bw);
93
+        if ((res != FR_OK) || (bw != sizeof(log_buff) - tail)) {
94
+            debug("error: f_write (B) returned %d", res);
95
+        } else {
96
+            res = f_write(&file, log_buff, head, &bw);
97
+            if ((res != FR_OK) || (bw != head)) {
98
+                debug("error: f_write (C) returned %d", res);
99
+            }
100
+        }
101
+    }
102
+
103
+    res = f_close(&file);
104
+    if (res != FR_OK) {
105
+        debug("error: f_close returned %d", res);
106
+    }
107
+}
108
+
109
+static int format_debug_log(char *buff, size_t len, const char *format, va_list args) {
110
+    int l = vsnprintf(buff, len, format, args);
111
+
112
+    if (l < 0) {
113
+        // encoding error
114
+        l = snprintf(buff, len, "%s: encoding error\r\n", __func__);
115
+    } else if (l >= (ssize_t)len) {
116
+        // not enough space for string
117
+        l = snprintf(buff, len, "%s: message too long (%d)\r\n", __func__, l);
118
+    }
119
+
120
+    return l;
121
+}
122
+
123
+void debug_log(bool log, const char* format, ...) {
124
+    static char line_buff[512];
125
+
126
+    va_list args;
127
+    va_start(args, format);
128
+    int l = format_debug_log(line_buff, sizeof(line_buff), format, args);
129
+    va_end(args);
130
+
131
+    if ((l > 0) && (l <= (int)sizeof(line_buff))) {
132
+        usb_cdc_write(line_buff, l);
133
+
134
+        if (log) {
135
+            add_to_log(line_buff, l);
136
+        }
137
+    }
138
+}
139
+
140
+void debug_handle_input(char *buff, uint32_t len) {
141
+    (void)buff;
142
+
143
+    if (len > 0) {
144
+        got_input = true;
145
+    }
146
+}
147
+
148
+void debug_wait_input(const char *format, ...) {
149
+    static char line_buff[512];
150
+
151
+    va_list args;
152
+    va_start(args, format);
153
+    int l = format_debug_log(line_buff, sizeof(line_buff), format, args);
154
+    va_end(args);
155
+
156
+    if ((l > 0) && (l <= (int)sizeof(line_buff))) {
157
+        usb_cdc_write(line_buff, l);
158
+    }
159
+
160
+    got_input = false;
161
+    usb_cdc_set_reroute(true);
162
+
163
+    while (!got_input) {
164
+        watchdog_update();
165
+        usb_run();
166
+    }
167
+
168
+    usb_cdc_set_reroute(false);
169
+}

+ 59
- 0
src/main.c View File

@@ -0,0 +1,59 @@
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 "fat_disk.h"
28
+#include "buttons.h"
29
+
30
+int main(void) {
31
+    heartbeat_init();
32
+    buttons_init();
33
+
34
+    cnsl_init();
35
+    usb_init();
36
+
37
+    if (watchdog_caused_reboot()) {
38
+        debug("reset by watchdog");
39
+    }
40
+
41
+    debug("fat_disk_init");
42
+    fat_disk_init();
43
+
44
+    // trigger after 500ms
45
+    watchdog_enable(500, 1);
46
+
47
+    debug("init done");
48
+
49
+    while (1) {
50
+        watchdog_update();
51
+
52
+        heartbeat_run();
53
+        buttons_run();
54
+        usb_run();
55
+        cnsl_run();
56
+    }
57
+
58
+    return 0;
59
+}

+ 70
- 0
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
+}

+ 117
- 0
src/usb_cdc.c View File

@@ -0,0 +1,117 @@
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 char *buf, uint32_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
+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] == 0x18)) {
80
+            // ASCII 0x18 = CAN (cancel)
81
+            debug("switching to bootloader");
82
+            reset_to_bootloader();
83
+        } else if (reroute_cdc_debug) {
84
+            debug_handle_input(buf, count);
85
+        } else {
86
+            cnsl_handle_input(buf, count);
87
+        }
88
+    }
89
+}
90
+
91
+// invoked when cdc when line state changed e.g connected/disconnected
92
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
93
+    (void) itf;
94
+    (void) rts;
95
+
96
+    static bool last_dtr = false;
97
+
98
+    if (dtr && !last_dtr) {
99
+        // clear left-over console input
100
+        cnsl_init();
101
+
102
+        // show past history
103
+        log_dump_to_usb();
104
+
105
+        debug("terminal connected");
106
+    } else if (!dtr && last_dtr) {
107
+        debug("terminal disconnected");
108
+    }
109
+
110
+    last_dtr = dtr;
111
+}
112
+
113
+// invoked when CDC interface received data from host
114
+void tud_cdc_rx_cb(uint8_t itf) {
115
+    (void) itf;
116
+    cdc_task();
117
+}

+ 229
- 0
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 "pico/unique_id.h"
31
+#include "tusb.h"
32
+
33
+#include "config.h"
34
+#include "usb_descriptors.h"
35
+
36
+/*
37
+ * A combination of interfaces must have a unique product id,
38
+ * since PC will save device driver after the first plug.
39
+ * Same VID/PID with different interface e.g MSC (first),
40
+ * then CDC (later) will possibly cause system error on PC.
41
+ *
42
+ * Auto ProductID layout's Bitmap:
43
+ *   [MSB]         HID | MSC | CDC          [LSB]
44
+ */
45
+#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
46
+#define USB_PID (0x4200 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) \
47
+    | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
48
+
49
+#define USB_VID   0xCafe
50
+#define USB_BCD   0x0200
51
+
52
+//--------------------------------------------------------------------+
53
+// Device Descriptors
54
+//--------------------------------------------------------------------+
55
+
56
+tusb_desc_device_t const desc_device =
57
+{
58
+    .bLength            = sizeof(tusb_desc_device_t),
59
+    .bDescriptorType    = TUSB_DESC_DEVICE,
60
+    .bcdUSB             = USB_BCD,
61
+
62
+    /*
63
+     * Use Interface Association Descriptor (IAD) for CDC
64
+     * As required by USB Specs IAD's subclass must be common class (2)
65
+     * and protocol must be IAD (1)
66
+     */
67
+    .bDeviceClass       = TUSB_CLASS_MISC,
68
+    .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
69
+    .bDeviceProtocol    = MISC_PROTOCOL_IAD,
70
+
71
+    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
72
+
73
+    .idVendor           = USB_VID,
74
+    .idProduct          = USB_PID,
75
+    .bcdDevice          = 0x0100,
76
+
77
+    .iManufacturer      = 0x01,
78
+    .iProduct           = 0x02,
79
+    .iSerialNumber      = 0x03,
80
+
81
+    .bNumConfigurations = 0x01
82
+};
83
+
84
+// Invoked when received GET DEVICE DESCRIPTOR
85
+// Application return pointer to descriptor
86
+uint8_t const * tud_descriptor_device_cb(void) {
87
+    return (uint8_t const *) &desc_device;
88
+}
89
+
90
+//--------------------------------------------------------------------+
91
+// Configuration Descriptor
92
+//--------------------------------------------------------------------+
93
+
94
+enum {
95
+    ITF_NUM_CDC = 0,
96
+    ITF_NUM_CDC_DATA,
97
+    ITF_NUM_MSC,
98
+    ITF_NUM_TOTAL
99
+};
100
+
101
+#define  CONFIG_TOTAL_LEN  (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN)
102
+
103
+#define EPNUM_CDC_NOTIF 0x82
104
+#define EPNUM_CDC_OUT   0x02
105
+#define EPNUM_CDC_IN    0x83
106
+#define EPNUM_MSC_OUT   0x03
107
+#define EPNUM_MSC_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_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_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
+    "VolcanoRC",                   // 2: Product
190
+    string_pico_serial,            // 3: Serials, should use chip ID
191
+    "Debug Serial",                // 4: CDC Interface
192
+    "Debug Memory",                // 5: MSC 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
+}

+ 215
- 0
src/usb_msc.c View File

@@ -0,0 +1,215 @@
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
+#include "bsp/board.h"
30
+#include "tusb.h"
31
+
32
+#include "config.h"
33
+#include "fat_disk.h"
34
+#include "log.h"
35
+
36
+static bool ejected = false;
37
+static bool medium_available = false;
38
+
39
+bool msc_is_medium_available(void) {
40
+    return medium_available;
41
+}
42
+
43
+void msc_set_medium_available(bool state) {
44
+    medium_available = state;
45
+    if (state) {
46
+        ejected = false;
47
+    }
48
+}
49
+
50
+// Invoked when received SCSI_CMD_INQUIRY
51
+// Application fill vendor id, product id and revision
52
+// with string up to 8, 16, 4 characters respectively
53
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8],
54
+        uint8_t product_id[16], uint8_t product_rev[4]) {
55
+    (void) lun;
56
+
57
+    const char vid[] = "xythobuz";
58
+    const char pid[] = "Debug Storage";
59
+    const char rev[] = "0.01";
60
+
61
+    memcpy(vendor_id  , vid, strlen(vid));
62
+    memcpy(product_id , pid, strlen(pid));
63
+    memcpy(product_rev, rev, strlen(rev));
64
+}
65
+
66
+// Invoked when received Test Unit Ready command.
67
+// return true allowing host to read/write this LUN e.g SD card inserted
68
+bool tud_msc_test_unit_ready_cb(uint8_t lun) {
69
+    if (!medium_available) {
70
+        // Additional Sense 3A-00 is NOT_FOUND
71
+        tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
72
+    }
73
+
74
+    return medium_available;
75
+}
76
+
77
+// Invoked when received SCSI_CMD_READ_CAPACITY_10 and
78
+// SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
79
+// Application update block count and block size
80
+void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) {
81
+    (void) lun;
82
+
83
+    if (!medium_available) {
84
+        *block_count = 0;
85
+        *block_size = 0;
86
+    } else {
87
+        *block_count = DISK_BLOCK_COUNT;
88
+        *block_size  = DISK_BLOCK_SIZE;
89
+    }
90
+}
91
+
92
+// Invoked when received Start Stop Unit command
93
+// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
94
+// - Start = 1 : active mode, if load_eject = 1 : load disk storage
95
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition,
96
+        bool start, bool load_eject) {
97
+    (void) lun;
98
+    (void) power_condition;
99
+
100
+    if (start) {
101
+        // load disk storage
102
+        debug("load disk storage %d", load_eject);
103
+    } else {
104
+        // unload disk storage
105
+        debug("unload disk storage %d", load_eject);
106
+        if (load_eject) {
107
+            medium_available = false;
108
+        }
109
+    }
110
+
111
+    return true;
112
+}
113
+
114
+// Callback invoked when received READ10 command.
115
+// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
116
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba,
117
+        uint32_t offset, void* buffer, uint32_t bufsize) {
118
+    (void) lun;
119
+
120
+    // out of ramdisk
121
+    if (lba >= DISK_BLOCK_COUNT) {
122
+        return -1;
123
+    }
124
+
125
+    // TODO better range checking and length calculation?
126
+
127
+    uint8_t const* addr = fat_disk_get_sector(lba) + offset;
128
+    memcpy(buffer, addr, bufsize);
129
+
130
+    return (int32_t) bufsize;
131
+}
132
+
133
+bool tud_msc_is_writable_cb (uint8_t lun) {
134
+    (void) lun;
135
+
136
+    return true;
137
+}
138
+
139
+// Callback invoked when received WRITE10 command.
140
+// Process data in buffer to disk's storage and return number of written bytes
141
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba,
142
+        uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
143
+    (void) lun;
144
+
145
+    // out of ramdisk
146
+    if (lba >= DISK_BLOCK_COUNT) {
147
+        return -1;
148
+    }
149
+
150
+    // TODO better range checking and length calculation?
151
+
152
+    uint8_t* addr = fat_disk_get_sector(lba) + offset;
153
+    memcpy(addr, buffer, bufsize);
154
+
155
+    return (int32_t) bufsize;
156
+}
157
+
158
+// Callback invoked when received an SCSI command not in built-in list below
159
+// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
160
+// - READ10 and WRITE10 has their own callbacks and MUST not be handled here
161
+int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16],
162
+        void* buffer, uint16_t bufsize) {
163
+    void const* response = NULL;
164
+    int32_t resplen = 0;
165
+
166
+    // most scsi handled is input
167
+    bool in_xfer = true;
168
+
169
+    switch (scsi_cmd[0]) {
170
+    case 0x1E:
171
+        // Prevent/Allow Medium Removal
172
+        if (scsi_cmd[4] & 0x01) {
173
+            // Prevent medium removal
174
+            if (!medium_available) {
175
+                debug("Host wants to lock non-existing medium. Not supported.");
176
+                resplen = -1;
177
+            } else {
178
+                debug("Host wants to lock medium.");
179
+            }
180
+        } else {
181
+            // Allow medium removal
182
+            if (medium_available) {
183
+                debug("Host ejected medium. Unplugging disk.");
184
+                medium_available = false;
185
+            } else {
186
+                debug("host ejected non-existing medium. Not supported.");
187
+                resplen = -1;
188
+            }
189
+        }
190
+        break;
191
+
192
+    default:
193
+        // Set Sense = Invalid Command Operation
194
+        tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
195
+
196
+        // negative means error -> tinyusb could stall and/or response with failed status
197
+        resplen = -1;
198
+        break;
199
+    }
200
+
201
+    // return resplen must not larger than bufsize
202
+    if (resplen > bufsize) {
203
+        resplen = bufsize;
204
+    }
205
+
206
+    if (response && (resplen > 0)) {
207
+        if (in_xfer) {
208
+            memcpy(buffer, response, (size_t) resplen);
209
+        } else {
210
+            // SCSI output
211
+        }
212
+    }
213
+
214
+    return (int32_t) resplen;
215
+}

+ 93
- 0
src/util.c View File

@@ -0,0 +1,93 @@
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
+#include "config.h"
25
+#include "log.h"
26
+#include "util.h"
27
+
28
+#define HEARTBEAT_INTERVAL_MS 500
29
+
30
+#ifdef PICO_DEFAULT_LED_PIN
31
+static uint32_t last_heartbeat = 0;
32
+#endif // PICO_DEFAULT_LED_PIN
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
+}
41
+
42
+void heartbeat_run(void) {
43
+#ifdef PICO_DEFAULT_LED_PIN
44
+    uint32_t now = to_ms_since_boot(get_absolute_time());
45
+    if (now >= (last_heartbeat + HEARTBEAT_INTERVAL_MS)) {
46
+        last_heartbeat = now;
47
+        gpio_xor_mask(1 << PICO_DEFAULT_LED_PIN);
48
+    }
49
+#endif // PICO_DEFAULT_LED_PIN
50
+}
51
+
52
+bool str_startswith(const char *str, const char *start) {
53
+    size_t l = strlen(start);
54
+    if (l > strlen(str)) {
55
+        return false;
56
+    }
57
+    return (strncmp(str, start, l) == 0);
58
+}
59
+
60
+int32_t convert_two_complement(int32_t b) {
61
+    if (b & 0x8000) {
62
+        b = -1 * ((b ^ 0xffff) + 1);
63
+    }
64
+    return b;
65
+}
66
+
67
+void reset_to_bootloader(void) {
68
+#ifdef PICO_DEFAULT_LED_PIN
69
+    reset_usb_boot(1 << PICO_DEFAULT_LED_PIN, 0);
70
+#else // ! PICO_DEFAULT_LED_PIN
71
+    reset_usb_boot(0, 0);
72
+#endif // PICO_DEFAULT_LED_PIN
73
+}
74
+
75
+void reset_to_main(void) {
76
+    watchdog_enable(1, 1);
77
+    while (1) {
78
+        // wait 1ms until watchdog kills us
79
+        asm volatile("nop");
80
+    }
81
+}
82
+
83
+void hexdump(uint8_t *buff, size_t len) {
84
+    for (size_t i = 0; i < len; i += 16) {
85
+        for (size_t j = 0; (j < 16) && ((i + j) < len); j++) {
86
+            print("0x%02X", buff[i + j]);
87
+            if ((j < 15) && ((i + j) < (len - 1))) {
88
+                print(" ");
89
+            }
90
+        }
91
+        println();
92
+    }
93
+}

+ 1
- 0
st7789

@@ -0,0 +1 @@
1
+Subproject commit 1b211accf9d626666f0d4aad5edffba2791398a2

Loading…
Cancel
Save