├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── VGATest ├── .gitignore ├── .vscode │ ├── .cortex-debug.peripherals.state.json │ ├── .cortex-debug.registers.state.json │ ├── c_cpp_properties.json │ ├── launch.json │ └── settings.json ├── CMakeLists.txt ├── VGATest.c ├── flash.c ├── flash.h ├── image.c ├── logic_analyser.c ├── perseverance.h ├── pico_extras_import.cmake ├── pico_sdk_import.cmake ├── sdring.c ├── sdring.h ├── vga.h ├── vga.pio ├── vgaaudio.c └── vgaaudio.h ├── flash-stream ├── README.md ├── flash.c └── flash.h ├── flashtest ├── .gitignore ├── CMakeLists.txt ├── README.md ├── flash_data.h ├── flash_data.py ├── flashtest.bin ├── flashtest.c └── pico_sdk_import.cmake ├── giftest ├── .gitignore ├── AnimatedGIF.h ├── CMakeLists.txt ├── README.md ├── gif.c ├── giftest.c ├── mkuf2.sh ├── pico_sdk_import.cmake ├── st7789_lcd.c ├── st7789_lcd.h └── st7789_lcd.pio ├── lasertest ├── CMakeLists.txt ├── lasertest.c └── pico_sdk_import.cmake ├── profile ├── .gitignore ├── .vscode │ ├── .cortex-debug.peripherals.state.json │ ├── .cortex-debug.registers.state.json │ ├── c_cpp_properties.json │ ├── launch.json │ └── settings.json ├── CMakeLists.txt ├── pico_sdk_import.cmake └── profile.c ├── pstest ├── .gitignore ├── .vscode │ ├── .cortex-debug.peripherals.state.json │ ├── .cortex-debug.registers.state.json │ ├── c_cpp_properties.json │ ├── launch.json │ └── settings.json ├── CMakeLists.txt ├── display.c ├── display.h ├── pico_extras_import.cmake ├── pico_sdk_import.cmake ├── pstest.c ├── st7789_lcd.c ├── st7789_lcd.h └── st7789_lcd.pio ├── rover ├── .gitignore ├── CMakeLists.txt ├── pico_sdk_import.cmake └── rover.c ├── sdaudio ├── .gitignore ├── .vscode │ ├── .cortex-debug.peripherals.state.json │ ├── .cortex-debug.registers.state.json │ ├── c_cpp_properties.json │ ├── launch.json │ └── settings.json ├── CMakeLists.txt ├── README.md ├── pico_extras_import.cmake ├── pico_sdk_import.cmake ├── sdaudio.c ├── sdring.c └── sdring.h └── sdgif ├── .gitignore ├── .vscode ├── .cortex-debug.peripherals.state.json ├── .cortex-debug.registers.state.json ├── c_cpp_properties.json ├── launch.json └── settings.json ├── AnimatedGIF.h ├── CMakeLists.txt ├── gif.c ├── pico_extras_import.cmake ├── pico_sdk_import.cmake ├── sdgif.c ├── st7789_lcd.c ├── st7789_lcd.h └── st7789_lcd.pio /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | mandelbrot/ 3 | adc-sample/ 4 | i2c2sd/ 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lasertest/VL53L1X"] 2 | path = lasertest/VL53L1X 3 | url = git@github.com:MichaelBell/pico-VL53L1X.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 (c) Michael Bell unless otherwise noted 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 4 | following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 7 | disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 10 | disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 21 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mike's pico projects 2 | 3 | I'd been creating one git repo per project, but that was getting a bit out of hand, so now I'm going to put new small projects into this repo. 4 | -------------------------------------------------------------------------------- /VGATest/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.gif 3 | *.huf 4 | *.dat 5 | frames/ 6 | feeding_duck*.h 7 | pcrane*.h 8 | image_lens.h -------------------------------------------------------------------------------- /VGATest/.vscode/.cortex-debug.peripherals.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /VGATest/.vscode/.cortex-debug.registers.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /VGATest/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [ 9 | "_DEBUG", 10 | "UNICODE", 11 | "_UNICODE" 12 | ], 13 | "windowsSdkVersion": "10.0.18362.0", 14 | "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.28.29333/bin/Hostx64/x64/cl.exe", 15 | "cStandard": "c17", 16 | "cppStandard": "c++17", 17 | "intelliSenseMode": "windows-msvc-x64", 18 | "configurationProvider": "ms-vscode.cmake-tools", 19 | "compileCommands": "${workspaceFolder}/build/compile_commands.json" 20 | } 21 | ], 22 | "version": 4 23 | } -------------------------------------------------------------------------------- /VGATest/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Pico Debug", 6 | "type":"cortex-debug", 7 | "cwd": "${workspaceRoot}", 8 | "executable": "${command:cmake.launchTargetPath}", 9 | "request": "launch", 10 | "servertype": "external", 11 | // This may need to be arm-none-eabi-gdb depending on your system 12 | "gdbPath": "arm-none-eabi-gdb", 13 | // Connect to an already running OpenOCD instance 14 | "gdbTarget": "inkypi:3333", 15 | "svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd", 16 | //"runToEntryPoint": "main", 17 | "postLaunchCommands": [ 18 | // "continue" 19 | ] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /VGATest/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.pio": "asm", 4 | "pio.h": "c", 5 | "sd_card.h": "c", 6 | "vga.h": "c", 7 | "stdlib.h": "c", 8 | "uart.h": "c", 9 | "stdio.h": "c", 10 | "pico.h": "c", 11 | "gpio.h": "c", 12 | "irq.h": "c", 13 | "critical_section.h": "c" 14 | } 15 | } -------------------------------------------------------------------------------- /VGATest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.13) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | set(PICO_COPY_TO_RAM 1) 9 | 10 | # Pull in Pico SDK (must be before project) 11 | include(pico_sdk_import.cmake) 12 | include(pico_extras_import.cmake) 13 | 14 | project(VGATest C CXX ASM) 15 | 16 | # Initialise the Pico SDK 17 | pico_sdk_init() 18 | 19 | # Add executable. Default name is the project name, version 0.1 20 | 21 | add_executable(VGATest VGATest.c image.c sdring.c vgaaudio.c) 22 | 23 | set_source_files_properties(image.c PROPERTIES COMPILE_FLAGS "-masm-syntax-unified") 24 | 25 | pico_set_program_name(VGATest "VGATest") 26 | pico_set_program_version(VGATest "0.1") 27 | 28 | pico_enable_stdio_uart(VGATest 0) 29 | pico_enable_stdio_usb(VGATest 0) 30 | 31 | pico_generate_pio_header(VGATest ${CMAKE_CURRENT_LIST_DIR}/vga.pio) 32 | 33 | # Slow down the flash to allow a faster system clock 34 | target_compile_definitions(VGATest PRIVATE PICO_FLASH_SPI_CLKDIV=4 PICO_AUDIO_DMA_IRQ=1 PICO_AUDIO_PIO=1) 35 | target_compile_definitions(bs2_default PRIVATE PICO_FLASH_SPI_CLKDIV=4) 36 | 37 | # Add the standard library to the build 38 | target_link_libraries(VGATest pico_stdlib pico_multicore) 39 | 40 | # Add any user requested libraries 41 | target_link_libraries(VGATest 42 | pico_sd_card 43 | hardware_dma 44 | hardware_pio 45 | hardware_interp 46 | pico_audio_i2s 47 | ) 48 | 49 | pico_add_extra_outputs(VGATest) 50 | 51 | -------------------------------------------------------------------------------- /VGATest/VGATest.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Michael Bell 2 | 3 | #include 4 | #include "pico/stdlib.h" 5 | #include "pico/multicore.h" 6 | #include "hardware/pio.h" 7 | #include "hardware/dma.h" 8 | #include "hardware/clocks.h" 9 | #include "hardware/irq.h" 10 | #include "hardware/structs/bus_ctrl.h" 11 | #include "hardware/regs/busctrl.h" 12 | #include "hardware/structs/ssi.h" 13 | #include "hardware/vreg.h" 14 | 15 | #include "vga.h" 16 | #include "vga.pio.h" 17 | 18 | #include "sdring.h" 19 | #include "vgaaudio.h" 20 | 21 | //#define RES_640_480_DOUBLE 22 | //#define RES_720p_SINGLE 23 | #define RES_720p_DOUBLE 24 | //#define RES_1080p_SINGLE 25 | 26 | #ifdef RES_640_480_DOUBLE 27 | // Timings for 640x480 28 | #define TIMING_V_PULSE 2 29 | #define TIMING_V_BACK (30 + TIMING_V_PULSE) 30 | #define TIMING_V_DISPLAY (480 + TIMING_V_BACK) 31 | #define TIMING_V_FRONT (9 + TIMING_V_DISPLAY) 32 | #define TIMING_H_FRONT (16 * 2) 33 | #define TIMING_H_PULSE (96 * 2) 34 | #define TIMING_H_BACK (48 * 2) 35 | #define TIMING_H_DISPLAY (640 * 2) 36 | #define CLOCK_VCO 1500 37 | #define CLOCK_PD1 5 38 | #define CLOCK_PD2 3 39 | #define CLOCK_RATE (100 * MHZ) 40 | #define CHANNEL_CLK_DIV 2 41 | #define VREG_VSEL VREG_VOLTAGE_1_10 42 | #endif 43 | #ifdef RES_720p_SINGLE 44 | // Timings for 720p 45 | #define TIMING_V_PULSE 5 46 | #define TIMING_V_BACK (19 + TIMING_V_PULSE) 47 | #define TIMING_V_DISPLAY (720 + TIMING_V_BACK) 48 | #define TIMING_V_FRONT (4 + TIMING_V_DISPLAY) 49 | #define TIMING_H_FRONT 64 50 | #define TIMING_H_PULSE 128 51 | #define TIMING_H_BACK 192 52 | #define TIMING_H_DISPLAY 1280 53 | #define CLOCK_VCO 1044 54 | #define CLOCK_PD1 7 55 | #define CLOCK_PD2 1 56 | #define CLOCK_RATE 149142857 57 | #define CHANNEL_CLK_DIV 1 58 | #define VREG_VSEL VREG_VOLTAGE_1_10 59 | #endif 60 | #ifdef RES_720p_DOUBLE 61 | #if 1 62 | // Timings for 720p at double sys clock 63 | #define TIMING_V_PULSE 5 64 | #define TIMING_V_BACK (21 + TIMING_V_PULSE) 65 | #define TIMING_V_DISPLAY (720 + TIMING_V_BACK) 66 | #define TIMING_V_FRONT (4 + TIMING_V_DISPLAY) 67 | #define TIMING_H_FRONT (64 * 2) 68 | #define TIMING_H_PULSE (130 * 2) 69 | #define TIMING_H_BACK (192 * 2) 70 | #define TIMING_H_DISPLAY (1280 * 2) 71 | //#define CLOCK_VCO 1488 72 | #define CLOCK_VCO 1500 73 | #define CLOCK_PD1 5 74 | #define CLOCK_PD2 1 75 | //#define CLOCK_RATE 297600 * KHZ 76 | #define CLOCK_RATE 300000 * KHZ 77 | #define CHANNEL_CLK_DIV 2 78 | #define VREG_VSEL VREG_VOLTAGE_1_15 79 | #else 80 | #define TIMING_V_PULSE 5 81 | #define TIMING_V_BACK (12 + TIMING_V_PULSE) 82 | #define TIMING_V_DISPLAY (720 + TIMING_V_BACK) 83 | #define TIMING_V_FRONT (4 + TIMING_V_DISPLAY) 84 | #define TIMING_H_FRONT (64 * 2) 85 | #define TIMING_H_PULSE (128 * 2) 86 | #define TIMING_H_BACK (192 * 2) 87 | #define TIMING_H_DISPLAY (1280 * 2) 88 | #define CLOCK_VCO 1140 89 | #define CLOCK_PD1 4 90 | #define CLOCK_PD2 1 91 | #define CLOCK_RATE 285000 * KHZ 92 | #define CHANNEL_CLK_DIV 2 93 | #define VREG_VSEL VREG_VOLTAGE_1_15 94 | #endif 95 | #endif 96 | #ifdef RES_1080p_SINGLE 97 | // Timings for 1080p 98 | #define TIMING_V_PULSE 8 99 | #define TIMING_V_BACK (6 + TIMING_V_PULSE) 100 | #define TIMING_V_DISPLAY (1080 + TIMING_V_BACK) 101 | #define TIMING_V_FRONT (17 + TIMING_V_DISPLAY) 102 | #define TIMING_H_FRONT 8 103 | #define TIMING_H_PULSE 32 104 | #define TIMING_H_BACK 40 105 | #define TIMING_H_DISPLAY 1920 106 | #define CLOCK_VCO 1332 107 | #define CLOCK_PD1 5 108 | #define CLOCK_PD2 1 109 | #define CLOCK_RATE 266400 * KHZ 110 | #define CHANNEL_CLK_DIV 1 111 | #define VREG_VSEL VREG_VOLTAGE_1_10 112 | #endif 113 | 114 | 115 | static uint16_t timing_row = 0; 116 | static uint16_t timing_phase = 0; 117 | static uint16_t eol_row = 0; 118 | 119 | void __no_inline_not_in_flash_func(drive_timing)() 120 | { 121 | while (!pio_sm_is_tx_fifo_full(vga_pio, vga_timing_sm)) { 122 | uint32_t instr; 123 | switch (timing_phase) { 124 | case 0: 125 | // Front Porch 126 | instr = 0x4000A042u; 127 | if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; 128 | instr |= (TIMING_H_FRONT - 3) << 16; 129 | pio_sm_put(vga_pio, vga_timing_sm, instr); 130 | break; 131 | 132 | case 1: 133 | // HSYNC 134 | instr = 0x0000A042u; 135 | if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; 136 | instr |= (TIMING_H_PULSE - 3) << 16; 137 | pio_sm_put(vga_pio, vga_timing_sm, instr); 138 | break; 139 | 140 | case 2: 141 | // Back Porch, trigger pixel channels if in display window 142 | instr = 0x4000C004u; 143 | if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; 144 | if (timing_row >= TIMING_V_BACK && timing_row < TIMING_V_DISPLAY) instr |= 0xC004u; 145 | else instr |= 0xA042u; 146 | instr |= (TIMING_H_BACK - 3) << 16; 147 | pio_sm_put(vga_pio, vga_timing_sm, instr); 148 | break; 149 | 150 | case 3: 151 | // Display, trigger next line at end 152 | instr = 0x4000C000u; 153 | if (timing_row == TIMING_V_FRONT - 1) instr = 0x4000C001u; 154 | if (timing_row >= TIMING_V_PULSE) instr |= 0x80000000u; 155 | instr |= (TIMING_H_DISPLAY - 3) << 16; 156 | pio_sm_put(vga_pio, vga_timing_sm, instr); 157 | 158 | if (++timing_row >= TIMING_V_FRONT) timing_row = 0; 159 | break; 160 | } 161 | 162 | timing_phase = (timing_phase + 1) & 3; 163 | } 164 | } 165 | 166 | void __no_inline_not_in_flash_func(timing_isr)() { 167 | drive_timing(); 168 | } 169 | 170 | void __no_inline_not_in_flash_func(end_of_line_isr)() { 171 | if (vga_pio->irq & 2) eol_row = 0; 172 | else eol_row++; 173 | hw_clear_bits(&vga_pio->irq, 0x3); 174 | 175 | if (eol_row == TIMING_V_DISPLAY || eol_row == TIMING_V_DISPLAY + 1) 176 | { 177 | display_end_frame(); 178 | } 179 | else if (eol_row == (TIMING_V_DISPLAY + 2) % (TIMING_V_FRONT)) 180 | { 181 | // Force SMs to get back into a good state: 182 | // Disable 183 | // Reset PC 184 | // Clear OSR (it is read when SM is re-enabled) 185 | // Drain FIFOs 186 | // Re-enable 187 | vga_pio->ctrl = 0x008; 188 | for (uint sm = vga_red_sm; sm <= vga_blue_sm; ++sm) 189 | { 190 | pio_sm_drain_tx_fifo(vga_pio, sm); 191 | pio_sm_exec_wait_blocking(vga_pio, sm, pio_encode_jmp(vga_channel_offset_end)); 192 | pio_sm_exec_wait_blocking(vga_pio, sm, pio_encode_mov(pio_osr, pio_null)); 193 | } 194 | vga_pio->ctrl = 0x70f; 195 | } 196 | else if (eol_row == (TIMING_V_DISPLAY + 3) % (TIMING_V_FRONT)) 197 | { 198 | display_start_new_frame(); 199 | } 200 | } 201 | 202 | // Setup must happen on core 1, to ensure interrupts are serviced fast enough. 203 | void vga_entry() { 204 | // Give core 1 priority access to the bus as it drives timing directly 205 | // and the shared PIO bus connection can get very busy with pixel data traffic 206 | //bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_PROC1_BITS; 207 | 208 | uint offset = pio_add_program(vga_pio, &vga_channel_program); 209 | assert(offset == 0); 210 | vga_channel_program_init(vga_pio, vga_red_sm, 0, CHANNEL_CLK_DIV); 211 | vga_channel_program_init(vga_pio, vga_green_sm, 6, CHANNEL_CLK_DIV); 212 | vga_channel_program_init(vga_pio, vga_blue_sm, 11, CHANNEL_CLK_DIV); 213 | vga_pio->ctrl = 0x707; 214 | 215 | offset = pio_add_program(vga_pio, &vga_timing_program); 216 | vga_timing_program_init(vga_pio, vga_timing_sm, offset, 16); 217 | 218 | hw_set_bits(&vga_pio->inte0, 0xf00); 219 | irq_set_exclusive_handler(PIO0_IRQ_0, end_of_line_isr); 220 | irq_set_enabled(PIO0_IRQ_0, true); 221 | 222 | hw_set_bits(&vga_pio->inte1, 0x080); 223 | irq_set_exclusive_handler(PIO0_IRQ_1, timing_isr); 224 | irq_set_priority(PIO0_IRQ_1, 0xC0); 225 | irq_set_enabled(PIO0_IRQ_1, true); 226 | 227 | audio_init(); 228 | 229 | // Notify setup is complete 230 | multicore_fifo_push_blocking(0); 231 | 232 | display_core1_loop(); 233 | } 234 | 235 | int main() 236 | { 237 | // Set required voltage 238 | vreg_set_voltage(VREG_VSEL); 239 | sleep_ms(10); 240 | 241 | // Set appropriate clock 242 | set_sys_clock_pll(CLOCK_VCO * MHZ, CLOCK_PD1, CLOCK_PD2); 243 | 244 | // Tell the periperal clock the new sys clock speed 245 | clock_configure(clk_peri, 246 | 0, 247 | CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, 248 | CLOCK_RATE, 249 | CLOCK_RATE); 250 | 251 | //stdio_init_all(); 252 | 253 | multicore_launch_core1(vga_entry); 254 | 255 | // Wait for setup complete then call into display code 256 | multicore_fifo_pop_blocking(); 257 | display_loop(); 258 | 259 | return 0; 260 | } 261 | -------------------------------------------------------------------------------- /VGATest/flash.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Michael Bell 2 | // You may use this under the terms of the BSD 3 clause license. 3 | 4 | #include 5 | #include "pico/stdlib.h" 6 | #include "hardware/dma.h" 7 | #include "hardware/structs/xip_ctrl.h" 8 | #include "hardware/irq.h" 9 | #include "hardware/structs/ssi.h" 10 | #include "hardware/regs/ssi.h" 11 | #include "pico/sync.h" 12 | 13 | #include "flash.h" 14 | 15 | #if defined(PICO_COPY_TO_RAM) && PICO_COPY_TO_RAM 16 | #define USE_SSI_DMA 17 | #endif 18 | 19 | #ifdef USE_SSI_DMA 20 | #define FLASH_MIN_TRANSFER 16 21 | #else 22 | #define FLASH_MIN_TRANSFER 4 23 | #endif 24 | 25 | uint32_t flash_buffer[FLASH_BUF_LEN_WORDS] __attribute__((aligned(1 << FLASH_BUF_LOG_SIZE_BYTES))); 26 | 27 | // Location in buffer that was returned to the user on last read 28 | // We should write past here. 29 | static volatile uint32_t* flash_prev_buffer_ptr; 30 | 31 | // Location in buffer that will be returned to the user on next read 32 | static volatile uint32_t* flash_buffer_ptr; 33 | 34 | // Words that have been requested 35 | static uint32_t flash_total_words_requested; 36 | 37 | // End of current transfer 38 | static uint32_t* flash_target_write_addr; 39 | 40 | // Source data in flash 41 | static uint32_t* flash_source_data_ptr; 42 | static uint32_t flash_source_data_len; 43 | 44 | // Start streaming more bytes from flash if there is space in the buffer 45 | static void __no_inline_not_in_flash_func(flash_transfer)() 46 | { 47 | uint32_t next_write_idx = (uint32_t*)dma_hw->ch[flash_dma_chan].write_addr - flash_buffer; 48 | next_write_idx &= FLASH_BUF_IDX_MASK; 49 | if (flash_buffer + next_write_idx == flash_prev_buffer_ptr) 50 | { 51 | // Fully caught up, don't start the next transfer yet. 52 | return; 53 | } 54 | 55 | if (flash_total_words_requested >= flash_source_data_len) 56 | { 57 | // Finished stream. 58 | return; 59 | } 60 | 61 | uint32_t* next_write_addr = (flash_buffer + next_write_idx); 62 | uint32_t words_to_read; 63 | if (next_write_addr < flash_prev_buffer_ptr) 64 | { 65 | words_to_read = flash_prev_buffer_ptr - next_write_addr; 66 | } 67 | else 68 | { 69 | words_to_read = FLASH_BUF_LEN_WORDS - next_write_idx + (flash_prev_buffer_ptr - flash_buffer); 70 | } 71 | 72 | if (words_to_read > flash_source_data_len - flash_total_words_requested) 73 | words_to_read = flash_source_data_len - flash_total_words_requested; 74 | 75 | // Don't make very small transfers. Don't make a transfer that would leave 76 | // a very small transfer to the end. 77 | if (words_to_read < FLASH_MIN_TRANSFER || 78 | ((flash_total_words_requested + words_to_read != flash_source_data_len) && 79 | (flash_total_words_requested + words_to_read + FLASH_MIN_TRANSFER > flash_source_data_len))) 80 | return; 81 | 82 | #ifdef USE_SSI_DMA 83 | ssi_hw->ssienr = 0; 84 | ssi_hw->ctrlr1 = words_to_read - 1; 85 | ssi_hw->dmacr = SSI_DMACR_BITS; 86 | ssi_hw->ssienr = 1; 87 | #endif 88 | 89 | dma_channel_transfer_to_buffer_now(flash_dma_chan, next_write_addr, words_to_read); 90 | #ifdef USE_SSI_DMA 91 | ssi_hw->dr0 = ((uintptr_t)(flash_source_data_ptr + flash_total_words_requested) << 8) | 0xa0; 92 | #endif 93 | 94 | flash_total_words_requested += words_to_read; 95 | flash_target_write_addr = flash_buffer + ((next_write_idx + words_to_read) & FLASH_BUF_IDX_MASK); 96 | } 97 | 98 | void __time_critical_func(flash_reset_stream)() 99 | { 100 | // Stop any running transfers 101 | if (dma_channel_is_busy(flash_dma_chan)) { 102 | #ifdef USE_SSI_DMA 103 | // TODO: Investigate if abort is possible. 104 | while (dma_channel_is_busy(flash_dma_chan)); 105 | #else 106 | dma_channel_abort(flash_dma_chan); 107 | xip_ctrl_hw->stream_ctr = 0; 108 | 109 | // Required to ensure the next flash transfer runs correctly. 110 | (void)*(io_rw_32*)XIP_NOCACHE_NOALLOC_BASE; 111 | #endif 112 | } 113 | 114 | // Clear the receive FIFO 115 | #ifndef USE_SSI_DMA 116 | while (!(xip_ctrl_hw->stat & XIP_STAT_FIFO_EMPTY)) 117 | (void) xip_ctrl_hw->stream_fifo; 118 | #endif 119 | 120 | // Reset read pointers to beginning 121 | flash_buffer_ptr = flash_prev_buffer_ptr = flash_buffer; 122 | 123 | // Read one less word to prevent the write address wrapping back to the beginning 124 | // and then we can't tell whether no data has been written or all the data has been written. 125 | flash_total_words_requested = FLASH_BUF_LEN_WORDS - 1; 126 | if (flash_total_words_requested > flash_source_data_len) flash_total_words_requested = flash_source_data_len; 127 | 128 | // Setup the transfer 129 | #ifdef USE_SSI_DMA 130 | ssi_hw->ssienr = 0; 131 | ssi_hw->ctrlr1 = flash_total_words_requested - 1; 132 | ssi_hw->dmacr = SSI_DMACR_BITS; 133 | ssi_hw->ssienr = 1; 134 | #else 135 | xip_ctrl_hw->stream_addr = (uintptr_t)flash_source_data_ptr; 136 | xip_ctrl_hw->stream_ctr = flash_source_data_len; 137 | #endif 138 | 139 | dma_channel_transfer_to_buffer_now(flash_dma_chan, flash_buffer, flash_total_words_requested); 140 | 141 | #ifdef USE_SSI_DMA 142 | ssi_hw->dr0 = ((uintptr_t)flash_source_data_ptr << 8) | 0xa0; 143 | #endif 144 | } 145 | 146 | void flash_set_stream(uint32_t* data, uint32_t len, bool start) 147 | { 148 | // Remember where we are transferring from 149 | flash_source_data_ptr = data; 150 | flash_source_data_len = len; 151 | 152 | flash_reset_stream(); 153 | } 154 | 155 | void flash_init(bool byte_swap) 156 | { 157 | // Setup the DMA channel 158 | dma_channel_config c = dma_channel_get_default_config(flash_dma_chan); 159 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 160 | channel_config_set_read_increment(&c, false); 161 | channel_config_set_write_increment(&c, true); 162 | channel_config_set_ring(&c, true, FLASH_BUF_LOG_SIZE_BYTES); 163 | channel_config_set_bswap(&c, !byte_swap); // Note data comes in byte swapped, so we reverse the request. 164 | 165 | #ifdef USE_SSI_DMA 166 | channel_config_set_dreq(&c, DREQ_XIP_SSIRX); 167 | dma_channel_configure( 168 | flash_dma_chan, // Channel to be configured 169 | &c, // The configuration we just created 170 | flash_buffer, // The write address 171 | (const void*)&ssi_hw->dr0, // The read address 172 | 0, // Number of transfers - set later 173 | false // Don't start yet 174 | ); 175 | #else 176 | channel_config_set_dreq(&c, DREQ_XIP_STREAM); 177 | dma_channel_configure( 178 | flash_dma_chan, // Channel to be configured 179 | &c, // The configuration we just created 180 | flash_buffer, // The write address 181 | (const void*)XIP_AUX_BASE, // The read address 182 | FLASH_BUF_LEN_WORDS, // Number of transfers - set later 183 | false // Don't start yet 184 | ); 185 | #endif 186 | } 187 | 188 | // Request data from flash. Returns amount of data that was 189 | // available - may be less than amount requested, and sets ptr_out 190 | // to point to it. The data returned will be discarded on next read. 191 | uint32_t __no_inline_not_in_flash_func(flash_get_data)(uint32_t len_req, uint32_t** ptr_out) 192 | { 193 | assert(len_req <= FLASH_BUF_LEN_WORDS / 2); 194 | assert(len_req > 0); 195 | 196 | uint32_t* write_ptr = (uint32_t*)dma_hw->ch[flash_dma_chan].write_addr; 197 | 198 | uint32_t words_available = 0; 199 | uint32_t true_words_available = 0; 200 | if (write_ptr < flash_buffer_ptr) { 201 | // Write pointer behind read pointer means we have wrapped. Can read until end of buffer 202 | words_available = FLASH_BUF_LEN_WORDS - (flash_buffer_ptr - flash_buffer); 203 | true_words_available = words_available + (write_ptr - flash_buffer); 204 | } 205 | else if (write_ptr > flash_buffer_ptr) 206 | { 207 | words_available = write_ptr - flash_buffer_ptr; 208 | true_words_available = words_available; 209 | } 210 | 211 | // Can now write over previously returned buffer. 212 | if (words_available > 0) 213 | flash_prev_buffer_ptr = flash_buffer_ptr; 214 | 215 | // Check whether we should now restart the transfer 216 | if (!dma_channel_is_busy(flash_dma_chan)) 217 | { 218 | flash_transfer(); 219 | } 220 | #ifndef USE_SSI_DMA 221 | else if (true_words_available < FLASH_BUF_LEN_WORDS / 2 && 222 | dma_hw->ch[flash_dma_chan].transfer_count < FLASH_BUF_LEN_WORDS / 4 && 223 | flash_source_data_len - flash_total_words_requested >= FLASH_BUF_LEN_WORDS / 4) 224 | { 225 | // Low buffer and near the end of the previous transfer so there's 226 | // more space for a longer transfer. Restart to avoid getting to the end 227 | // of a transfer and having to wait for another read to restart it. 228 | dma_channel_abort(flash_dma_chan); 229 | flash_total_words_requested -= (flash_target_write_addr - (uint32_t*)dma_hw->ch[flash_dma_chan].write_addr) & FLASH_BUF_IDX_MASK; 230 | flash_transfer(); 231 | } 232 | #endif 233 | 234 | if (words_available < len_req) len_req = words_available; 235 | 236 | *ptr_out = (uint32_t*)flash_buffer_ptr; 237 | flash_buffer_ptr += len_req; 238 | if (flash_buffer_ptr == flash_buffer + FLASH_BUF_LEN_WORDS) 239 | { 240 | flash_buffer_ptr = flash_buffer; 241 | } 242 | 243 | return len_req; 244 | } 245 | 246 | void __not_in_flash_func(flash_copy_data_blocking)(uint32_t* dst_ptr, uint32_t len_in_words) 247 | { 248 | uint32_t words_to_read = len_in_words; 249 | uint32_t* write_ptr = dst_ptr; 250 | while (words_to_read > 0) { 251 | uint32_t* data_ptr; 252 | uint32_t len_req = MIN(words_to_read, FLASH_BUF_LEN_WORDS / 4); 253 | uint32_t words_read = flash_get_data(len_req, &data_ptr); 254 | memcpy(write_ptr, data_ptr, words_read * sizeof(uint32_t)); 255 | write_ptr += words_read; 256 | words_to_read -= words_read; 257 | } 258 | } 259 | 260 | uint32_t __not_in_flash_func(flash_get_data_in_ringbuffer_blocking)(uint32_t len_req) 261 | { 262 | assert(len_req <= FLASH_BUF_LEN_WORDS / 2); 263 | assert(len_req > 0); 264 | 265 | uint32_t words_available = ((uint32_t*)dma_hw->ch[flash_dma_chan].write_addr - flash_buffer_ptr) & FLASH_BUF_IDX_MASK; 266 | 267 | if (words_available < FLASH_BUF_LEN_WORDS / 4 && 268 | !dma_channel_is_busy(flash_dma_chan) && 269 | flash_total_words_requested < flash_source_data_len) 270 | { 271 | assert((uint32_t*)dma_hw->ch[flash_dma_chan].write_addr != flash_prev_buffer_ptr); 272 | flash_transfer(); 273 | } 274 | 275 | while (words_available < len_req) { 276 | if (!dma_channel_is_busy(flash_dma_chan)) 277 | { 278 | assert((uint32_t*)dma_hw->ch[flash_dma_chan].write_addr != flash_prev_buffer_ptr); 279 | flash_transfer(); 280 | } 281 | words_available = ((uint32_t*)dma_hw->ch[flash_dma_chan].write_addr - flash_buffer_ptr) & FLASH_BUF_IDX_MASK; 282 | } 283 | 284 | uint32_t idx_out = flash_buffer_ptr - flash_buffer; 285 | uint32_t new_idx = (idx_out + len_req) & FLASH_BUF_IDX_MASK; 286 | flash_buffer_ptr = flash_buffer + new_idx; 287 | 288 | return idx_out; 289 | } 290 | 291 | void __not_in_flash_func(flash_release_ringbuffer)() 292 | { 293 | assert(flash_prev_buffer_ptr != flash_buffer_ptr); 294 | 295 | // Set prev pointer to one less than buffer pointer as buffer pointer must always be 296 | // one ahead of write pointer to prevent lock up. 297 | flash_prev_buffer_ptr = flash_buffer + (((flash_buffer_ptr - flash_buffer) - 1) & FLASH_BUF_IDX_MASK); 298 | 299 | // Check whether we should now restart the transfer 300 | if (!dma_channel_is_busy(flash_dma_chan)) 301 | { 302 | flash_transfer(); 303 | } 304 | } -------------------------------------------------------------------------------- /VGATest/flash.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Michael Bell 2 | // You may use this under the terms of the BSD 3 clause license. 3 | 4 | // Size of the ring buffer - must be a power of 2 <= 2^15 bytes. 5 | #define FLASH_BUF_LOG_SIZE_BYTES 14 6 | #define FLASH_BUF_LEN_WORDS (1 << (FLASH_BUF_LOG_SIZE_BYTES - 2)) 7 | #define FLASH_BUF_IDX_MASK (FLASH_BUF_LEN_WORDS - 1) 8 | 9 | // Note this is in the range of channels that the SD card uses 10 | // Currently assuming we'll use either SD or flash. 11 | #define flash_dma_chan 11 12 | 13 | // Configure the flash streaming resources 14 | void flash_init(bool byte_swap); 15 | 16 | // Set the data to stream and optionally start streaming. 17 | // If start_streaming is false then start the stream by calling reset_stream. 18 | void flash_set_stream(uint32_t* data, uint32_t len, bool start_streaming); 19 | 20 | // Request data from flash. Returns amount of data that was 21 | // available - may be less than amount requested, and sets ptr_out 22 | // to point to it. The data returned will be discarded on next read. 23 | uint32_t flash_get_data(uint32_t len_req_in_words, uint32_t** ptr_out); 24 | 25 | // Copy data out of the stream - user friendly interface to the above. 26 | void flash_copy_data_blocking(uint32_t* dst_ptr, uint32_t len_in_words); 27 | 28 | // Reset the stream to the beginning. 29 | void flash_reset_stream(); 30 | 31 | // Request a fixed amount of data from flash and access in ring buffer. 32 | // The flash ring buffer is aligned and you may need to wrap the returned index 33 | // Total length requested at any given time must be <= FLASH_BUF_LEN_WORDS - 1, 34 | // and ideally you should hold less than FLASH_BUF_LEN_WORDS / 2 so that 35 | // new data can be buffered. 36 | // Blocks until the requested amount of data has been read from flash. 37 | // Calling this a subsequent time does *not* release the data, it must be 38 | // released explicitly. 39 | uint32_t flash_get_data_in_ringbuffer_blocking(uint32_t len_req_in_words); 40 | 41 | // Release all data previously fetched with ringbuffer_blocking. 42 | void flash_release_ringbuffer(); 43 | 44 | extern uint32_t flash_buffer[FLASH_BUF_LEN_WORDS] __attribute__((aligned(1 << FLASH_BUF_LOG_SIZE_BYTES))); -------------------------------------------------------------------------------- /VGATest/logic_analyser.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | // PIO logic analyser example 8 | // 9 | // This program captures samples from a group of pins, at a fixed rate, once a 10 | // trigger condition is detected (level condition on one pin). The samples are 11 | // transferred to a capture buffer using the system DMA. 12 | // 13 | // 1 to 32 pins can be captured, at a sample rate no greater than system clock 14 | // frequency. 15 | 16 | #include 17 | 18 | #include "pico/stdlib.h" 19 | #include "hardware/pio.h" 20 | #include "hardware/dma.h" 21 | 22 | // Some logic to analyse: 23 | #include "hardware/structs/pwm.h" 24 | 25 | void logic_analyser_init(PIO pio, uint sm, uint pin_base, uint pin_count, float div) { 26 | // Load a program to capture n pins. This is just a single `in pins, n` 27 | // instruction with a wrap. 28 | uint16_t capture_prog_instr = pio_encode_in(pio_pins, pin_count); 29 | struct pio_program capture_prog = { 30 | .instructions = &capture_prog_instr, 31 | .length = 1, 32 | .origin = -1 33 | }; 34 | uint offset = pio_add_program(pio, &capture_prog); 35 | 36 | // Configure state machine to loop over this `in` instruction forever, 37 | // with autopush enabled. 38 | pio_sm_config c = pio_get_default_sm_config(); 39 | sm_config_set_in_pins(&c, pin_base); 40 | sm_config_set_wrap(&c, offset, offset); 41 | sm_config_set_clkdiv(&c, div); 42 | sm_config_set_in_shift(&c, true, true, 32); 43 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); 44 | pio_sm_init(pio, sm, offset, &c); 45 | } 46 | 47 | void logic_analyser_arm(PIO pio, uint sm, uint dma_chan, uint32_t *capture_buf, size_t capture_size_words, 48 | uint trigger_pin, bool trigger_level) { 49 | pio_sm_set_enabled(pio, sm, false); 50 | pio_sm_clear_fifos(pio, sm); 51 | 52 | dma_channel_config c = dma_channel_get_default_config(dma_chan); 53 | channel_config_set_read_increment(&c, false); 54 | channel_config_set_write_increment(&c, true); 55 | channel_config_set_dreq(&c, pio_get_dreq(pio, sm, false)); 56 | 57 | dma_channel_configure(dma_chan, &c, 58 | capture_buf, // Destinatinon pointer 59 | &pio->rxf[sm], // Source pointer 60 | capture_size_words, // Number of transfers 61 | true // Start immediately 62 | ); 63 | 64 | pio_sm_exec(pio, sm, pio_encode_wait_gpio(trigger_level, trigger_pin)); 65 | pio_sm_set_enabled(pio, sm, true); 66 | } 67 | 68 | void print_capture_buf(const uint32_t *buf, uint pin_base, uint pin_count, uint32_t n_samples) { 69 | // Display the capture buffer in text form, like this: 70 | // 00: __--__--__--__--__--__-- 71 | // 01: ____----____----____---- 72 | printf("Capture:\n"); 73 | for (int pin = 0; pin < pin_count; ++pin) { 74 | printf("%02d: ", pin + pin_base); 75 | for (int sample = 0; sample < n_samples; ++sample) { 76 | uint bit_index = pin + sample * pin_count; 77 | bool level = !!(buf[bit_index / 32] & 1u << (bit_index % 32)); 78 | printf(level ? "-" : "_"); 79 | } 80 | printf("\n"); 81 | } 82 | } 83 | 84 | #if 0 85 | int main() { 86 | stdio_init_all(); 87 | printf("PIO logic analyser example\n"); 88 | 89 | uint32_t capture_buf[(CAPTURE_PIN_COUNT * CAPTURE_N_SAMPLES + 31) / 32]; 90 | 91 | PIO pio = pio0; 92 | uint sm = 0; 93 | uint dma_chan = 0; 94 | 95 | logic_analyser_init(pio, sm, CAPTURE_PIN_BASE, CAPTURE_PIN_COUNT, 1.f); 96 | 97 | printf("Arming trigger\n"); 98 | logic_analyser_arm(pio, sm, dma_chan, capture_buf, //; 99 | (CAPTURE_PIN_COUNT * CAPTURE_N_SAMPLES + 31) / 32, 100 | CAPTURE_PIN_BASE, true); 101 | 102 | printf("Starting PWM example\n"); 103 | // PWM example: ----------------------------------------------------------- 104 | gpio_set_function(CAPTURE_PIN_BASE, GPIO_FUNC_PWM); 105 | gpio_set_function(CAPTURE_PIN_BASE + 1, GPIO_FUNC_PWM); 106 | // Topmost value of 3: count from 0 to 3 and then wrap, so period is 4 cycles 107 | pwm_hw->slice[0].top = 3; 108 | // Divide frequency by two to slow things down a little 109 | pwm_hw->slice[0].div = 4 << PWM_CH0_DIV_INT_LSB; 110 | // Set channel A to be high for 1 cycle each period (duty cycle 1/4) and 111 | // channel B for 3 cycles (duty cycle 3/4) 112 | pwm_hw->slice[0].cc = 113 | (1 << PWM_CH0_CC_A_LSB) | 114 | (3 << PWM_CH0_CC_B_LSB); 115 | // Enable this PWM slice 116 | pwm_hw->slice[0].csr = PWM_CH0_CSR_EN_BITS; 117 | // ------------------------------------------------------------------------ 118 | 119 | dma_channel_wait_for_finish_blocking(dma_chan); 120 | 121 | print_capture_buf(capture_buf, CAPTURE_PIN_BASE, CAPTURE_PIN_COUNT, CAPTURE_N_SAMPLES); 122 | } 123 | #endif -------------------------------------------------------------------------------- /VGATest/pico_extras_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_extras_import.cmake 2 | 3 | # This can be dropped into an external project to help locate pico-extras 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH)) 7 | set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH}) 8 | message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT)) 12 | set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT}) 13 | message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH)) 17 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | if (NOT PICO_EXTRAS_PATH) 22 | if (PICO_EXTRAS_FETCH_FROM_GIT) 23 | include(FetchContent) 24 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 25 | if (PICO_EXTRAS_FETCH_FROM_GIT_PATH) 26 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 27 | endif () 28 | FetchContent_Declare( 29 | PICO_EXTRAS 30 | GIT_REPOSITORY https://github.com/raspberrypi/pico-extras 31 | GIT_TAG master 32 | ) 33 | if (NOT PICO_EXTRAS) 34 | message("Downloading PICO EXTRAS") 35 | FetchContent_Populate(PICO_EXTRAS) 36 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_SOURCE_DIR}) 37 | endif () 38 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 39 | else () 40 | if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras") 41 | set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras) 42 | message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}") 43 | else() 44 | message(FATAL_ERROR 45 | "PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif() 48 | endif () 49 | endif () 50 | 51 | set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS") 52 | set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable") 53 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS") 54 | 55 | get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 56 | if (NOT EXISTS ${PICO_EXTRAS_PATH}) 57 | message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found") 58 | endif () 59 | 60 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE) 61 | 62 | add_subdirectory(${PICO_EXTRAS_PATH} pico_extras) -------------------------------------------------------------------------------- /VGATest/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /VGATest/sdring.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Michael Bell 2 | // You may use this under the terms of the BSD 3 clause license. 3 | 4 | // Size of the ring buffer - must be a power of 2 >= 2^11 and <= 2^15 bytes. 5 | #define SDRING_BUF_LOG_SIZE_BYTES 16 6 | #define SDRING_BUF_LEN_WORDS (1 << (SDRING_BUF_LOG_SIZE_BYTES - 2)) 7 | #define SDRING_BUF_IDX_MASK (SDRING_BUF_LEN_WORDS - 1) 8 | 9 | // Configure the flash streaming resources 10 | void sdring_init(bool byte_swap); 11 | 12 | // Set the data to stream and optionally start streaming. 13 | // If start_streaming is false then start the stream by calling reset_stream. 14 | void sdring_set_stream(uint32_t stream_idx, uint32_t start_block, uint32_t len_bytes, bool start_streaming, bool prime_buffer); 15 | 16 | // Reset the stream to the beginning. 17 | void sdring_reset_stream(uint32_t stream_idx, bool prime_buffer); 18 | 19 | // Whether the end of the file has been reached. 20 | bool sdring_eof(uint32_t stream_idx); 21 | 22 | // Get number of words available to read 23 | uint32_t sdring_words_available(uint32_t stream_idx); 24 | 25 | // Request a fixed amount of data from flash and access in ring buffer. 26 | // The flash ring buffer is aligned and you may need to wrap the returned index 27 | // Total length requested at any given time must be <= FLASH_BUF_LEN_WORDS - 1, 28 | // and ideally you should hold less than FLASH_BUF_LEN_WORDS / 2 so that 29 | // new data can be buffered. 30 | // Blocks until the requested amount of data has been read from flash. 31 | // Calling this a subsequent time does *not* release the data, it must be 32 | // released explicitly. 33 | uint32_t sdring_get_data_in_ringbuffer_blocking(uint32_t stream_idx, uint32_t len_req_in_words); 34 | 35 | // Release all data previously fetched with ringbuffer_blocking. 36 | void sdring_release_ringbuffer(uint32_t stream_idx); 37 | 38 | extern uint32_t sdring_buffer_0[SDRING_BUF_LEN_WORDS] __attribute__((aligned(1 << SDRING_BUF_LOG_SIZE_BYTES))); 39 | extern uint32_t sdring_buffer_1[SDRING_BUF_LEN_WORDS] __attribute__((aligned(1 << SDRING_BUF_LOG_SIZE_BYTES))); -------------------------------------------------------------------------------- /VGATest/vga.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Michael Bell 2 | 3 | #define vga_pio pio0 4 | #define vga_red_sm 0 5 | #define vga_green_sm 1 6 | #define vga_blue_sm 2 7 | #define vga_timing_sm 3 8 | 9 | // Loop to run any main display code, called on core0 after setup. 10 | void display_loop(); 11 | 12 | // Loop to run any worked code on core1, called after setup 13 | void display_core1_loop(); 14 | 15 | // These calls are made from within interrupts in timing critical code on core1, 16 | // throw to one of the main loops if anything interesting has to happen 17 | 18 | // Call to user code to start transfers for next frame 19 | void display_start_new_frame(); 20 | 21 | // Call to user to notify that the frame has ended, and any running 22 | // transfers should be aborted. 23 | void display_end_frame(); 24 | -------------------------------------------------------------------------------- /VGATest/vga.pio: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2021 Michael Bell 2 | 3 | ; One channel of 5 bit pixel colour output 4 | ; 5 | ; Idea is to run 3 of these channels simultaneously for RGB 6 | ; 7 | ; Encoding is packed into 32-bit words: 8 | ; 2 bits: Type: 9 | ; 00: End of line, data must be zero. 10 | ; 01: 3 RLE encoded blocks, 5 bits pixel, 5 run length - 2 (min length is 2). 11 | ; 11: 6 raw 5-bit pixels 12 | ; 30 bits data 13 | ; 1 pixel written per 2 clocks. 14 | 15 | .program vga_channel 16 | .origin 0 17 | jmp end ; PC = 0 -> End of line 18 | 19 | out pins, 5 ; PC = 1 -> RLE ; Pixel 1A 20 | jmp rle 21 | 22 | out pins, 5 ; PC = 3 -> Raw ; Pixel 1 23 | set y, 3 24 | raw_loop: 25 | out pins, 5 ; Pixel 2,3,4,5 26 | jmp y--, raw_loop 27 | out pins, 5 ; Pixel 6 28 | out pc, 2 29 | 30 | rle: 31 | out x, 5 ; Pixel 2A 32 | rleAL: 33 | jmp x--, rleAL2 34 | 35 | rleB: 36 | out pins, 5 ; Pixel 1B 37 | out x, 5 38 | rleBL: 39 | jmp x--, rleBL [1] ; Pixel B 40 | 41 | rleC: 42 | out pins, 5 ; Pixel 1C 43 | out x, 5 44 | rleCL2: 45 | jmp x--, rleCL ; Pixel C 46 | out pc, 2 47 | 48 | rleAL2: 49 | jmp rleAL ; Pixel A 50 | 51 | rleCL: 52 | jmp rleCL2 53 | 54 | PUBLIC end: 55 | mov pins, null 56 | out x, 32 57 | wait 1 irq 4 58 | pull noblock 59 | out pc, 2 60 | 61 | ; Set two sync pins based and then wait base don input word: 62 | ; 31: VSync 63 | ; 30: HSync 64 | ; 29-16: Delay duration in pixel clocks, total loop duration is delay + 3 65 | ; 15-0: Instruction to run after delay, sensible options are: 66 | ; nop : 0xa042 ; Do nothing 67 | ; irq 1 : 0xc001 ; Signal to CPU requesting next line 68 | ; irq 4 : 0xc004 ; Signal to data SMs to start. 69 | .program vga_timing 70 | out pins, 2 ; Set VS & HS 71 | out x, 14 ; Loop count 72 | sync_loop: 73 | jmp x--, sync_loop [1] 74 | out exec, 16 [1] 75 | 76 | % c-sdk { 77 | 78 | static inline void vga_channel_program_init(PIO pio, uint sm, uint base_pin, uint16_t clk_div) { 79 | for (int i = 0; i < 5; ++i) { 80 | pio_gpio_init(pio, base_pin + i); 81 | } 82 | pio_sm_set_consecutive_pindirs(pio, sm, base_pin, 5, true); 83 | 84 | pio_sm_config c = vga_channel_program_get_default_config(0); 85 | sm_config_set_out_pins(&c, base_pin, 5); 86 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 87 | sm_config_set_clkdiv_int_frac(&c, clk_div, 0); 88 | sm_config_set_out_shift(&c, false, true, 32); 89 | pio_sm_init(pio, sm, 0, &c); 90 | } 91 | 92 | static inline void vga_timing_program_init(PIO pio, uint sm, uint offset, uint sync_base_pin) { 93 | pio_gpio_init(pio, sync_base_pin); 94 | pio_gpio_init(pio, sync_base_pin + 1); 95 | pio_sm_set_consecutive_pindirs(pio, sm, sync_base_pin, 2, true); 96 | 97 | pio_sm_config c = vga_timing_program_get_default_config(offset); 98 | sm_config_set_out_pins(&c, sync_base_pin, 2); 99 | sm_config_set_out_shift(&c, false, true, 32); 100 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 101 | sm_config_set_clkdiv(&c, 1.0f); 102 | pio_sm_init(pio, sm, offset, &c); 103 | pio_sm_set_enabled(pio, sm, true); 104 | } 105 | 106 | %} 107 | -------------------------------------------------------------------------------- /VGATest/vgaaudio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pico/stdlib.h" 3 | #include "hardware/dma.h" 4 | 5 | #include "pico/audio_i2s.h" 6 | #include "sdring.h" 7 | 8 | #define START_SECTOR 10000000 9 | #define FILE_LEN_BYTES 35127300 10 | 11 | #define SDRING_INDEX_MASK_HALFWORDS ((1 << (SDRING_BUF_LOG_SIZE_BYTES - 1)) - 1) 12 | #define SAMPLES_PER_BUFFER (96) 13 | 14 | static struct audio_buffer_pool *internal_audio_init() { 15 | 16 | static audio_format_t audio_format = { 17 | .format = AUDIO_BUFFER_FORMAT_PCM_S16, 18 | .sample_freq = 44100, 19 | .channel_count = 2, 20 | }; 21 | 22 | static struct audio_buffer_format producer_format = { 23 | .format = &audio_format, 24 | .sample_stride = 4 25 | }; 26 | 27 | struct audio_buffer_pool *producer_pool = 28 | audio_new_producer_pool(&producer_format, 29 | 48, 30 | SAMPLES_PER_BUFFER); 31 | 32 | bool __unused ok; 33 | const struct audio_format *output_format; 34 | struct audio_i2s_config config = { 35 | .data_pin = 26, 36 | .clock_pin_base = 27, 37 | .dma_channel = 1, 38 | .pio_sm = 3, 39 | }; 40 | 41 | output_format = audio_i2s_setup(&audio_format, &config); 42 | if (!output_format) { 43 | panic("PicoAudio: Unable to open audio device.\n"); 44 | } 45 | 46 | ok = audio_i2s_connect(producer_pool); 47 | assert(ok); 48 | audio_i2s_set_enabled(true); 49 | 50 | return producer_pool; 51 | } 52 | 53 | static struct audio_buffer_pool *producer_pool; 54 | 55 | uint32_t audio_buffers_transferred = 0; 56 | 57 | extern volatile uint32_t debug_flag; 58 | 59 | void audio_reset() 60 | { 61 | audio_buffers_transferred = 0; 62 | sdring_set_stream(1, START_SECTOR, FILE_LEN_BYTES, true, true); 63 | } 64 | 65 | void audio_init() 66 | { 67 | producer_pool = internal_audio_init(); 68 | } 69 | 70 | void audio_transfer(bool one_buffer_only) 71 | { 72 | while (sdring_words_available(1) >= SAMPLES_PER_BUFFER) 73 | { 74 | struct audio_buffer *buffer = take_audio_buffer(producer_pool, false); 75 | if (!buffer) 76 | return; 77 | 78 | uint16_t* pcm_data_ptr = (uint16_t*)sdring_buffer_1; 79 | 80 | uint32_t sdring_idx = sdring_get_data_in_ringbuffer_blocking(1, buffer->max_sample_count) << 1; 81 | assert(buffer->buffer->size >= sizeof(uint16_t) * buffer->max_sample_count * 2); 82 | int16_t *samples = (int16_t*)buffer->buffer->bytes; 83 | for (uint i = 0; i < buffer->max_sample_count * 2; i++) { 84 | int16_t sample = pcm_data_ptr[(sdring_idx + i) & SDRING_INDEX_MASK_HALFWORDS]; 85 | samples[i] = sample >> 5u; 86 | } 87 | sdring_release_ringbuffer(1); 88 | 89 | buffer->sample_count = buffer->max_sample_count; 90 | give_audio_buffer(producer_pool, buffer); 91 | 92 | ++audio_buffers_transferred; 93 | //debug_flag = 1; 94 | 95 | if (one_buffer_only) 96 | break; 97 | } 98 | } -------------------------------------------------------------------------------- /VGATest/vgaaudio.h: -------------------------------------------------------------------------------- 1 | 2 | void audio_init(); 3 | 4 | void audio_reset(); 5 | 6 | void audio_transfer(); 7 | 8 | extern uint32_t audio_buffers_transferred; 9 | -------------------------------------------------------------------------------- /flash-stream/README.md: -------------------------------------------------------------------------------- 1 | # Library to stream flash data on Pico 2 | 3 | A simplified interface to stream a large amount of data (e.g. an image) from the on board flash. 4 | 5 | Automatically switches to the faster SSI DMA access mode if compiled with PICO_COPY_TO_RAM. 6 | 7 | The simplest way to use it is: 8 | ``` 9 | flash_init(false); 10 | flash_set_stream(data_ptr, data_len, true); 11 | ``` 12 | 13 | Then in a loop you can fetch chunks of data with: 14 | ``` 15 | flash_copy_data_blocking(out_ptr, len); 16 | ``` 17 | Note that data is copied out in 32-bit words and the length is specified in 32-bit words not bytes. 18 | 19 | There is also a lower level interface to access data directly from the ringbuffer it is streamed into, in order to avoid having extra copy, see the header. 20 | 21 | Currently it uses DMA channel 11 (hard coded). 22 | -------------------------------------------------------------------------------- /flash-stream/flash.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Michael Bell 2 | // You may use this under the terms of the BSD 3 clause license. 3 | 4 | #include 5 | #include "pico/stdlib.h" 6 | #include "hardware/dma.h" 7 | #include "hardware/structs/xip_ctrl.h" 8 | #include "hardware/irq.h" 9 | #include "hardware/structs/ssi.h" 10 | #include "hardware/regs/ssi.h" 11 | #include "pico/sync.h" 12 | 13 | #if defined(PICO_COPY_TO_RAM) && PICO_COPY_TO_RAM 14 | #define USE_SSI_DMA 15 | #endif 16 | 17 | #ifdef USE_SSI_DMA 18 | #define FLASH_MIN_TRANSFER 16 19 | #else 20 | #define FLASH_MIN_TRANSFER 4 21 | #endif 22 | 23 | #define FLASH_BUF_LOG_SIZE_BYTES 13 24 | #define FLASH_BUF_LEN_WORDS (1 << (FLASH_BUF_LOG_SIZE_BYTES - 2)) 25 | #define FLASH_BUF_IDX_MASK (FLASH_BUF_LEN_WORDS - 1) 26 | static uint32_t flash_buffer[FLASH_BUF_LEN_WORDS] __attribute__((aligned(1 << FLASH_BUF_LOG_SIZE_BYTES))); 27 | 28 | // Location in buffer that was returned to the user on last read 29 | // We should write past here. 30 | static volatile uint32_t* flash_prev_buffer_ptr; 31 | 32 | // Location in buffer that will be returned to the user on next read 33 | static volatile uint32_t* flash_buffer_ptr; 34 | 35 | // Words that have been requested 36 | static uint32_t flash_total_words_requested; 37 | 38 | // End of current transfer 39 | static uint32_t* flash_target_write_addr; 40 | 41 | // Source data in flash 42 | static uint32_t* flash_source_data_ptr; 43 | static uint32_t flash_source_data_len; 44 | 45 | // Note this is in the range of channels that the SD card uses 46 | // Currently assuming we'll use either SD or flash. 47 | #define flash_dma_chan 11 48 | 49 | // Start streaming more bytes from flash if there is space in the buffer 50 | static void __no_inline_not_in_flash_func(flash_transfer)() 51 | { 52 | uint32_t next_write_idx = (uint32_t*)dma_hw->ch[flash_dma_chan].write_addr - flash_buffer; 53 | next_write_idx &= FLASH_BUF_IDX_MASK; 54 | if (flash_buffer + next_write_idx == flash_prev_buffer_ptr) 55 | { 56 | // Fully caught up, don't start the next transfer yet. 57 | return; 58 | } 59 | 60 | if (flash_total_words_requested >= flash_source_data_len) 61 | { 62 | // Finished stream. 63 | return; 64 | } 65 | 66 | uint32_t* next_write_addr = (flash_buffer + next_write_idx); 67 | uint32_t words_to_read; 68 | if (next_write_addr < flash_prev_buffer_ptr) 69 | { 70 | words_to_read = flash_prev_buffer_ptr - next_write_addr; 71 | } 72 | else 73 | { 74 | words_to_read = FLASH_BUF_LEN_WORDS - next_write_idx + (flash_prev_buffer_ptr - flash_buffer); 75 | } 76 | 77 | if (words_to_read > flash_source_data_len - flash_total_words_requested) 78 | words_to_read = flash_source_data_len - flash_total_words_requested; 79 | 80 | // Don't make very small transfers. Don't make a transfer that would leave 81 | // a very small transfer to the end. 82 | if (words_to_read < FLASH_MIN_TRANSFER || 83 | ((flash_total_words_requested + words_to_read != flash_source_data_len) && 84 | (flash_total_words_requested + words_to_read + FLASH_MIN_TRANSFER > flash_source_data_len))) 85 | return; 86 | 87 | #ifdef USE_SSI_DMA 88 | ssi_hw->ssienr = 0; 89 | ssi_hw->ctrlr1 = words_to_read - 1; 90 | ssi_hw->dmacr = SSI_DMACR_BITS; 91 | ssi_hw->ssienr = 1; 92 | #endif 93 | 94 | dma_channel_transfer_to_buffer_now(flash_dma_chan, next_write_addr, words_to_read); 95 | #ifdef USE_SSI_DMA 96 | ssi_hw->dr0 = ((uintptr_t)(flash_source_data_ptr + flash_total_words_requested) << 8) | 0xa0; 97 | #endif 98 | 99 | flash_total_words_requested += words_to_read; 100 | flash_target_write_addr = flash_buffer + ((next_write_idx + words_to_read) & FLASH_BUF_IDX_MASK); 101 | } 102 | 103 | void __time_critical_func(flash_reset_stream)() 104 | { 105 | // Stop any running transfers 106 | if (dma_channel_is_busy(flash_dma_chan)) { 107 | #ifdef USE_SSI_DMA 108 | // TODO: Investigate if abort is possible. 109 | while (dma_channel_is_busy(flash_dma_chan)); 110 | #else 111 | dma_channel_abort(flash_dma_chan); 112 | xip_ctrl_hw->stream_ctr = 0; 113 | 114 | // Required to ensure the next flash transfer runs correctly. 115 | (void)*(io_rw_32*)XIP_NOCACHE_NOALLOC_BASE; 116 | #endif 117 | } 118 | 119 | // Clear the receive FIFO 120 | #ifndef USE_SSI_DMA 121 | while (!(xip_ctrl_hw->stat & XIP_STAT_FIFO_EMPTY)) 122 | (void) xip_ctrl_hw->stream_fifo; 123 | #endif 124 | 125 | // Reset read pointers to beginning 126 | flash_buffer_ptr = flash_prev_buffer_ptr = flash_buffer; 127 | 128 | // Read one less word to prevent the write address wrapping back to the beginning 129 | // and then we can't tell whether no data has been written or all the data has been written. 130 | flash_total_words_requested = FLASH_BUF_LEN_WORDS - 1; 131 | if (flash_total_words_requested > flash_source_data_len) flash_total_words_requested = flash_source_data_len; 132 | 133 | // Setup the transfer 134 | #ifdef USE_SSI_DMA 135 | ssi_hw->ssienr = 0; 136 | ssi_hw->ctrlr1 = flash_total_words_requested - 1; 137 | ssi_hw->dmacr = SSI_DMACR_BITS; 138 | ssi_hw->ssienr = 1; 139 | #else 140 | xip_ctrl_hw->stream_addr = (uintptr_t)flash_source_data_ptr; 141 | xip_ctrl_hw->stream_ctr = flash_source_data_len; 142 | #endif 143 | 144 | dma_channel_transfer_to_buffer_now(flash_dma_chan, flash_buffer, flash_total_words_requested); 145 | 146 | #ifdef USE_SSI_DMA 147 | ssi_hw->dr0 = ((uintptr_t)flash_source_data_ptr << 8) | 0xa0; 148 | #endif 149 | } 150 | 151 | void flash_set_stream(uint32_t* data, uint32_t len, bool start) 152 | { 153 | // Remember where we are transferring from 154 | flash_source_data_ptr = data; 155 | flash_source_data_len = len; 156 | 157 | flash_reset_stream(); 158 | } 159 | 160 | void flash_init(bool byte_swap) 161 | { 162 | // Setup the DMA channel 163 | dma_channel_config c = dma_channel_get_default_config(flash_dma_chan); 164 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 165 | channel_config_set_read_increment(&c, false); 166 | channel_config_set_write_increment(&c, true); 167 | channel_config_set_ring(&c, true, FLASH_BUF_LOG_SIZE_BYTES); 168 | channel_config_set_bswap(&c, !byte_swap); // Note data comes in byte swapped, so we reverse the request. 169 | 170 | #ifdef USE_SSI_DMA 171 | channel_config_set_dreq(&c, DREQ_XIP_SSIRX); 172 | dma_channel_configure( 173 | flash_dma_chan, // Channel to be configured 174 | &c, // The configuration we just created 175 | flash_buffer, // The write address 176 | (const void*)&ssi_hw->dr0, // The read address 177 | 0, // Number of transfers - set later 178 | false // Don't start yet 179 | ); 180 | #else 181 | channel_config_set_dreq(&c, DREQ_XIP_STREAM); 182 | dma_channel_configure( 183 | flash_dma_chan, // Channel to be configured 184 | &c, // The configuration we just created 185 | flash_buffer, // The write address 186 | (const void*)XIP_AUX_BASE, // The read address 187 | FLASH_BUF_LEN_WORDS, // Number of transfers - set later 188 | false // Don't start yet 189 | ); 190 | #endif 191 | } 192 | 193 | // Request data from flash. Returns amount of data that was 194 | // available - may be less than amount requested, and sets ptr_out 195 | // to point to it. The data returned will be discarded on next read. 196 | uint32_t __no_inline_not_in_flash_func(flash_get_data)(uint32_t len_req, uint32_t** ptr_out) 197 | { 198 | assert(len_req <= FLASH_BUF_LEN_WORDS / 2); 199 | assert(len_req > 0); 200 | 201 | uint32_t* write_ptr = (uint32_t*)dma_hw->ch[flash_dma_chan].write_addr; 202 | 203 | uint32_t words_available = 0; 204 | uint32_t true_words_available = 0; 205 | if (write_ptr < flash_buffer_ptr) { 206 | // Write pointer behind read pointer means we have wrapped. Can read until end of buffer 207 | words_available = FLASH_BUF_LEN_WORDS - (flash_buffer_ptr - flash_buffer); 208 | true_words_available = words_available + (write_ptr - flash_buffer); 209 | } 210 | else if (write_ptr > flash_buffer_ptr) 211 | { 212 | words_available = write_ptr - flash_buffer_ptr; 213 | true_words_available = words_available; 214 | } 215 | 216 | // Can now write over previously returned buffer. 217 | if (words_available > 0) 218 | flash_prev_buffer_ptr = flash_buffer_ptr; 219 | 220 | // Check whether we should now restart the transfer 221 | if (!dma_channel_is_busy(flash_dma_chan)) 222 | { 223 | flash_transfer(); 224 | } 225 | #ifndef USE_SSI_DMA 226 | else if (true_words_available < FLASH_BUF_LEN_WORDS / 2 && 227 | dma_hw->ch[flash_dma_chan].transfer_count < FLASH_BUF_LEN_WORDS / 4 && 228 | flash_source_data_len - flash_total_words_requested >= FLASH_BUF_LEN_WORDS / 4) 229 | { 230 | // Low buffer and near the end of the previous transfer so there's 231 | // more space for a longer transfer. Restart to avoid getting to the end 232 | // of a transfer and having to wait for another read to restart it. 233 | dma_channel_abort(flash_dma_chan); 234 | flash_total_words_requested -= (flash_target_write_addr - (uint32_t*)dma_hw->ch[flash_dma_chan].write_addr) & FLASH_BUF_IDX_MASK; 235 | flash_transfer(); 236 | } 237 | #endif 238 | 239 | if (words_available < len_req) len_req = words_available; 240 | 241 | *ptr_out = (uint32_t*)flash_buffer_ptr; 242 | flash_buffer_ptr += len_req; 243 | if (flash_buffer_ptr == flash_buffer + FLASH_BUF_LEN_WORDS) 244 | { 245 | flash_buffer_ptr = flash_buffer; 246 | } 247 | 248 | return len_req; 249 | } 250 | 251 | void __not_in_flash_func(flash_copy_data_blocking)(uint32_t* dst_ptr, uint32_t len_in_words) 252 | { 253 | uint32_t words_to_read = len_in_words; 254 | uint32_t* write_ptr = dst_ptr; 255 | while (words_to_read > 0) { 256 | uint32_t* data_ptr; 257 | uint32_t len_req = MIN(words_to_read, FLASH_BUF_LEN_WORDS / 4); 258 | uint32_t words_read = flash_get_data(len_req, &data_ptr); 259 | memcpy(write_ptr, data_ptr, words_read * sizeof(uint32_t)); 260 | write_ptr += words_read; 261 | words_to_read -= words_read; 262 | } 263 | } -------------------------------------------------------------------------------- /flash-stream/flash.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Michael Bell 2 | // You may use this under the terms of the BSD 3 clause license. 3 | 4 | // Configure the flash streaming resources 5 | void flash_init(bool byte_swap); 6 | 7 | // Set the data to stream and optionally start streaming. 8 | // If start_streaming is false then start the stream by calling reset_stream. 9 | void flash_set_stream(uint32_t* data, uint32_t len, bool start_streaming); 10 | 11 | // Request data from flash. Returns amount of data that was 12 | // available - may be less than amount requested, and sets ptr_out 13 | // to point to it. The data returned will be discarded on next read. 14 | uint32_t flash_get_data(uint32_t len_req_in_words, uint32_t** ptr_out); 15 | 16 | // Copy data out of the stream - user friendly interface to the above. 17 | void flash_copy_data_blocking(uint32_t* dst_ptr, uint32_t len_in_words); 18 | 19 | // Reset the stream to the beginning. 20 | void flash_reset_stream(); 21 | 22 | -------------------------------------------------------------------------------- /flashtest/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | duck.h 3 | *.gif 4 | -------------------------------------------------------------------------------- /flashtest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.12) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | # initalize pico_sdk from installed location 9 | # (note this can come from environment, CMake cache etc) 10 | set(PICO_SDK_PATH "/home/pi/pico/pico-sdk") 11 | 12 | # Pull in Pico SDK (must be before project) 13 | include(pico_sdk_import.cmake) 14 | 15 | project(flashtest C CXX) 16 | 17 | # Initialise the Pico SDK 18 | pico_sdk_init() 19 | 20 | # Add executable. Default name is the project name, version 0.1 21 | 22 | add_executable(flashtest flashtest.c ) 23 | 24 | pico_set_program_name(flashtest "flashtest") 25 | pico_set_program_version(flashtest "0.1") 26 | 27 | pico_enable_stdio_uart(flashtest 1) 28 | pico_enable_stdio_usb(flashtest 0) 29 | 30 | # Add the standard library to the build 31 | target_link_libraries(flashtest pico_stdlib hardware_dma) 32 | 33 | pico_add_extra_outputs(flashtest) 34 | 35 | -------------------------------------------------------------------------------- /flashtest/README.md: -------------------------------------------------------------------------------- 1 | # Test for aborting and immediately restarting a flash transfer 2 | 3 | This test should simply print "Hello, world!" and then loop forever 4 | streaming data from the flash. 5 | 6 | However, it appears the first word is lost on the third transfer if you 7 | repeatedly start and abort streaming transfers from the flash. 8 | 9 | Adding a 1 microsecond sleep after the abort fixes the test. 10 | -------------------------------------------------------------------------------- /flashtest/flash_data.h: -------------------------------------------------------------------------------- 1 | const uint32_t flash_data[] = { 2 | 0xffff, 3 | 1, 4 | 2, 5 | 3, 6 | 4, 7 | 5, 8 | 6, 9 | 7, 10 | 8, 11 | 9, 12 | 10, 13 | 11, 14 | 12, 15 | 13, 16 | 14, 17 | 15, 18 | 16, 19 | 17, 20 | 18, 21 | 19, 22 | 20, 23 | 21, 24 | 22, 25 | 23, 26 | 24, 27 | 25, 28 | 26, 29 | 27, 30 | 28, 31 | 29, 32 | 30, 33 | 31, 34 | 32, 35 | 33, 36 | 34, 37 | 35, 38 | 36, 39 | 37, 40 | 38, 41 | 39, 42 | 40, 43 | 41, 44 | 42, 45 | 43, 46 | 44, 47 | 45, 48 | 46, 49 | 47, 50 | 48, 51 | 49, 52 | 50, 53 | 51, 54 | 52, 55 | 53, 56 | 54, 57 | 55, 58 | 56, 59 | 57, 60 | 58, 61 | 59, 62 | 60, 63 | 61, 64 | 62, 65 | 63, 66 | 64, 67 | 65, 68 | 66, 69 | 67, 70 | 68, 71 | 69, 72 | 70, 73 | 71, 74 | 72, 75 | 73, 76 | 74, 77 | 75, 78 | 76, 79 | 77, 80 | 78, 81 | 79, 82 | 80, 83 | 81, 84 | 82, 85 | 83, 86 | 84, 87 | 85, 88 | 86, 89 | 87, 90 | 88, 91 | 89, 92 | 90, 93 | 91, 94 | 92, 95 | 93, 96 | 94, 97 | 95, 98 | 96, 99 | 97, 100 | 98, 101 | 99, 102 | 100, 103 | 101, 104 | 102, 105 | 103, 106 | 104, 107 | 105, 108 | 106, 109 | 107, 110 | 108, 111 | 109, 112 | 110, 113 | 111, 114 | 112, 115 | 113, 116 | 114, 117 | 115, 118 | 116, 119 | 117, 120 | 118, 121 | 119, 122 | 120, 123 | 121, 124 | 122, 125 | 123, 126 | 124, 127 | 125, 128 | 126, 129 | 127, 130 | 128, 131 | 129, 132 | 130, 133 | 131, 134 | 132, 135 | 133, 136 | 134, 137 | 135, 138 | 136, 139 | 137, 140 | 138, 141 | 139, 142 | 140, 143 | 141, 144 | 142, 145 | 143, 146 | 144, 147 | 145, 148 | 146, 149 | 147, 150 | 148, 151 | 149, 152 | 150, 153 | 151, 154 | 152, 155 | 153, 156 | 154, 157 | 155, 158 | 156, 159 | 157, 160 | 158, 161 | 159, 162 | 160, 163 | 161, 164 | 162, 165 | 163, 166 | 164, 167 | 165, 168 | 166, 169 | 167, 170 | 168, 171 | 169, 172 | 170, 173 | 171, 174 | 172, 175 | 173, 176 | 174, 177 | 175, 178 | 176, 179 | 177, 180 | 178, 181 | 179, 182 | 180, 183 | 181, 184 | 182, 185 | 183, 186 | 184, 187 | 185, 188 | 186, 189 | 187, 190 | 188, 191 | 189, 192 | 190, 193 | 191, 194 | 192, 195 | 193, 196 | 194, 197 | 195, 198 | 196, 199 | 197, 200 | 198, 201 | 199, 202 | 200, 203 | 201, 204 | 202, 205 | 203, 206 | 204, 207 | 205, 208 | 206, 209 | 207, 210 | 208, 211 | 209, 212 | 210, 213 | 211, 214 | 212, 215 | 213, 216 | 214, 217 | 215, 218 | 216, 219 | 217, 220 | 218, 221 | 219, 222 | 220, 223 | 221, 224 | 222, 225 | 223, 226 | 224, 227 | 225, 228 | 226, 229 | 227, 230 | 228, 231 | 229, 232 | 230, 233 | 231, 234 | 232, 235 | 233, 236 | 234, 237 | 235, 238 | 236, 239 | 237, 240 | 238, 241 | 239, 242 | 240, 243 | 241, 244 | 242, 245 | 243, 246 | 244, 247 | 245, 248 | 246, 249 | 247, 250 | 248, 251 | 249, 252 | 250, 253 | 251, 254 | 252, 255 | 253, 256 | 254, 257 | 255, 258 | }; 259 | -------------------------------------------------------------------------------- /flashtest/flash_data.py: -------------------------------------------------------------------------------- 1 | f = open("flash_data.h", "w") 2 | 3 | print("const uint32_t flash_data[] = {", file=f) 4 | 5 | for i in range(256): 6 | print(" %d," % i, file=f) 7 | 8 | print("};", file=f) 9 | -------------------------------------------------------------------------------- /flashtest/flashtest.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelBell/pico-projects/0b87012822fe7fe3bb55229e8e92f2179ae4eddf/flashtest/flashtest.bin -------------------------------------------------------------------------------- /flashtest/flashtest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "pico/stdlib.h" 4 | #include "hardware/dma.h" 5 | #include "hardware/regs/addressmap.h" 6 | #include "hardware/regs/ssi.h" 7 | #include "hardware/structs/xip_ctrl.h" 8 | #include "hardware/structs/ssi.h" 9 | 10 | #include "flash_data.h" 11 | 12 | #define FLASH_BUFFER_LEN 512 13 | uint flash_dma_chan; 14 | uint32_t flash_read_buffer[FLASH_BUFFER_LEN / 4]; 15 | uintptr_t flash_last_read_addr = 0; 16 | 17 | void flash_dma_init() 18 | { 19 | flash_dma_chan = dma_claim_unused_channel(true); 20 | 21 | dma_channel_config cfg = dma_channel_get_default_config(flash_dma_chan); 22 | channel_config_set_read_increment(&cfg, false); 23 | channel_config_set_write_increment(&cfg, true); 24 | channel_config_set_dreq(&cfg, DREQ_XIP_STREAM); 25 | dma_channel_configure( 26 | flash_dma_chan, 27 | &cfg, 28 | (void *) flash_read_buffer, // Write addr 29 | (const void *) XIP_AUX_BASE, // Read addr 30 | FLASH_BUFFER_LEN / 4, // Transfer count 31 | false 32 | ); 33 | } 34 | 35 | void flash_start_dma(uint8_t* addr) 36 | { 37 | while (!(xip_ctrl_hw->stat & XIP_STAT_FIFO_EMPTY)) 38 | (void) xip_ctrl_hw->stream_fifo; 39 | xip_ctrl_hw->stream_addr = (uintptr_t)addr & (~3u); 40 | xip_ctrl_hw->stream_ctr = FLASH_BUFFER_LEN / 4; 41 | 42 | dma_channel_transfer_to_buffer_now(flash_dma_chan, flash_read_buffer, FLASH_BUFFER_LEN / 4); 43 | flash_last_read_addr = (uintptr_t)addr & (~3u); 44 | } 45 | 46 | void flash_stop_dma() 47 | { 48 | if (dma_channel_is_busy(flash_dma_chan)) { 49 | xip_ctrl_hw->stream_ctr = 0; 50 | dma_channel_abort(flash_dma_chan); 51 | //while (ssi_hw->sr & SSI_SR_BUSY_BITS); 52 | (void)ssi_hw->sr; 53 | //sleep_us(1); // Transfer reliable if you uncomment this 54 | } 55 | } 56 | 57 | int main() 58 | { 59 | //stdio_init_all(); 60 | 61 | flash_dma_init(); 62 | 63 | //puts("Hello, world!"); 64 | 65 | // Start the streaming transfer from flash 66 | flash_start_dma((uint8_t*)flash_data); 67 | 68 | uint count_correct = 0; 69 | while (1) { 70 | uint32_t buff[4]; 71 | 72 | // Wait until write address on the DMA has been incremented twice 73 | uint32_t read_end_addr = ((uintptr_t)flash_read_buffer + 7); 74 | while (dma_hw->ch[flash_dma_chan].write_addr < read_end_addr); 75 | 76 | // Copy out the beginning of the buffer being DMA'd into 77 | memcpy(buff, flash_read_buffer, 16); 78 | 79 | // Stop and immediately restart the streaming transfer 80 | flash_stop_dma(); 81 | flash_start_dma((uint8_t*)flash_data); 82 | 83 | // First word to be transferred should always be 0xffff 84 | // Rest of the words just count up from 1. 85 | if (buff[0] != 0xffff) { 86 | asm volatile ("bkpt"); 87 | // Print out the first four words that were in the receiving buffer, 88 | // and the count of correct transfers 89 | //printf("%08x %08x %08x %08x %d\n", buff[0], buff[1], buff[2], buff[3], count_correct); 90 | 91 | // Reset and pause 92 | count_correct = 0; 93 | flash_stop_dma(); 94 | sleep_ms(100); 95 | 96 | // Clear the buffer and start again 97 | memset(flash_read_buffer, 0, FLASH_BUFFER_LEN); 98 | flash_start_dma((uint8_t*)flash_data); 99 | } 100 | else count_correct++; 101 | 102 | if (!(count_correct & 0xff)) { 103 | printf("%d cycles OK\n", count_correct); 104 | sleep_ms(100); 105 | } 106 | } 107 | 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /flashtest/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /giftest/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | duck.h 3 | *.gif 4 | -------------------------------------------------------------------------------- /giftest/AnimatedGIF.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 BitBank Software, Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | //=========================================================================== 12 | 13 | #ifndef __ANIMATEDGIF__ 14 | #define __ANIMATEDGIF__ 15 | #if defined( PICO_BUILD ) || defined( __MACH__ ) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) 16 | #include 17 | #include 18 | #include 19 | #include 20 | #define memcpy_P memcpy 21 | #define PROGMEM 22 | #else 23 | #include 24 | #endif 25 | // 26 | // GIF Animator 27 | // Written by Larry Bank 28 | // Copyright (c) 2020 BitBank Software, Inc. 29 | // bitbank@pobox.com 30 | // 31 | // Designed to decode images up to 480x320 32 | // using less than 22K of RAM 33 | // 34 | 35 | /* GIF Defines and variables */ 36 | #define MAX_CHUNK_SIZE 255 37 | #define LZW_BUF_SIZE (12*MAX_CHUNK_SIZE) 38 | #define LZW_HIGHWATER (10*MAX_CHUNK_SIZE) 39 | #define MAX_WIDTH 480 40 | #define FILE_BUF_SIZE 8192 41 | 42 | #define PIXEL_FIRST 0 43 | #define PIXEL_LAST 4096 44 | #define LINK_UNUSED 5911 // 0x1717 to use memset 45 | #define LINK_END 5912 46 | #define MAX_HASH 5003 47 | #define MAXMAXCODE 4096 48 | 49 | // RGB565 pixel byte order in the palette 50 | #define BIG_ENDIAN_PIXELS 0 51 | #define LITTLE_ENDIAN_PIXELS 1 52 | 53 | enum { 54 | GIF_PALETTE_RGB565 = 0, // default 55 | GIF_PALETTE_RGB888 56 | }; 57 | // 58 | // Draw callback pixel type 59 | // RAW = 8-bit palettized pixels requiring transparent pixel handling 60 | // COOKED = 16 or 24-bpp fully rendered pixels ready for display 61 | // 62 | enum { 63 | GIF_DRAW_RAW = 0, 64 | GIF_DRAW_COOKED 65 | }; 66 | 67 | enum { 68 | GIF_SUCCESS = 0, 69 | GIF_DECODE_ERROR, 70 | GIF_TOO_WIDE, 71 | GIF_INVALID_PARAMETER, 72 | GIF_UNSUPPORTED_FEATURE, 73 | GIF_FILE_NOT_OPEN, 74 | GIF_EARLY_EOF, 75 | GIF_EMPTY_FRAME, 76 | GIF_BAD_FILE, 77 | GIF_ERROR_MEMORY 78 | }; 79 | 80 | typedef struct gif_file_tag 81 | { 82 | int32_t iPos; // current file position 83 | int32_t iSize; // file size 84 | uint8_t *pData; // memory file pointer 85 | void * fHandle; // class pointer to File/SdFat or whatever you want 86 | } GIFFILE; 87 | 88 | typedef struct gif_info_tag 89 | { 90 | int32_t iFrameCount; // total frames in file 91 | int32_t iDuration; // duration of animation in milliseconds 92 | int32_t iMaxDelay; // maximum frame delay 93 | int32_t iMinDelay; // minimum frame delay 94 | } GIFINFO; 95 | 96 | typedef struct gif_draw_tag 97 | { 98 | int iX, iY; // Corner offset of this frame on the canvas 99 | int y; // current line being drawn (0 = top line of image) 100 | int iWidth, iHeight; // size of this frame 101 | uint8_t *pPixels; // 8-bit source pixels for this line 102 | uint16_t *pPalette; // little or big-endian RGB565 palette entries (default) 103 | uint8_t *pPalette24; // RGB888 palette (optional) 104 | uint8_t ucTransparent; // transparent color 105 | uint8_t ucHasTransparency; // flag indicating the transparent color is in use 106 | uint8_t ucDisposalMethod; // frame disposal method 107 | uint8_t ucBackground; // background color 108 | } GIFDRAW; 109 | 110 | // Callback function prototypes 111 | typedef int32_t (GIF_READ_CALLBACK)(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen); 112 | typedef int32_t (GIF_SEEK_CALLBACK)(GIFFILE *pFile, int32_t iPosition); 113 | typedef void (GIF_DRAW_CALLBACK)(GIFDRAW *pDraw); 114 | typedef void * (GIF_OPEN_CALLBACK)(const char *szFilename, int32_t *pFileSize); 115 | typedef void (GIF_CLOSE_CALLBACK)(void *pHandle); 116 | typedef void * (GIF_ALLOC_CALLBACK)(uint32_t iSize); 117 | typedef void (GIF_FREE_CALLBACK)(void *buffer); 118 | // 119 | // our private structure to hold a GIF image decode state 120 | // 121 | typedef struct gif_image_tag 122 | { 123 | int iWidth, iHeight, iCanvasWidth, iCanvasHeight; 124 | int iX, iY; // GIF corner offset 125 | int iBpp; 126 | int iError; // last error 127 | int iFrameDelay; // delay in milliseconds for this frame 128 | int iXCount, iYCount; // decoding position in image (countdown values) 129 | int iLZWOff; // current LZW data offset 130 | int iLZWSize; // current quantity of data in the LZW buffer 131 | int iCommentPos; // file offset of start of comment data 132 | short sCommentLen; // length of comment 133 | GIF_READ_CALLBACK *pfnRead; 134 | GIF_SEEK_CALLBACK *pfnSeek; 135 | GIF_DRAW_CALLBACK *pfnDraw; 136 | GIF_OPEN_CALLBACK *pfnOpen; 137 | GIF_CLOSE_CALLBACK *pfnClose; 138 | GIFFILE GIFFile; 139 | unsigned char *pFrameBuffer; 140 | unsigned char *pPixels, *pOldPixels; 141 | unsigned char ucLineBuf[MAX_WIDTH]; // current line 142 | unsigned char ucFileBuf[FILE_BUF_SIZE]; // holds temp data and pixel stack 143 | unsigned short pPalette[384]; // can hold RGB565 or RGB888 - set in begin() 144 | unsigned short pLocalPalette[384]; // color palettes for GIF images 145 | unsigned char ucLZW[LZW_BUF_SIZE]; // holds 6 chunks (6x255) of GIF LZW data packed together 146 | unsigned short usGIFTable[4096]; 147 | unsigned char ucGIFPixels[8192]; 148 | unsigned char bEndOfFrame; 149 | unsigned char ucGIFBits, ucBackground, ucTransparent, ucCodeStart, ucMap, bUseLocalPalette, ucLittleEndian; 150 | unsigned char ucPaletteType; // RGB565 or RGB888 151 | unsigned char ucDrawType; // RAW or COOKED 152 | } GIFIMAGE; 153 | 154 | #ifdef __cplusplus 155 | // 156 | // The GIF class wraps portable C code which does the actual work 157 | // 158 | class AnimatedGIF 159 | { 160 | public: 161 | int open(uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw); 162 | int open(const char *szFilename, GIF_OPEN_CALLBACK *pfnOpen, GIF_CLOSE_CALLBACK *pfnClose, GIF_READ_CALLBACK *pfnRead, GIF_SEEK_CALLBACK *pfnSeek, GIF_DRAW_CALLBACK *pfnDraw); 163 | void close(); 164 | void reset(); 165 | void begin(int iEndian, unsigned char ucPaletteType = GIF_PALETTE_RGB565); 166 | int playFrame(bool bSync, int *delayMilliseconds); 167 | int getCanvasWidth(); 168 | int allocFrameBuf(GIF_ALLOC_CALLBACK *pfnAlloc); 169 | int setDrawType(int iType); 170 | int freeFrameBuf(GIF_FREE_CALLBACK *pfnFree); 171 | uint8_t *getFrameBuf(); 172 | int getCanvasHeight(); 173 | int getInfo(GIFINFO *pInfo); 174 | int getLastError(); 175 | int getComment(char *destBuffer); 176 | 177 | private: 178 | GIFIMAGE _gif; 179 | }; 180 | #else 181 | // C interface 182 | int GIF_openRAM(GIFIMAGE *pGIF, uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw); 183 | int GIF_openFile(GIFIMAGE *pGIF, const char *szFilename, GIF_DRAW_CALLBACK *pfnDraw); 184 | void GIF_close(GIFIMAGE *pGIF); 185 | void GIF_begin(GIFIMAGE *pGIF, int iEndian , unsigned char ucPaletteType); 186 | void GIF_reset(GIFIMAGE *pGIF); 187 | int GIF_playFrame(GIFIMAGE *pGIF, int *delayMilliseconds); 188 | int GIF_getCanvasWidth(GIFIMAGE *pGIF); 189 | int GIF_getCanvasHeight(GIFIMAGE *pGIF); 190 | int GIF_getComment(GIFIMAGE *pGIF, char *destBuffer); 191 | int GIF_getInfo(GIFIMAGE *pGIF, GIFINFO *pInfo); 192 | int GIF_getLastError(GIFIMAGE *pGIF); 193 | #endif // __cplusplus 194 | 195 | // Due to unaligned memory causing an exception, we have to do these macros the slow way 196 | #define INTELSHORT(p) ((*p) + (*(p+1)<<8)) 197 | #define INTELLONG(p) ((*p) + (*(p+1)<<8) + (*(p+2)<<16) + (*(p+3)<<24)) 198 | #define MOTOSHORT(p) (((*(p))<<8) + (*(p+1))) 199 | #define MOTOLONG(p) (((*p)<<24) + ((*(p+1))<<16) + ((*(p+2))<<8) + (*(p+3))) 200 | 201 | // Must be a 32-bit target processor 202 | #define REGISTER_WIDTH 32 203 | 204 | #endif // __ANIMATEDGIF__ 205 | -------------------------------------------------------------------------------- /giftest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.12) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | set(PICO_BOARD pimoroni_tiny2040) 9 | 10 | # initalize pico_sdk from installed location 11 | # (note this can come from environment, CMake cache etc) 12 | set(PICO_SDK_PATH "/home/pi/pico/pico-sdk") 13 | 14 | # Pull in Pico SDK (must be before project) 15 | include(pico_sdk_import.cmake) 16 | 17 | project(giftest C CXX) 18 | 19 | # Initialise the Pico SDK 20 | pico_sdk_init() 21 | 22 | # Add executable. Default name is the project name, version 0.1 23 | 24 | add_executable(giftest giftest.c st7789_lcd.c gif.c) 25 | 26 | pico_set_program_name(giftest "giftest") 27 | pico_set_program_version(giftest "0.1") 28 | 29 | pico_enable_stdio_uart(giftest 1) 30 | pico_enable_stdio_usb(giftest 0) 31 | 32 | pico_generate_pio_header(giftest ${CMAKE_CURRENT_LIST_DIR}/st7789_lcd.pio) 33 | 34 | # Add the standard library to the build 35 | target_link_libraries(giftest pico_stdlib) 36 | 37 | # Add any user requested libraries 38 | target_link_libraries(giftest 39 | hardware_pio 40 | hardware_dma 41 | hardware_pwm 42 | ) 43 | 44 | pico_add_extra_outputs(giftest) 45 | 46 | -------------------------------------------------------------------------------- /giftest/README.md: -------------------------------------------------------------------------------- 1 | # Test of animated GIF library on Pico using an ST7789 display 2 | 3 | The animated GIF library is from here: https://github.com/bitbank2/AnimatedGIF 4 | 5 | I haven't committed duck.h, you can generate it by getting the gif from https://media.giphy.com/media/wsUtUtLR3A2XPvfLVs/giphy-downsized.gif and formatting it with: 6 | ``` 7 | xxd -i giphy-downsized.gif duck.h 8 | ``` 9 | Then add const in front of the array name to ensure it goes into flash (it's too big for RAM). 10 | -------------------------------------------------------------------------------- /giftest/giftest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pico/stdlib.h" 3 | #include "hardware/pio.h" 4 | #include "hardware/dma.h" 5 | #include "hardware/regs/addressmap.h" 6 | #include "hardware/regs/ssi.h" 7 | #include "hardware/structs/xip_ctrl.h" 8 | #include "hardware/structs/ssi.h" 9 | #include "hardware/pwm.h" 10 | 11 | #include "st7789_lcd.h" 12 | #include "AnimatedGIF.h" 13 | //#include "duck.h" 14 | 15 | GIFIMAGE gif; 16 | PIO pio = pio0; 17 | uint sm = 0; 18 | uint chan[2]; 19 | uint chan_idx = 0; 20 | 21 | #define DISPLAY_WIDTH 240 22 | #define DISPLAY_HEIGHT 240 23 | 24 | int minx = 0; 25 | int miny = 0; 26 | int maxx = DISPLAY_WIDTH - 1; 27 | int maxy = DISPLAY_HEIGHT - 1; 28 | 29 | static uint8_t* gif_addr = (uint8_t*)0x10010000; 30 | static uint32_t gif_len = 2164211; 31 | 32 | #define PIXEL_DATA_LEN (32 + ((DISPLAY_WIDTH + 1)>>1)) 33 | uint32_t pixel_data[2][PIXEL_DATA_LEN]; 34 | 35 | void draw(GIFDRAW *pDraw) { 36 | int width = MIN(pDraw->iWidth, maxx - minx - pDraw->iX); 37 | int y = MIN(pDraw->iY + pDraw->y, maxy - miny) + miny; 38 | 39 | dma_channel_wait_for_finish_blocking(chan[chan_idx]); 40 | uint8_t* dataPtr = pDraw->pPixels; 41 | uint32_t* pixelPtr = pixel_data[chan_idx]; 42 | 43 | if (pDraw->ucDisposalMethod == 2) 44 | { 45 | pixelPtr += st7789_add_pixels_at_cmd(pixelPtr, pDraw->iX + minx, y, width); 46 | uint16_t bgPixel = pDraw->pPalette[pDraw->ucBackground]; 47 | for (int x = 0; x < width; ++x) { 48 | uint8_t data = *dataPtr++; 49 | uint16_t pixel; 50 | if (data == pDraw->ucTransparent) 51 | pixel = bgPixel; 52 | else 53 | pixel = pDraw->pPalette[data]; 54 | if (x & 1) *pixelPtr++ |= pixel; 55 | else *pixelPtr = pixel << 16; 56 | } 57 | if (width & 1) pixelPtr++; 58 | } 59 | else 60 | { 61 | if (pDraw->ucHasTransparency) { 62 | uint8_t tData = pDraw->ucTransparent; 63 | int x = 0, count = 0; 64 | uint32_t* pixelEndPtr = pixel_data[chan_idx] + PIXEL_DATA_LEN - 6; 65 | 66 | while (x < width) { 67 | while (*dataPtr++ == tData && x < width) ++x; 68 | if (x == width) break; 69 | 70 | // Back up as we incremented past the first non-transparent pixel 71 | dataPtr--; 72 | 73 | int startx = x; 74 | uint32_t* startPixelPtr = pixelPtr; 75 | pixelPtr += 5; 76 | for (; x < width && *dataPtr != tData; ++x) { 77 | uint16_t pixel = pDraw->pPalette[*dataPtr++]; 78 | if ((x - startx) & 1) { 79 | *pixelPtr++ |= pixel; 80 | if (pixelPtr >= pixelEndPtr) { 81 | ++x; 82 | break; 83 | } 84 | } 85 | else *pixelPtr = pixel << 16; 86 | } 87 | if ((x - startx) & 1) pixelPtr++; 88 | 89 | st7789_add_pixels_at_cmd(startPixelPtr, pDraw->iX + minx + startx, y, x - startx); 90 | 91 | if (pixelPtr >= pixelEndPtr && x < width) 92 | { 93 | st7789_dma_buffer(chan, chan_idx, pixel_data[chan_idx], pixelPtr - pixel_data[chan_idx]); 94 | chan_idx ^= 1; 95 | dma_channel_wait_for_finish_blocking(chan[chan_idx]); 96 | pixelPtr = pixel_data[chan_idx]; 97 | pixelEndPtr = pixel_data[chan_idx] + PIXEL_DATA_LEN - 6; 98 | } 99 | } 100 | } 101 | else 102 | { 103 | pixelPtr += st7789_add_pixels_at_cmd(pixelPtr, pDraw->iX + minx, y, width); 104 | for (int x = 0; x < width; ++x) { 105 | uint16_t pixel = pDraw->pPalette[*dataPtr++]; 106 | if (x & 1) *pixelPtr++ |= pixel; 107 | else *pixelPtr = pixel << 16; 108 | } 109 | if (width & 1) pixelPtr++; 110 | } 111 | } 112 | 113 | st7789_dma_buffer(chan, chan_idx, pixel_data[chan_idx], pixelPtr - pixel_data[chan_idx]); 114 | chan_idx ^= 1; 115 | } 116 | 117 | #define FLASH_BUFFER_LEN 268 118 | uint flash_dma_chan; 119 | uint32_t flash_read_buffer[FLASH_BUFFER_LEN / 4]; 120 | uintptr_t flash_last_read_addr = 0; 121 | 122 | void flash_dma_init() 123 | { 124 | flash_dma_chan = dma_claim_unused_channel(true); 125 | 126 | dma_channel_config cfg = dma_channel_get_default_config(flash_dma_chan); 127 | channel_config_set_read_increment(&cfg, false); 128 | channel_config_set_write_increment(&cfg, true); 129 | channel_config_set_dreq(&cfg, DREQ_XIP_STREAM); 130 | dma_channel_configure( 131 | flash_dma_chan, 132 | &cfg, 133 | (void *) flash_read_buffer, // Write addr 134 | (const void *) XIP_AUX_BASE, // Read addr 135 | FLASH_BUFFER_LEN / 4, // Transfer count 136 | false 137 | ); 138 | } 139 | 140 | void flash_start_dma(uint8_t* addr) 141 | { 142 | while (!(xip_ctrl_hw->stat & XIP_STAT_FIFO_EMPTY)) 143 | (void) xip_ctrl_hw->stream_fifo; 144 | xip_ctrl_hw->stream_addr = (uintptr_t)addr & (~3u); 145 | xip_ctrl_hw->stream_ctr = FLASH_BUFFER_LEN / 4; 146 | 147 | dma_channel_transfer_to_buffer_now(flash_dma_chan, (void*)flash_read_buffer, FLASH_BUFFER_LEN / 4); 148 | flash_last_read_addr = (uintptr_t)addr & (~3u); 149 | } 150 | 151 | void flash_stop_dma() 152 | { 153 | if (dma_channel_is_busy(flash_dma_chan)) { 154 | xip_ctrl_hw->stream_ctr = 0; 155 | //while (ssi_hw->sr & SSI_SR_BUSY_BITS); 156 | (void)*(io_rw_32*)XIP_NOCACHE_NOALLOC_BASE; 157 | dma_channel_abort(flash_dma_chan); 158 | //sleep_us(1); // My transfer becomes unreliable if I remove this line 159 | } 160 | } 161 | 162 | int32_t gifRead(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen) 163 | { 164 | int32_t iBytesRead; 165 | 166 | iBytesRead = iLen; 167 | if ((pFile->iSize - pFile->iPos) < iLen) 168 | iBytesRead = pFile->iSize - pFile->iPos; 169 | if (iBytesRead <= 0) 170 | return 0; 171 | 172 | int32_t buffer_offset = (pFile->pData + pFile->iPos) - (uint8_t*)flash_last_read_addr; 173 | if (buffer_offset >= 0 && 174 | buffer_offset + iBytesRead <= FLASH_BUFFER_LEN - 4) 175 | { 176 | uint32_t read_end_addr = ((uintptr_t)flash_read_buffer + buffer_offset + iBytesRead - 1) | 3; 177 | while (dma_channel_is_busy(flash_dma_chan) && dma_hw->ch[flash_dma_chan].write_addr < read_end_addr); 178 | 179 | asm volatile ("" ::: "memory"); 180 | memcpy(pBuf, (uint8_t*)flash_read_buffer + buffer_offset, iBytesRead); 181 | pFile->iPos += iBytesRead; 182 | if (buffer_offset + iBytesRead > FLASH_BUFFER_LEN - 259) { 183 | flash_stop_dma(); 184 | flash_start_dma(&pFile->pData[pFile->iPos]); 185 | } 186 | } 187 | else 188 | { 189 | flash_stop_dma(); 190 | memcpy(pBuf, &pFile->pData[pFile->iPos], iBytesRead); 191 | pFile->iPos += iBytesRead; 192 | 193 | flash_start_dma(&pFile->pData[pFile->iPos]); 194 | } 195 | 196 | return iBytesRead; 197 | } 198 | 199 | const uint LED_BRIGHTNESS[3] = { 240, 248, 230 }; 200 | 201 | int main() 202 | { 203 | stdio_init_all(); 204 | 205 | gpio_set_function(TINY2040_LED_R_PIN, GPIO_FUNC_PWM); 206 | gpio_set_function(TINY2040_LED_G_PIN, GPIO_FUNC_PWM); 207 | gpio_set_function(TINY2040_LED_B_PIN, GPIO_FUNC_PWM); 208 | pwm_set_wrap(pwm_gpio_to_slice_num(TINY2040_LED_R_PIN), 255); 209 | pwm_set_wrap(pwm_gpio_to_slice_num(TINY2040_LED_B_PIN), 255); 210 | pwm_set_clkdiv(pwm_gpio_to_slice_num(TINY2040_LED_R_PIN), 128.f); 211 | pwm_set_clkdiv(pwm_gpio_to_slice_num(TINY2040_LED_B_PIN), 128.f); 212 | pwm_set_gpio_level(TINY2040_LED_R_PIN, 256); 213 | pwm_set_gpio_level(TINY2040_LED_G_PIN, 256); 214 | pwm_set_gpio_level(TINY2040_LED_B_PIN, 256); 215 | pwm_set_enabled(pwm_gpio_to_slice_num(TINY2040_LED_R_PIN), true); 216 | pwm_set_enabled(pwm_gpio_to_slice_num(TINY2040_LED_B_PIN), true); 217 | 218 | st7789_init(pio, sm); 219 | st7789_create_dma_channels(pio, sm, chan); 220 | 221 | st7789_start_pixels(pio, sm, DISPLAY_WIDTH * DISPLAY_HEIGHT); 222 | st7789_dma_repeat_pixel_one_channel(chan[0], 0, DISPLAY_WIDTH * DISPLAY_HEIGHT); 223 | 224 | flash_dma_init(); 225 | 226 | GIF_begin(&gif, LITTLE_ENDIAN_PIXELS, GIF_PALETTE_RGB565); 227 | while (1) { 228 | uint32_t frame_num = 0; 229 | gpio_put(TINY2040_LED_R_PIN, 1); 230 | gpio_put(TINY2040_LED_G_PIN, 1); 231 | gpio_put(TINY2040_LED_B_PIN, 1); 232 | 233 | flash_stop_dma(); 234 | flash_start_dma(gif_addr); 235 | if (!GIF_openRAM(&gif, (uint8_t *)gif_addr, gif_len, draw)) { 236 | printf("Open failed\n"); 237 | } 238 | else 239 | { 240 | //gif.pfnRead = gifRead; 241 | minx = MAX(0, (DISPLAY_WIDTH - gif.iCanvasWidth) >> 1); 242 | maxx = MIN(DISPLAY_WIDTH - 1, minx + gif.iCanvasWidth); 243 | miny = MAX(0, (DISPLAY_HEIGHT - gif.iCanvasHeight) >> 1); 244 | maxy = MIN(DISPLAY_HEIGHT - 1, minx + gif.iCanvasHeight); 245 | 246 | int delay = 10; 247 | absolute_time_t start_time = get_absolute_time(); 248 | while (GIF_playFrame(&gif, &delay)) { 249 | pwm_set_gpio_level(TINY2040_LED_R_PIN + ((frame_num>>1) % 3), 256); 250 | ++frame_num; 251 | pwm_set_gpio_level(TINY2040_LED_R_PIN + ((frame_num>>1) % 3), LED_BRIGHTNESS[(frame_num>>1) % 3]); 252 | 253 | uint32_t frame_time = absolute_time_diff_us(start_time, get_absolute_time()); 254 | delay *= 1000; 255 | delay -= frame_time; 256 | printf("Frame time %dus\n", frame_time); 257 | delay -= 100; 258 | if (delay > 0) 259 | sleep_us(delay); 260 | 261 | start_time = get_absolute_time(); 262 | } 263 | GIF_close(&gif); 264 | printf("Loop done\n"); 265 | } 266 | } 267 | 268 | return 0; 269 | } 270 | -------------------------------------------------------------------------------- /giftest/mkuf2.sh: -------------------------------------------------------------------------------- 1 | uf2conv.py -f rp2040 -b 0x10010000 $1 -o gifdata.uf2 2 | stat --printf="Data size to set in giftest.c: %s\n" $1 3 | -------------------------------------------------------------------------------- /giftest/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /giftest/st7789_lcd.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include "pico/stdlib.h" 11 | #include "hardware/pio.h" 12 | #include "hardware/gpio.h" 13 | #include "hardware/dma.h" 14 | 15 | #include "st7789_lcd.pio.h" 16 | 17 | #define SCREEN_WIDTH 240 18 | #define SCREEN_HEIGHT 240 19 | 20 | #define PIN_DIN 5 21 | #define PIN_CLK 6 22 | #define PIN_CS 7 // Must be CLK + 1 23 | #define PIN_DC 4 24 | #define PIN_RESET 9 25 | #define PIN_BL 3 26 | 27 | #define SERIAL_CLK_DIV 2.f 28 | 29 | // Format: cmd length (including cmd byte), post delay in units of 5 ms, then cmd payload 30 | // Note the delays have been shortened a little 31 | static const uint8_t st7789_init_seq[] = { 32 | 1, 20, 0x01, // Software reset 33 | 1, 10, 0x11, // Exit sleep mode 34 | 2, 2, 0x3a, 0x55, // Set colour mode to 16 bit 35 | 2, 0, 0x36, 0x00, // Set MADCTL: row then column, refresh is bottom to top ???? 36 | 5, 0, 0x2a, 0x00, 0x00, 0x00, 0xf0, // CASET: column addresses from 0 to 240 (f0) 37 | 5, 0, 0x2b, 0x00, 0x00, 0x00, 0xf0, // RASET: row addresses from 0 to 240 (f0) 38 | 1, 2, 0x21, // Inversion on, then 10 ms delay (supposedly a hack?) 39 | 1, 2, 0x13, // Normal display on, then 10 ms delay 40 | 1, 2, 0x29, // Main screen turn on, then wait 500 ms 41 | 0 // Terminate list 42 | }; 43 | 44 | static inline uint32_t st7789_encode_cmd(uint8_t cmd, uint32_t data_count) 45 | { 46 | uint32_t instr = 0x100; 47 | if (data_count) instr = (data_count * 8) << 8; 48 | instr |= cmd; 49 | return instr; 50 | } 51 | 52 | static inline void lcd_write_cmd(PIO pio, uint sm, const uint8_t *cmd, size_t count) { 53 | st7789_lcd_wait_idle(pio, sm); 54 | 55 | uint32_t instr = st7789_encode_cmd(cmd[0], count - 1); 56 | pio_sm_put(pio, sm, instr); 57 | 58 | if (count >= 2) { 59 | assert(count <= 5); 60 | 61 | instr = 0; 62 | for (int i = 1; i < count; ++i) { 63 | instr <<= 8; 64 | instr |= cmd[i]; 65 | } 66 | instr <<= 8 * (5 - count); 67 | pio_sm_put(pio, sm, instr); 68 | } 69 | } 70 | 71 | void st7789_init(PIO pio, uint sm) { 72 | uint offset = pio_add_program(pio, &st7789_lcd_program); 73 | st7789_lcd_program_init(pio, sm, offset, PIN_DIN, PIN_CLK, PIN_DC, SERIAL_CLK_DIV); 74 | 75 | gpio_init(PIN_RESET); 76 | gpio_init(PIN_BL); 77 | gpio_set_dir(PIN_RESET, GPIO_OUT); 78 | gpio_set_dir(PIN_BL, GPIO_OUT); 79 | 80 | gpio_put(PIN_RESET, 1); 81 | gpio_put(PIN_BL, 0); 82 | 83 | const uint8_t *cmd = st7789_init_seq; 84 | while (*cmd) { 85 | lcd_write_cmd(pio, sm, cmd + 2, *cmd); 86 | sleep_ms(*(cmd + 1) * 5); 87 | cmd += *cmd + 2; 88 | } 89 | 90 | gpio_put(PIN_BL, 1); 91 | } 92 | 93 | void st7789_start_pixels(PIO pio, uint sm, uint32_t num_pixels) { 94 | st7789_lcd_wait_idle(pio, sm); 95 | 96 | // RAMWR 97 | uint32_t cmd = st7789_encode_cmd(0x2c, num_pixels * 2); 98 | pio_sm_put(pio, sm, cmd); 99 | } 100 | 101 | void st7789_start_pixels_at(PIO pio, uint sm, uint8_t x, uint8_t y, uint32_t num_pixels) { 102 | uint8_t ca_cmd[] = {0x2a, 0x00, x, 0x00, 0xf0}; // CASET: column addresses from 0 to 240 (f0) 103 | uint8_t ra_cmd[] = {0x2b, 0x00, y, 0x00, 0xf0}; // RASET: row addresses from 0 to 240 (f0) 104 | 105 | lcd_write_cmd(pio, sm, ca_cmd, 5); 106 | lcd_write_cmd(pio, sm, ra_cmd, 5); 107 | 108 | // RAMWR 109 | uint32_t cmd = st7789_encode_cmd(0x2c, num_pixels * 2); 110 | pio_sm_put(pio, sm, cmd); 111 | } 112 | 113 | uint32_t st7789_add_pixels_at_cmd(uint32_t* buffer, uint8_t x, uint8_t y, uint32_t num_pixels) { 114 | uint8_t ca_cmd[] = {0x2a, 0x00, x, 0x00, 0xf0}; 115 | uint8_t ra_cmd[] = {0x2b, 0x00, y, 0x00, 0xf0}; 116 | 117 | *buffer++ = st7789_encode_cmd(0x2a, 4); 118 | *buffer++ = 0xf0 | ((uint32_t)x << 16); 119 | *buffer++ = st7789_encode_cmd(0x2b, 4); 120 | *buffer++ = 0xf0 | ((uint32_t)y << 16); 121 | *buffer++ = st7789_encode_cmd(0x2c, num_pixels * 2); 122 | 123 | return 5; 124 | } 125 | 126 | void st7789_create_dma_channels(PIO pio, uint sm, uint chan[2]) 127 | { 128 | chan[0] = dma_claim_unused_channel(true); 129 | chan[1] = dma_claim_unused_channel(true); 130 | 131 | dma_channel_config c = dma_channel_get_default_config(chan[0]); 132 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 133 | channel_config_set_dreq(&c, pio_get_dreq(pio, sm, true)); 134 | channel_config_set_read_increment(&c, true); 135 | channel_config_set_write_increment(&c, false); 136 | 137 | dma_channel_configure( 138 | chan[0], // Channel to be configured 139 | &c, // The configuration we just created 140 | &pio->txf[sm], // The write address 141 | NULL, // The initial read address - set later 142 | 0, // Number of transfers - set later 143 | false // Don't start yet 144 | ); 145 | 146 | c = dma_channel_get_default_config(chan[1]); 147 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 148 | channel_config_set_dreq(&c, pio_get_dreq(pio, sm, true)); 149 | channel_config_set_read_increment(&c, true); 150 | channel_config_set_write_increment(&c, false); 151 | 152 | dma_channel_configure( 153 | chan[1], // Channel to be configured 154 | &c, // The configuration we just created 155 | &pio->txf[sm], // The write address 156 | NULL, // The initial read address - set later 157 | 0, // Number of transfers - set later 158 | false // Don't start yet 159 | ); 160 | } 161 | 162 | static inline void st7789_chain_or_trigger(uint this_chan, uint other_chan, uint ctrl) 163 | { 164 | if (dma_channel_is_busy(other_chan)) { 165 | // Other channel is busy, chain this one to it 166 | dma_channel_hw_addr(this_chan)->al1_ctrl = ctrl; 167 | uint other_ctrl = dma_channel_hw_addr(other_chan)->ctrl_trig; 168 | other_ctrl &= ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS; 169 | other_ctrl |= this_chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB; 170 | dma_channel_hw_addr(other_chan)->al1_ctrl = other_ctrl; 171 | 172 | if (!dma_channel_is_busy(other_chan) && !dma_channel_is_busy(this_chan)) { 173 | // Manually start this channel 174 | dma_channel_hw_addr(this_chan)->ctrl_trig = ctrl; 175 | } 176 | } else { 177 | dma_channel_hw_addr(this_chan)->ctrl_trig = ctrl; 178 | } 179 | } 180 | 181 | void st7789_dma_buffer(uint chan[2], uint chan_idx, const uint32_t* data, uint len) 182 | { 183 | uint this_chan = chan[chan_idx]; 184 | uint other_chan = chan[chan_idx ^ 1]; 185 | 186 | // Ensure any previous transfer is finished. 187 | dma_channel_wait_for_finish_blocking(this_chan); 188 | 189 | dma_channel_hw_addr(this_chan)->read_addr = (uintptr_t)data; 190 | dma_channel_hw_addr(this_chan)->transfer_count = len; 191 | uint ctrl = dma_channel_hw_addr(this_chan)->ctrl_trig; 192 | ctrl &= ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS; 193 | ctrl |= DMA_CH0_CTRL_TRIG_INCR_READ_BITS | (this_chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB); 194 | 195 | st7789_chain_or_trigger(this_chan, other_chan, ctrl); 196 | } 197 | 198 | void st7789_dma_buffer_one_channel(uint chan, const uint32_t* data, uint len) 199 | { 200 | dma_channel_wait_for_finish_blocking(chan); 201 | dma_channel_hw_addr(chan)->read_addr = (uintptr_t)data; 202 | dma_channel_hw_addr(chan)->transfer_count = len; 203 | uint ctrl = dma_channel_hw_addr(chan)->ctrl_trig; 204 | ctrl &= ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS; 205 | ctrl |= DMA_CH0_CTRL_TRIG_INCR_READ_BITS | (chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB); 206 | dma_channel_hw_addr(chan)->ctrl_trig = ctrl; 207 | } 208 | 209 | static uint32_t pixel_to_dma[3]; 210 | 211 | void st7789_dma_repeat_pixel(uint chan[2], uint chan_idx, uint16_t pixel, uint repeats) 212 | { 213 | uint this_chan = chan[chan_idx]; 214 | uint other_chan = chan[chan_idx ^ 1]; 215 | 216 | dma_channel_wait_for_finish_blocking(this_chan); 217 | 218 | pixel_to_dma[chan_idx] = ((uint32_t)pixel << 16) | pixel; 219 | dma_channel_hw_addr(this_chan)->read_addr = (uintptr_t)&pixel_to_dma[chan_idx]; 220 | dma_channel_hw_addr(this_chan)->transfer_count = (repeats+1)>>1; 221 | uint ctrl = dma_channel_hw_addr(this_chan)->ctrl_trig; 222 | ctrl &= ~(DMA_CH0_CTRL_TRIG_INCR_READ_BITS | DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS); 223 | ctrl |= this_chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB; 224 | 225 | st7789_chain_or_trigger(this_chan, other_chan, ctrl); 226 | } 227 | 228 | void st7789_dma_repeat_pixel_one_channel(uint chan, uint16_t pixel, uint repeats) 229 | { 230 | dma_channel_wait_for_finish_blocking(chan); 231 | 232 | pixel_to_dma[2] = ((uint32_t)pixel << 16) | pixel; 233 | //printf(" P: %08X * %d\n", pixel, (repeats+1)>>1); 234 | dma_channel_hw_addr(chan)->read_addr = (uintptr_t)&pixel_to_dma[2]; 235 | dma_channel_hw_addr(chan)->transfer_count = (repeats+1)>>1; 236 | uint ctrl = dma_channel_hw_addr(chan)->ctrl_trig; 237 | ctrl &= ~(DMA_CH0_CTRL_TRIG_INCR_READ_BITS | DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS); 238 | ctrl |= chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB; 239 | dma_channel_hw_addr(chan)->ctrl_trig = ctrl; 240 | } 241 | 242 | -------------------------------------------------------------------------------- /giftest/st7789_lcd.h: -------------------------------------------------------------------------------- 1 | void st7789_init(PIO pio, uint sm); 2 | void st7789_start_pixels(PIO pio, uint sm, uint32_t num_pixels); 3 | void st7789_start_pixels_at(PIO pio, uint sm, uint8_t x, uint8_t y, uint32_t num_pixels); 4 | uint32_t st7789_add_pixels_at_cmd(uint32_t* buffer, uint8_t x, uint8_t y, uint32_t num_pixels); 5 | void st7789_create_dma_channels(PIO pio, uint sm, uint chan[2]); 6 | void st7789_dma_buffer(uint chan[2], uint chan_idx, const uint32_t* data, uint len); 7 | void st7789_dma_repeat_pixel(uint chan[2], uint chan_idx, uint16_t pixel, uint repeats); 8 | void st7789_dma_buffer_one_channel(uint chan, const uint32_t* data, uint len); 9 | void st7789_dma_repeat_pixel_one_channel(uint chan, uint16_t pixel, uint repeats); 10 | 11 | #include "st7789_lcd.pio.h" 12 | -------------------------------------------------------------------------------- /giftest/st7789_lcd.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | .program st7789_lcd 8 | .side_set 3 9 | 10 | ; This transmits 32-bit DMA'd data of the form: 11 | ; Header: | 24 bits | 8 bits | 12 | ; | Data bit count - 1 | Command | 13 | ; Data: (data bit count)/8 data bytes 14 | ; Bytes are sent from MSB first, so the last word should be right aligned 15 | ; if the data byte count isn't a multiple of 4. 16 | ; If the data byte count is not a multiple of 4 then the remaining part 17 | ; of the word is discarded. 18 | ; If no data is to be sent "data bit count - 1" should be 0, else it should 19 | ; be the number of bits to be sent minus one. 20 | 21 | ; Data on OUT pin 0 22 | ; 23 | ; Clock on side-set pin 0 24 | ; CS on side-set pin 1 25 | ; 26 | ; D/C on set pin 0 27 | 28 | top: 29 | set pins, 1 side 0b10 ; D/C pin high when stalled 30 | out x, 24 side 0b10 ; Read data length 31 | jmp !x, top side 0b10 ; Discard if we just read all zeros 32 | 33 | set y, 7 side 0b10 ; Send 8 bit command 34 | set pins, 0 side 0b10 ; Start cmd 35 | cmd_loop: 36 | out pins, 1 side 0b00 37 | jmp y--, cmd_loop side 0b01 38 | 39 | jmp x--, data_next side 0b00 ; Data length 1 means no data, 40 | data_next: ; we never just have 1 bit 41 | jmp !x, top side 0b00 ; If no data go to next command 42 | 43 | set pins, 1 side 0b00 ; Start data 44 | data_loop: ; Send data 45 | out pins, 1 side 0b00 46 | jmp x--, data_loop side 0b01 47 | 48 | 49 | % c-sdk { 50 | 51 | static inline void st7789_lcd_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clk_pin, uint dc_pin, float clk_div) { 52 | pio_gpio_init(pio, data_pin); 53 | pio_gpio_init(pio, clk_pin); 54 | pio_gpio_init(pio, clk_pin + 1); 55 | pio_gpio_init(pio, dc_pin); 56 | pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, true); 57 | pio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 2, true); 58 | pio_sm_set_consecutive_pindirs(pio, sm, dc_pin, 1, true); 59 | pio_sm_config c = st7789_lcd_program_get_default_config(offset); 60 | sm_config_set_sideset_pins(&c, clk_pin); 61 | sm_config_set_out_pins(&c, data_pin, 1); 62 | sm_config_set_set_pins(&c, dc_pin, 1); 63 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 64 | sm_config_set_clkdiv(&c, clk_div); 65 | sm_config_set_out_shift(&c, false, true, 32); 66 | pio_sm_init(pio, sm, offset, &c); 67 | pio_sm_set_enabled(pio, sm, true); 68 | } 69 | 70 | // SM is done when it stalls on an empty FIFO 71 | 72 | static inline void st7789_lcd_wait_idle(PIO pio, uint sm) { 73 | uint32_t sm_stall_mask = 1u << (sm + PIO_FDEBUG_TXSTALL_LSB); 74 | pio->fdebug = sm_stall_mask; 75 | while (!(pio->fdebug & sm_stall_mask)) 76 | ; 77 | } 78 | %} 79 | -------------------------------------------------------------------------------- /lasertest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.12) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | # initalize pico_sdk from installed location 9 | # (note this can come from environment, CMake cache etc) 10 | set(PICO_SDK_PATH "/home/pi/pico/pico-sdk") 11 | 12 | # Pull in Pico SDK (must be before project) 13 | include(pico_sdk_import.cmake) 14 | 15 | project(lasertest C CXX) 16 | 17 | # Initialise the Pico SDK 18 | pico_sdk_init() 19 | 20 | add_subdirectory(VL53L1X) 21 | 22 | # Add executable. Default name is the project name, version 0.1 23 | 24 | add_executable(lasertest lasertest.c ) 25 | 26 | pico_set_program_name(lasertest "lasertest") 27 | pico_set_program_version(lasertest "0.1") 28 | 29 | pico_enable_stdio_uart(lasertest 0) 30 | pico_enable_stdio_usb(lasertest 1) 31 | 32 | # Add the standard library to the build 33 | target_link_libraries(lasertest pico_stdlib) 34 | 35 | # Add any user requested libraries 36 | target_link_libraries(lasertest 37 | hardware_i2c 38 | vl53l1x 39 | ) 40 | 41 | pico_add_extra_outputs(lasertest) 42 | 43 | -------------------------------------------------------------------------------- /lasertest/lasertest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pico/stdlib.h" 3 | #include "pico/bootrom.h" 4 | #include "hardware/i2c.h" 5 | 6 | #include "vl53l1_api.h" 7 | #include "vl53l1_platform.h" 8 | 9 | // I2C defines 10 | // This example will use I2C0 on GPIO8 (SDA) and GPIO9 (SCL) running at 400KHz. 11 | // Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments 12 | #define I2C_PORT i2c0 13 | #define I2C_SDA 0 14 | #define I2C_SCL 1 15 | 16 | static VL53L1_Dev_t laser_dev; 17 | 18 | #define CHECK_STATUS(FAIL_MSG) if (Status != VL53L1_ERROR_NONE) { \ 19 | puts("FAIL: " FAIL_MSG); \ 20 | sleep_ms(5000); \ 21 | reset_usb_boot(0, 0); } 22 | 23 | void laser_init() 24 | { 25 | VL53L1_Error Status = VL53L1_ERROR_NONE; 26 | 27 | laser_dev.I2cDevAddr = 0x29; 28 | Status = VL53L1_software_reset(&laser_dev); 29 | CHECK_STATUS("Software Reset"); 30 | 31 | laser_dev.I2cDevAddr = 0x29; 32 | Status = VL53L1_WaitDeviceBooted(&laser_dev); 33 | CHECK_STATUS("Device Boot"); 34 | 35 | Status = VL53L1_DataInit(&laser_dev); 36 | CHECK_STATUS("Data Init"); 37 | 38 | Status = VL53L1_StaticInit(&laser_dev); 39 | CHECK_STATUS("Static Init"); 40 | 41 | VL53L1_DeviceInfo_t DeviceInfo; 42 | Status = VL53L1_GetDeviceInfo(&laser_dev, &DeviceInfo); 43 | CHECK_STATUS("Device Info"); 44 | printf("VL53L0X_GetDeviceInfo:\n"); 45 | printf("Device Name : %s\n", DeviceInfo.Name); 46 | printf("Device Type : %s\n", DeviceInfo.Type); 47 | printf("Device ID : %s\n", DeviceInfo.ProductId); 48 | printf("ProductRevisionMajor : %d\n", DeviceInfo.ProductRevisionMajor); 49 | printf("ProductRevisionMinor : %d\n", DeviceInfo.ProductRevisionMinor); 50 | 51 | VL53L1_PerformRefSpadManagement(&laser_dev); 52 | CHECK_STATUS("PerformRefSpadManagement"); 53 | 54 | VL53L1_SetXTalkCompensationEnable(&laser_dev, 0); 55 | CHECK_STATUS("SetXTalkCompensationEnable"); 56 | } 57 | 58 | void laser_start_ranging(int mode) 59 | { 60 | VL53L1_Error Status = VL53L1_ERROR_NONE; 61 | if (mode > 0) { 62 | Status = VL53L1_SetDistanceMode(&laser_dev, mode); 63 | CHECK_STATUS("Set Distance Mode"); 64 | } 65 | Status = VL53L1_StartMeasurement(&laser_dev); 66 | CHECK_STATUS("Start Ranging"); 67 | } 68 | 69 | static VL53L1_RangingMeasurementData_t RangingMeasurementData; 70 | 71 | float laser_get_distance() 72 | { 73 | VL53L1_Error Status = VL53L1_ERROR_NONE; 74 | 75 | Status = VL53L1_WaitMeasurementDataReady(&laser_dev); 76 | CHECK_STATUS("Wait For Data"); 77 | Status = VL53L1_GetRangingMeasurementData(&laser_dev, &RangingMeasurementData); 78 | CHECK_STATUS("Get Data"); 79 | float dist = RangingMeasurementData.RangeMilliMeter * 0.001f; 80 | VL53L1_ClearInterruptAndStartMeasurement(&laser_dev); 81 | return dist; 82 | } 83 | 84 | int main() 85 | { 86 | stdio_init_all(); 87 | 88 | // I2C Initialisation. Using it at 400Khz. 89 | i2c_init(I2C_PORT, 100*1000); 90 | 91 | gpio_set_function(I2C_SDA, GPIO_FUNC_I2C); 92 | gpio_set_function(I2C_SCL, GPIO_FUNC_I2C); 93 | gpio_pull_up(I2C_SDA); 94 | gpio_pull_up(I2C_SCL); 95 | 96 | sleep_ms(3000); 97 | 98 | laser_init(); 99 | laser_start_ranging(2); 100 | 101 | while (1) { 102 | float dist = laser_get_distance(); 103 | printf("Dist: %.2f\n", dist); 104 | sleep_ms(300); 105 | } 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /lasertest/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /profile/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.gif 3 | -------------------------------------------------------------------------------- /profile/.vscode/.cortex-debug.peripherals.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /profile/.vscode/.cortex-debug.registers.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /profile/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [ 9 | "_DEBUG", 10 | "UNICODE", 11 | "_UNICODE" 12 | ], 13 | "windowsSdkVersion": "10.0.18362.0", 14 | "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.28.29333/bin/Hostx64/x64/cl.exe", 15 | "cStandard": "c17", 16 | "cppStandard": "c++17", 17 | "intelliSenseMode": "windows-msvc-x64", 18 | "configurationProvider": "ms-vscode.cmake-tools", 19 | "compileCommands": "${workspaceFolder}/build/compile_commands.json" 20 | } 21 | ], 22 | "version": 4 23 | } -------------------------------------------------------------------------------- /profile/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Pico Debug", 6 | "type":"cortex-debug", 7 | "cwd": "${workspaceRoot}", 8 | "executable": "${command:cmake.launchTargetPath}", 9 | "request": "launch", 10 | "servertype": "external", 11 | // This may need to be arm-none-eabi-gdb depending on your system 12 | "gdbPath": "arm-none-eabi-gdb", 13 | // Connect to an already running OpenOCD instance 14 | "gdbTarget": "inkypi:3333", 15 | "svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd", 16 | //"runToEntryPoint": "main", 17 | "postLaunchCommands": [ 18 | // "continue" 19 | ] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /profile/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.pio": "asm", 4 | "systick.h": "c" 5 | } 6 | } -------------------------------------------------------------------------------- /profile/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.13) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | # initalize pico_sdk from installed location 9 | # (note this can come from environment, CMake cache etc) 10 | #set(PICO_SDK_PATH "C:/Users/mike/pico/pico-sdk") 11 | 12 | # Pull in Pico SDK (must be before project) 13 | include(pico_sdk_import.cmake) 14 | 15 | project(profile C CXX ASM) 16 | 17 | # Initialise the Pico SDK 18 | pico_sdk_init() 19 | 20 | # Add executable. Default name is the project name, version 0.1 21 | 22 | add_executable(profile profile.c ) 23 | 24 | pico_set_program_name(profile "profile") 25 | pico_set_program_version(profile "0.1") 26 | 27 | pico_enable_stdio_uart(profile 0) 28 | pico_enable_stdio_usb(profile 1) 29 | 30 | # Add the standard library to the build 31 | target_link_libraries(profile pico_stdlib pico_multicore hardware_interp) 32 | 33 | pico_add_extra_outputs(profile) 34 | 35 | -------------------------------------------------------------------------------- /profile/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /profile/profile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pico/stdlib.h" 3 | #include "pico/multicore.h" 4 | #include "hardware/interp.h" 5 | #include "hardware/structs/systick.h" 6 | 7 | // Evil from https://stackoverflow.com/questions/8551418/c-preprocessor-macro-for-returning-a-string-repeated-a-certain-number-of-times 8 | #define REP0(X) 9 | #define REP1(X) X 10 | #define REP2(X) REP1(X) X 11 | #define REP3(X) REP2(X) X 12 | #define REP4(X) REP3(X) X 13 | #define REP5(X) REP4(X) X 14 | #define REP6(X) REP5(X) X 15 | #define REP7(X) REP6(X) X 16 | #define REP8(X) REP7(X) X 17 | #define REP9(X) REP8(X) X 18 | #define REP10(X) REP9(X) X 19 | 20 | #define REP(HUNDREDS,TENS,ONES,X) \ 21 | REP##HUNDREDS(REP10(REP10(X))) \ 22 | REP##TENS(REP10(X)) \ 23 | REP##ONES(X) 24 | 25 | void core1_entry() { 26 | 27 | systick_hw->csr = 0b101; 28 | systick_hw->rvr = 0x10000; 29 | 30 | while (1) { 31 | systick_hw->cvr = 0; 32 | 33 | uint32_t count; 34 | asm ( 35 | "movs r6, #0\n\t" 36 | "str r6, [%[st], #8]\n\t" 37 | 38 | REP(0,6,4,"ldr r6, [%[itp0], #20]\n\t") 39 | 40 | "ldr %[count], [%[st], #8]" 41 | : [count] "=l" (count) : [itp0] "l" (interp0), [st] "l" (systick_hw) : "r6"); 42 | 43 | count = 0x10000 - count; 44 | 45 | multicore_fifo_push_blocking(count); 46 | 47 | sleep_ms(1000); 48 | } 49 | } 50 | 51 | int main() 52 | { 53 | stdio_init_all(); 54 | 55 | sleep_ms(3000); 56 | 57 | puts("Hello, world!"); 58 | 59 | multicore_launch_core1(core1_entry); 60 | 61 | while (1) { 62 | uint32_t count = multicore_fifo_pop_blocking(); 63 | 64 | printf("Systick count: %d\n", count); 65 | } 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /pstest/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | duck.h 3 | *.gif 4 | -------------------------------------------------------------------------------- /pstest/.vscode/.cortex-debug.peripherals.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /pstest/.vscode/.cortex-debug.registers.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /pstest/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [ 9 | "_DEBUG", 10 | "UNICODE", 11 | "_UNICODE" 12 | ], 13 | "windowsSdkVersion": "10.0.18362.0", 14 | "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.28.29333/bin/Hostx64/x64/cl.exe", 15 | "cStandard": "c17", 16 | "cppStandard": "c++17", 17 | "intelliSenseMode": "windows-msvc-x64", 18 | "configurationProvider": "ms-vscode.cmake-tools", 19 | "compileCommands": "${workspaceFolder}/build/compile_commands.json" 20 | } 21 | ], 22 | "version": 4 23 | } -------------------------------------------------------------------------------- /pstest/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Pico Debug", 6 | "type":"cortex-debug", 7 | "cwd": "${workspaceRoot}", 8 | "executable": "${command:cmake.launchTargetPath}", 9 | "request": "launch", 10 | "servertype": "external", 11 | // This may need to be arm-none-eabi-gdb depending on your system 12 | "gdbPath": "arm-none-eabi-gdb", 13 | // Connect to an already running OpenOCD instance 14 | "gdbTarget": "inkypi:3333", 15 | "svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd", 16 | "runToMain": true, 17 | "postRestartCommands": [ 18 | "break main", 19 | "continue" 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /pstest/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.pio": "pio", 4 | "pio.h": "c", 5 | "sd_card.h": "c" 6 | } 7 | } -------------------------------------------------------------------------------- /pstest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.13) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | set(PICO_BOARD pimoroni_picosystem) 9 | 10 | # Pull in Pico SDK (must be before project) 11 | include(pico_sdk_import.cmake) 12 | include(pico_extras_import.cmake) 13 | 14 | project(pstest C CXX ASM) 15 | 16 | # Initialise the Pico SDK 17 | pico_sdk_init() 18 | 19 | # Add executable. Default name is the project name, version 0.1 20 | 21 | add_executable(pstest pstest.c display.c st7789_lcd.c ) 22 | 23 | pico_set_program_name(pstest "pstest") 24 | pico_set_program_version(pstest "0.1") 25 | 26 | pico_enable_stdio_uart(pstest 1) 27 | pico_enable_stdio_usb(pstest 0) 28 | 29 | pico_generate_pio_header(pstest ${CMAKE_CURRENT_LIST_DIR}/st7789_lcd.pio) 30 | 31 | # Add the standard library to the build 32 | target_link_libraries(pstest pico_stdlib) 33 | 34 | # Add any user requested libraries 35 | target_link_libraries(pstest 36 | hardware_dma 37 | hardware_pio 38 | hardware_pwm 39 | ) 40 | 41 | pico_add_extra_outputs(pstest) 42 | 43 | -------------------------------------------------------------------------------- /pstest/display.c: -------------------------------------------------------------------------------- 1 | #include "pico/stdlib.h" 2 | 3 | #include "display.h" 4 | 5 | #include "hardware/dma.h" 6 | #include "hardware/gpio.h" 7 | #include "hardware/pio.h" 8 | 9 | const psd_colour PSD_BLACK = {.c = 0}; 10 | const psd_colour PSD_RED = {.r = 0x1f}; 11 | const psd_colour PSD_GREEN = {.g = 0x3f}; 12 | const psd_colour PSD_BLUE = {.b = 0x1f}; 13 | 14 | void ps_display_init(PS_DISPLAY* disp, psd_colour bg) 15 | { 16 | gpio_init(PICOSYSTEM_LCD_VSYNC_PIN); 17 | gpio_set_dir(PICOSYSTEM_LCD_VSYNC_PIN, GPIO_IN); 18 | 19 | disp->background = bg; 20 | 21 | st7789_init(&disp->st, pio0, 0, disp->buffer[0], disp->buffer[1]); 22 | 23 | st7789_start_pixels_at(&disp->st, 0, 0, DISPLAY_ROWS - 1, DISPLAY_COLS - 1); 24 | st7789_repeat_pixel(&disp->st, disp->background.c, DISPLAY_ROWS * DISPLAY_COLS); 25 | st7789_trigger_transfer(&disp->st); 26 | st7789_wait_for_transfer_complete(&disp->st); 27 | } 28 | 29 | void ps_display_draw_rect(PS_DISPLAY* disp, psd_vec pos, psd_vec size, psd_colour colour) 30 | { 31 | // TODO 32 | } 33 | 34 | void ps_display_draw_frect(PS_DISPLAY* disp, psd_vec pos, psd_vec size, psd_colour colour) 35 | { 36 | st7789_start_pixels_at(&disp->st, pos.x, pos.y, pos.x + size.x - 1, pos.y + size.y - 1); 37 | st7789_repeat_pixel(&disp->st, colour.c, size.x * size.y); 38 | } 39 | 40 | psd_sprite* ps_display_add_sprite(PS_DISPLAY* disp, psd_vec pos, psd_vec size, const uint32_t* data, uint32_t data_len) 41 | { 42 | psd_sprite* sprite = &disp->sprites[disp->max_sprite++]; 43 | sprite->pos = pos; 44 | sprite->size = size; 45 | sprite->data = data; 46 | sprite->data_len = data_len; 47 | sprite->depth = 0; 48 | sprite->enabled = 1; 49 | sprite->draw = 1; 50 | 51 | return sprite; 52 | } 53 | 54 | void ps_display_move_sprite(PS_DISPLAY* disp, psd_sprite* sprite, psd_vec pos) 55 | { 56 | if (sprite->pos.x != pos.x || sprite->pos.y != pos.y) 57 | { 58 | ps_display_draw_frect(disp, sprite->pos, sprite->size, disp->background); 59 | 60 | sprite->pos = pos; 61 | sprite->draw = 1; 62 | 63 | // TODO Overlapping sprites. 64 | } 65 | } 66 | 67 | void ps_display_render(PS_DISPLAY* disp) 68 | { 69 | // Queue draw for sprites 70 | for (uint16_t i = 0; i < disp->max_sprite; ++i) 71 | { 72 | psd_sprite* sprite = &disp->sprites[i]; 73 | if (sprite->draw) 74 | { 75 | st7789_start_pixels_at(&disp->st, sprite->pos.x, sprite->pos.y, sprite->pos.x + sprite->size.x - 1, sprite->pos.y + sprite->size.y - 1); 76 | st7789_dma_pixel_data(&disp->st, sprite->data, sprite->data_len); 77 | } 78 | } 79 | 80 | // Wait for a new VSYNC - this should be changed to a rising edge GPIO interrupt 81 | while(gpio_get(PICOSYSTEM_LCD_VSYNC_PIN)) {} 82 | while (!gpio_get(PICOSYSTEM_LCD_VSYNC_PIN)) {} 83 | 84 | st7789_trigger_transfer(&disp->st); 85 | } 86 | 87 | void ps_display_finish_render(PS_DISPLAY* disp) 88 | { 89 | st7789_wait_for_transfer_complete(&disp->st); 90 | } 91 | -------------------------------------------------------------------------------- /pstest/display.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "st7789_lcd.h" 3 | 4 | #define PSD_CMD_BUFFER_SIZE 4096 5 | #define PSD_MAX_SPRITES 256 6 | 7 | #define DISPLAY_ROWS 240 8 | #define DISPLAY_COLS 240 9 | 10 | typedef struct 11 | { 12 | uint8_t x; 13 | uint8_t y; 14 | } psd_vec; 15 | 16 | typedef struct 17 | { 18 | psd_vec tl; 19 | psd_vec br; 20 | } psd_box; 21 | 22 | typedef union 23 | { 24 | struct { 25 | uint16_t b : 5; 26 | uint16_t g : 6; 27 | uint16_t r : 5; 28 | }; 29 | uint16_t c; 30 | } psd_colour; 31 | 32 | extern const psd_colour PSD_BLACK; 33 | extern const psd_colour PSD_RED; 34 | extern const psd_colour PSD_GREEN; 35 | extern const psd_colour PSD_BLUE; 36 | 37 | typedef struct 38 | { 39 | psd_vec pos; 40 | psd_vec size; 41 | const uint32_t* data; 42 | uint32_t depth : 6; 43 | uint32_t enabled : 1; 44 | uint32_t draw : 1; 45 | uint32_t data_len : 24; 46 | } psd_sprite; 47 | 48 | typedef struct 49 | { 50 | ST7789 st; 51 | uint32_t buffer[2][PSD_CMD_BUFFER_SIZE]; 52 | 53 | psd_sprite sprites[PSD_MAX_SPRITES]; 54 | uint16_t max_sprite; 55 | 56 | psd_colour background; 57 | } PS_DISPLAY; 58 | 59 | void ps_display_init(PS_DISPLAY* disp, psd_colour background); 60 | 61 | psd_sprite* ps_display_add_sprite(PS_DISPLAY* disp, psd_vec pos, psd_vec size, const uint32_t* data, uint32_t data_len); 62 | void ps_display_move_sprite(PS_DISPLAY* disp, psd_sprite* sprite, psd_vec pos); 63 | 64 | void ps_display_draw_rect(PS_DISPLAY* disp, psd_vec pos, psd_vec size, psd_colour colour); 65 | void ps_display_draw_frect(PS_DISPLAY* disp, psd_vec pos, psd_vec size, psd_colour colour); 66 | 67 | void ps_display_render(PS_DISPLAY* disp); 68 | void ps_display_finish_render(PS_DISPLAY* disp); -------------------------------------------------------------------------------- /pstest/pico_extras_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_extras_import.cmake 2 | 3 | # This can be dropped into an external project to help locate pico-extras 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH)) 7 | set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH}) 8 | message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT)) 12 | set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT}) 13 | message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH)) 17 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | if (NOT PICO_EXTRAS_PATH) 22 | if (PICO_EXTRAS_FETCH_FROM_GIT) 23 | include(FetchContent) 24 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 25 | if (PICO_EXTRAS_FETCH_FROM_GIT_PATH) 26 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 27 | endif () 28 | FetchContent_Declare( 29 | PICO_EXTRAS 30 | GIT_REPOSITORY https://github.com/raspberrypi/pico-extras 31 | GIT_TAG master 32 | ) 33 | if (NOT PICO_EXTRAS) 34 | message("Downloading PICO EXTRAS") 35 | FetchContent_Populate(PICO_EXTRAS) 36 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_SOURCE_DIR}) 37 | endif () 38 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 39 | else () 40 | if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras") 41 | set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras) 42 | message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}") 43 | else() 44 | message(FATAL_ERROR 45 | "PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif() 48 | endif () 49 | endif () 50 | 51 | set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS") 52 | set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable") 53 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS") 54 | 55 | get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 56 | if (NOT EXISTS ${PICO_EXTRAS_PATH}) 57 | message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found") 58 | endif () 59 | 60 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE) 61 | 62 | add_subdirectory(${PICO_EXTRAS_PATH} pico_extras) -------------------------------------------------------------------------------- /pstest/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /pstest/pstest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "pico/stdlib.h" 4 | 5 | #include "display.h" 6 | 7 | #include "hardware/gpio.h" 8 | 9 | PS_DISPLAY disp; 10 | 11 | const uint32_t ball_data[18] = 12 | { 13 | 0x0000f800, 0xf800f800, 0xf8000000, 14 | 0xf800f800, 0xf800f800, 0xf800f800, 15 | 0xf800f800, 0xffffffff, 0xf800f800, 16 | 0xf800f800, 0xffffffff, 0xf800f800, 17 | 0xf800f800, 0xf800f800, 0xf800f800, 18 | 0x0000f800, 0xf800f800, 0xf8000000, 19 | }; 20 | 21 | #define BOUNDS 234 22 | const psd_vec ball_size = {6, 6}; 23 | 24 | typedef struct 25 | { 26 | float x; 27 | float y; 28 | } fvec; 29 | 30 | typedef struct 31 | { 32 | fvec pos; 33 | fvec speed; 34 | psd_sprite* sprite; 35 | } ball; 36 | 37 | #define NUM_BALLS 250 38 | ball balls[NUM_BALLS]; 39 | 40 | int main() 41 | { 42 | stdio_init_all(); 43 | 44 | gpio_init_mask(0xffe000); 45 | gpio_set_dir_in_masked(0xff0000); 46 | gpio_set_dir_out_masked(0x00e000); 47 | for (uint32_t i = 16; i < 24; i++) 48 | { 49 | gpio_pull_up(i); 50 | } 51 | 52 | ps_display_init(&disp, PSD_BLACK); 53 | 54 | for (uint32_t i = 0; i < NUM_BALLS; ++i) 55 | { 56 | psd_vec start_pos; 57 | start_pos.x = rand() % BOUNDS; 58 | start_pos.y = rand() % BOUNDS; 59 | balls[i].pos.x = start_pos.x; 60 | balls[i].pos.y = start_pos.y; 61 | balls[i].speed.x = ((float)rand() / (RAND_MAX / 4)) - 2.f; 62 | balls[i].speed.y = ((float)rand() / (RAND_MAX / 4)) - 2.f; 63 | balls[i].sprite = ps_display_add_sprite(&disp, start_pos, ball_size, ball_data, sizeof(ball_data) / sizeof(uint32_t)); 64 | } 65 | 66 | ps_display_render(&disp); 67 | 68 | while (1) 69 | { 70 | for (uint32_t i = 0; i < NUM_BALLS; ++i) 71 | { 72 | ball* ball_ptr = &balls[i]; 73 | 74 | ball_ptr->pos.x += balls[i].speed.x; 75 | if (ball_ptr->pos.x < 0.f) 76 | { 77 | ball_ptr->pos.x = 0.f; 78 | ball_ptr->speed.x = -ball_ptr->speed.x; 79 | } 80 | else if (ball_ptr->pos.x > BOUNDS) 81 | { 82 | ball_ptr->pos.x = BOUNDS; 83 | ball_ptr->speed.x = -ball_ptr->speed.x; 84 | } 85 | 86 | balls[i].pos.y += balls[i].speed.y; 87 | if (ball_ptr->pos.y < 0.f) 88 | { 89 | ball_ptr->pos.y = 0.f; 90 | ball_ptr->speed.y = -ball_ptr->speed.y; 91 | } 92 | else if (ball_ptr->pos.y > BOUNDS) 93 | { 94 | ball_ptr->pos.y = BOUNDS; 95 | ball_ptr->speed.y = -ball_ptr->speed.y; 96 | } 97 | } 98 | 99 | ps_display_finish_render(&disp); 100 | 101 | for (uint32_t i = 0; i < NUM_BALLS; ++i) 102 | { 103 | psd_vec newpos; 104 | newpos.x = balls[i].pos.x; 105 | newpos.y = balls[i].pos.y; 106 | ps_display_move_sprite(&disp, balls[i].sprite, newpos); 107 | } 108 | 109 | ps_display_render(&disp); 110 | } 111 | 112 | return 0; 113 | } -------------------------------------------------------------------------------- /pstest/st7789_lcd.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include "pico/stdlib.h" 11 | #include "hardware/pio.h" 12 | #include "hardware/gpio.h" 13 | #include "hardware/dma.h" 14 | #include "hardware/pwm.h" 15 | 16 | #include "st7789_lcd.h" 17 | #include "st7789_lcd.pio.h" 18 | 19 | #define SCREEN_WIDTH 240 20 | #define SCREEN_HEIGHT 240 21 | 22 | #define PIN_DIN PICOSYSTEM_LCD_MOSI_PIN 23 | #define PIN_CLK PICOSYSTEM_LCD_SCLK_PIN 24 | #define PIN_CS PICOSYSTEM_LCD_CSN_PIN // Must be CLK - 1 25 | #define PIN_DC PICOSYSTEM_LCD_DC_PIN 26 | #define PIN_RESET PICOSYSTEM_LCD_RESET_PIN 27 | #define PIN_BL PICOSYSTEM_BACKLIGHT_PIN 28 | 29 | #define SERIAL_CLK_DIV 1.f 30 | 31 | // Format: cmd length (including cmd byte), post delay in units of 5 ms, then cmd payload 32 | // Note the delays have been shortened a little 33 | #if 1 34 | static const uint8_t st7789_init_seq[] = { 35 | 1, 20, 0x01, // Software reset 36 | 1, 10, 0x11, // Exit sleep mode 37 | 2, 2, 0x3a, 0x55, // Set colour mode to 16 bit 38 | 2, 0, 0x35, 0x00, // Enable VSYNC output 39 | 3, 0, 0x44, 0x00, 0xf0, // VSYNC after 240 lines 40 | 2, 0, 0xC6, 0x15, // 50Hz display 41 | 2, 0, 0x36, 0x00, // Set MADCTL: row then column, refresh is bottom to top ???? 42 | 1, 2, 0x21, // Inversion on, then 10 ms delay (supposedly a hack?) 43 | 1, 2, 0x13, // Normal display on, then 10 ms delay 44 | 1, 2, 0x29, // Main screen turn on, then wait 10 ms 45 | 0 // Terminate list 46 | }; 47 | #else 48 | // Modified sequence from Pimoroni's python library 49 | // This plays with some power settings which makes the image slightly brighter, 50 | // but if anything seems to make flickering slightly worse. 51 | static const uint8_t st7789_init_seq[] = { 52 | 1, 30, 0x01, 53 | 2, 0, 0x36, 0x00, 54 | 2, 0, 0x3A, 0x55, 55 | 2, 0, 0xB7, 0x14, 56 | 2, 0, 0xBB, 0x37, 57 | 3, 0, 0xC2, 0x01, 0xFF, 58 | 2, 0, 0xC3, 0x12, 59 | 2, 0, 0xC4, 0x20, 60 | 3, 0, 0xD0, 0xA4, 0XA1, 61 | 2, 0, 0xC6, 0x0F, 62 | 5, 0, 0x2a, 0x00, 0x00, 0x00, 0xf0, // CASET: column addresses from 0 to 240 (f0) 63 | 5, 0, 0x2b, 0x00, 0x00, 0x00, 0xf0, // RASET: row addresses from 0 to 240 (f0) 64 | 1, 2, 0x21, 65 | 1, 2, 0x13, 66 | 1, 10, 0x11, 67 | 1, 2, 0x29, 68 | 0 69 | }; 70 | #endif 71 | 72 | static inline uint32_t st7789_encode_cmd(uint8_t cmd, uint32_t data_count) 73 | { 74 | uint32_t instr = 0x100; 75 | if (data_count) instr = (data_count * 8) << 8; 76 | instr |= cmd; 77 | return instr; 78 | } 79 | 80 | static inline void lcd_write_cmd(PIO pio, uint sm, const uint8_t *cmd, int count) { 81 | st7789_lcd_wait_idle(pio, sm); 82 | 83 | uint32_t instr = st7789_encode_cmd(*cmd++, count - 1); 84 | pio_sm_put(pio, sm, instr); 85 | 86 | if (count >= 2) { 87 | count--; 88 | while (count > 0) { 89 | instr = 0; 90 | for (int i = 0; i < count && i < 4; ++i) { 91 | instr <<= 8; 92 | instr |= cmd[i]; 93 | } 94 | if (count < 4) 95 | instr <<= 8 * (4 - count); 96 | pio_sm_put(pio, sm, instr); 97 | count -= 4; 98 | } 99 | } 100 | } 101 | 102 | static void st7789_create_dma_channels(ST7789* st) 103 | { 104 | st->data_chan = dma_claim_unused_channel(true); 105 | st->ctrl_chan = dma_claim_unused_channel(true); 106 | 107 | dma_channel_config c = dma_channel_get_default_config(st->data_chan); 108 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 109 | channel_config_set_dreq(&c, pio_get_dreq(st->pio, st->sm, true)); 110 | channel_config_set_read_increment(&c, false); 111 | channel_config_set_write_increment(&c, false); 112 | channel_config_set_chain_to(&c, st->ctrl_chan); 113 | channel_config_set_irq_quiet(&c, true); 114 | 115 | dma_channel_configure( 116 | st->data_chan, // Channel to be configured 117 | &c, // The configuration we just created 118 | &st->pio->txf[st->sm], // The write address 119 | st->data_buf, // The initial read address 120 | 0, // Number of transfers - set later 121 | false // Don't start yet 122 | ); 123 | 124 | c = dma_channel_get_default_config(st->ctrl_chan); 125 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 126 | channel_config_set_read_increment(&c, true); 127 | channel_config_set_write_increment(&c, true); 128 | channel_config_set_ring(&c, true, 4); 129 | 130 | dma_channel_configure( 131 | st->ctrl_chan, // Channel to be configured 132 | &c, // The configuration we just created 133 | &dma_hw->ch[st->data_chan].read_addr, // Control block for data DMA channel 134 | st->ctrl_buf, // The initial read address 135 | 4, // Number of transfers 136 | false // Don't start yet 137 | ); 138 | 139 | st->ctrl_ctrl = dma_hw->ch[st->data_chan].ctrl_trig; 140 | } 141 | 142 | static void st7789_write_tcb(ST7789* st, const uint32_t* data_ptr, uint32_t word_count, bool repeat) 143 | { 144 | uint ctrl = st->ctrl_ctrl; 145 | if (!repeat) ctrl |= DMA_CH0_CTRL_TRIG_INCR_READ_BITS; 146 | if (word_count == 0) ctrl = 0; 147 | 148 | *st->ctrl_ptr++ = (uintptr_t)data_ptr; 149 | *st->ctrl_ptr++ = (uintptr_t)&st->pio->txf[st->sm]; 150 | *st->ctrl_ptr++ = word_count; 151 | *st->ctrl_ptr++ = ctrl; 152 | } 153 | 154 | void st7789_init(ST7789* st, PIO pio, uint sm, uint32_t* data_buf, uint32_t* ctrl_buf) 155 | { 156 | st->pio = pio; 157 | st->sm = sm; 158 | st->data_buf = st->data_ptr = data_buf; 159 | st->ctrl_buf = st->ctrl_ptr = ctrl_buf; 160 | st->transfer_in_progress = false; 161 | 162 | uint offset = pio_add_program(pio, &st7789_lcd_program); 163 | st7789_lcd_program_init(pio, sm, offset, PIN_DIN, PIN_CS, PIN_DC, SERIAL_CLK_DIV); 164 | 165 | pwm_config cfg = pwm_get_default_config(); 166 | pwm_set_wrap(pwm_gpio_to_slice_num(PIN_BL), 65535); 167 | pwm_init(pwm_gpio_to_slice_num(PIN_BL), &cfg, true); 168 | gpio_set_function(PIN_BL, GPIO_FUNC_PWM); 169 | pwm_set_gpio_level(PIN_BL, 0); 170 | 171 | gpio_init(PIN_RESET); 172 | gpio_set_dir(PIN_RESET, GPIO_OUT); 173 | 174 | gpio_put(PIN_RESET, 1); 175 | 176 | const uint8_t *cmd = st7789_init_seq; 177 | while (*cmd) { 178 | lcd_write_cmd(pio, sm, cmd + 2, *cmd); 179 | sleep_ms(*(cmd + 1) * 5); 180 | cmd += *cmd + 2; 181 | } 182 | 183 | pwm_set_gpio_level(PIN_BL, 10000); 184 | 185 | st7789_create_dma_channels(st); 186 | } 187 | 188 | void st7789_start_pixels_at(ST7789* st, uint8_t x, uint8_t y, uint8_t maxx, uint8_t maxy) { 189 | uint8_t ca_cmd[] = {0x2a, 0x00, x, 0x00, maxx}; 190 | uint8_t ra_cmd[] = {0x2b, 0x00, y, 0x00, maxy}; 191 | 192 | uint32_t* data_ptr = st->data_ptr; 193 | *st->data_ptr++ = st7789_encode_cmd(0x2a, 4); 194 | *st->data_ptr++ = maxx | ((uint32_t)x << 16); 195 | *st->data_ptr++ = st7789_encode_cmd(0x2b, 4); 196 | *st->data_ptr++ = maxy | ((uint32_t)y << 16); 197 | *st->data_ptr++ = st7789_encode_cmd(0x2c, (uint32_t)(maxx - x + 1) * (uint32_t)(maxy - y + 1) * 2); 198 | 199 | st7789_write_tcb(st, data_ptr, 5, false); 200 | } 201 | 202 | void st7789_trigger_transfer(ST7789* st) 203 | { 204 | if (st->data_ptr != st->data_buf) 205 | { 206 | st->transfer_in_progress = true; 207 | 208 | // Zero length TCB finishes the chain 209 | st7789_write_tcb(st, NULL, 0, false); 210 | 211 | // Begin the transfer 212 | dma_channel_hw_addr(st->ctrl_chan)->read_addr = (uintptr_t)st->ctrl_buf; 213 | dma_channel_start(st->ctrl_chan); 214 | } 215 | 216 | // Reset pointers for next frame 217 | st->data_ptr = st->data_buf; 218 | st->ctrl_ptr = st->ctrl_buf; 219 | } 220 | 221 | void st7789_wait_for_transfer_complete(ST7789* st) 222 | { 223 | if (st->transfer_in_progress) 224 | { 225 | while (!(dma_hw->intr & (1u << st->data_chan))) {} 226 | dma_hw->ints0 = 1u << st->data_chan; 227 | st->transfer_in_progress = false; 228 | } 229 | } 230 | 231 | void st7789_repeat_pixel(ST7789* st, uint16_t pixel, uint repeats) 232 | { 233 | if (repeats & 1) 234 | { 235 | // Odd number of repeats, fix up the count to output one more pixel 236 | st->data_ptr[-1] += 0x1000; 237 | } 238 | 239 | *st->data_ptr = ((uint32_t)pixel << 16) | pixel; 240 | st7789_write_tcb(st, st->data_ptr++, (repeats+1) >> 1, true); 241 | } 242 | 243 | void st7789_dma_pixel_data(ST7789* st, const uint32_t* pixels, uint len) 244 | { 245 | st7789_write_tcb(st, pixels, len, false); 246 | } 247 | -------------------------------------------------------------------------------- /pstest/st7789_lcd.h: -------------------------------------------------------------------------------- 1 | #include "hardware/pio.h" 2 | 3 | typedef struct { 4 | PIO pio; 5 | uint sm; 6 | uint data_chan; 7 | uint ctrl_chan; 8 | uint ctrl_ctrl; 9 | uint32_t* data_buf; 10 | uint32_t* ctrl_buf; 11 | uint32_t* data_ptr; 12 | uint32_t* ctrl_ptr; 13 | volatile bool transfer_in_progress; 14 | } ST7789; 15 | 16 | // Setup the display and return CB 17 | void st7789_init(ST7789* st, PIO pio, uint sm, uint32_t* data_buf, uint32_t* ctrl_buf); 18 | 19 | // Queue data transfer into a given box 20 | void st7789_start_pixels_at(ST7789* st, uint8_t x, uint8_t y, uint8_t maxx, uint8_t maxy); 21 | 22 | // Write all queued data 23 | void st7789_trigger_transfer(ST7789* st); 24 | 25 | // Wait for previous transfer to complete, this must be called before queueing any further commands 26 | void st7789_wait_for_transfer_complete(ST7789* st); 27 | 28 | // Queue a single pixel multiple times. Repeats is the number of times the pixel is repeated. 29 | void st7789_repeat_pixel(ST7789* st, uint16_t pixel, uint repeats); 30 | 31 | // Queue pixel data from memory, note len is data length in 32-bit words, not pixels. 32 | void st7789_dma_pixel_data(ST7789* st, const uint32_t* pixels, uint len); -------------------------------------------------------------------------------- /pstest/st7789_lcd.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Loosely based on the RPi st7789 example 3 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 4 | ; Changes copyright (c) 2021 Michael Bell 5 | ; 6 | ; SPDX-License-Identifier: BSD-3-Clause 7 | ; 8 | 9 | .program st7789_lcd 10 | .side_set 2 11 | 12 | ; This transmits 32-bit DMA'd data of the form: 13 | ; Header: | 24 bits | 8 bits | 14 | ; | Data bit count | Command | 15 | ; Data: (data bit count)/8 data bytes 16 | ; Bytes are sent from MSB first, so the last word should be right aligned 17 | ; if the data byte count isn't a multiple of 4. 18 | ; If the data byte count is not a multiple of 4 then the remaining part 19 | ; of the word is discarded. 20 | ; If no data is to be sent "data bit count" should be 1, else it should 21 | ; be the number of bits to be sent. 22 | 23 | ; Data on OUT pin 0 24 | ; Clock on side-set pin 1 25 | ; CS on side-set pin 0 26 | ; D/C on SET pin 0 27 | 28 | set pins, 0 side 0b01 ; Set D/C to command 29 | next_cmd: 30 | set y, 7 side 0b01 ; Send 8 bit command 31 | discard: 32 | out x, 24 side 0b01 ; Read data length 33 | jmp x--, cmd_loop side 0b01 ; Discard if we just read all zeros 34 | jmp discard side 0b01 35 | 36 | cmd_loop: 37 | out pins, 1 side 0b00 38 | jmp y--, cmd_loop side 0b10 39 | 40 | jmp !x, next_cmd side 0b00 ; If no data go to next command 41 | 42 | set pins, 1 side 0b00 ; Set D/C to data 43 | data_loop: ; Send data 44 | out pins, 1 side 0b00 45 | jmp x--, data_loop side 0b10 46 | 47 | 48 | % c-sdk { 49 | 50 | static inline void st7789_lcd_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint cs_pin, uint dc_pin, float clk_div) { 51 | pio_gpio_init(pio, data_pin); 52 | pio_gpio_init(pio, cs_pin); 53 | pio_gpio_init(pio, cs_pin + 1); 54 | pio_gpio_init(pio, dc_pin); 55 | pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, true); 56 | pio_sm_set_consecutive_pindirs(pio, sm, dc_pin, 1, true); 57 | pio_sm_set_consecutive_pindirs(pio, sm, cs_pin, 2, true); 58 | pio_sm_config c = st7789_lcd_program_get_default_config(offset); 59 | sm_config_set_sideset_pins(&c, cs_pin); 60 | sm_config_set_out_pins(&c, data_pin, 1); 61 | sm_config_set_set_pins(&c, dc_pin, 1); 62 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 63 | sm_config_set_clkdiv(&c, clk_div); 64 | sm_config_set_out_shift(&c, false, true, 32); 65 | pio_sm_init(pio, sm, offset, &c); 66 | pio_sm_set_enabled(pio, sm, true); 67 | } 68 | 69 | // SM is done when it stalls on an empty FIFO 70 | 71 | static inline void st7789_lcd_wait_idle(PIO pio, uint sm) { 72 | uint32_t sm_stall_mask = 1u << (sm + PIO_FDEBUG_TXSTALL_LSB); 73 | pio->fdebug = sm_stall_mask; 74 | while (!(pio->fdebug & sm_stall_mask)) 75 | ; 76 | } 77 | %} 78 | -------------------------------------------------------------------------------- /rover/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /rover/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.12) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | # initalize pico_sdk from installed location 9 | # (note this can come from environment, CMake cache etc) 10 | set(PICO_SDK_PATH "/home/pi/pico/pico-sdk") 11 | 12 | # Pull in Pico SDK (must be before project) 13 | include(pico_sdk_import.cmake) 14 | 15 | project(rover C CXX) 16 | 17 | # Initialise the Pico SDK 18 | pico_sdk_init() 19 | 20 | # Add executable. Default name is the project name, version 0.1 21 | 22 | add_executable(rover rover.c ) 23 | 24 | pico_set_program_name(rover "rover") 25 | pico_set_program_version(rover "0.1") 26 | 27 | pico_enable_stdio_uart(rover 0) 28 | pico_enable_stdio_usb(rover 1) 29 | 30 | # Add the standard library to the build 31 | target_link_libraries(rover pico_stdlib 32 | hardware_adc 33 | hardware_pwm) 34 | 35 | pico_add_extra_outputs(rover) 36 | 37 | -------------------------------------------------------------------------------- /rover/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /rover/rover.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pico/stdlib.h" 3 | #include "hardware/pwm.h" 4 | #include "hardware/adc.h" 5 | 6 | #define BASE_PIN 6 7 | #define NUM_PINS 8 8 | 9 | int main() 10 | { 11 | gpio_init(25); 12 | gpio_set_dir(25, 1); 13 | gpio_put(25, 1); 14 | 15 | stdio_init_all(); 16 | sleep_ms(2000); 17 | printf("Hello, world\n"); 18 | 19 | adc_init(); 20 | 21 | // Speed control pot 22 | adc_gpio_init(28); 23 | adc_select_input(2); 24 | uint16_t speed = 2048 + (adc_read() >> 1); 25 | adc_select_input(1); 26 | 27 | // Light sensor - we're powering from GPIO pin 26 which 28 | // is a bit naughty but it draws hardly anything so fine. 29 | adc_gpio_init(27); 30 | gpio_init(26); 31 | gpio_set_dir(26, 1); 32 | gpio_put(26, 1); 33 | uint16_t brightness = adc_read(); 34 | 35 | printf("Speed: %hu\t Bright: %hu\n", speed, brightness); 36 | 37 | for (int i = 0; i < 5; ++i) { 38 | sleep_ms(200); 39 | gpio_put(25, 0); 40 | sleep_ms(200); 41 | gpio_put(25, 1); 42 | } 43 | 44 | #if 1 45 | #if 1 46 | for (int pin = BASE_PIN; pin < BASE_PIN+NUM_PINS; ++pin) { 47 | gpio_set_function(pin, GPIO_FUNC_PWM); 48 | pwm_set_gpio_level(pin, 0); 49 | } 50 | for (int pin = BASE_PIN; pin < BASE_PIN+NUM_PINS; pin += 2) { 51 | uint slice = pwm_gpio_to_slice_num(pin); 52 | pwm_set_wrap(slice, 4095); 53 | pwm_set_clkdiv(slice, 32768.f); 54 | pwm_set_enabled(slice, true); 55 | } 56 | 57 | for (int pin = BASE_PIN; pin < BASE_PIN+NUM_PINS; pin += 2) { 58 | pwm_set_gpio_level(pin, speed); 59 | } 60 | sleep_ms(1000); 61 | for (int pin = BASE_PIN; pin < BASE_PIN+NUM_PINS; pin += 2) { 62 | pwm_set_gpio_level(pin, 0); 63 | } 64 | 65 | #else 66 | for (int pin = BASE_PIN; pin < BASE_PIN+NUM_PINS; ++pin) { 67 | gpio_init(pin); 68 | gpio_set_dir(pin, 1); 69 | gpio_put(pin, 0); 70 | } 71 | 72 | gpio_put_masked(0b11111111 << BASE_PIN, 0b01010101 << BASE_PIN); 73 | sleep_ms(2000); 74 | gpio_put_masked(0b11111111 << BASE_PIN, 0b00000000 << BASE_PIN); 75 | #endif 76 | 77 | gpio_put(25, 0); 78 | #endif 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /sdaudio/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.gif 3 | *.huf 4 | *.dat 5 | frames/ -------------------------------------------------------------------------------- /sdaudio/.vscode/.cortex-debug.peripherals.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /sdaudio/.vscode/.cortex-debug.registers.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /sdaudio/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [ 9 | "_DEBUG", 10 | "UNICODE", 11 | "_UNICODE" 12 | ], 13 | "windowsSdkVersion": "10.0.18362.0", 14 | "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.28.29333/bin/Hostx64/x64/cl.exe", 15 | "cStandard": "c17", 16 | "cppStandard": "c++17", 17 | "intelliSenseMode": "windows-msvc-x64", 18 | "configurationProvider": "ms-vscode.cmake-tools", 19 | "compileCommands": "${workspaceFolder}/build/compile_commands.json" 20 | } 21 | ], 22 | "version": 4 23 | } -------------------------------------------------------------------------------- /sdaudio/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Pico Debug", 6 | "type":"cortex-debug", 7 | "cwd": "${workspaceRoot}", 8 | "executable": "${command:cmake.launchTargetPath}", 9 | "request": "launch", 10 | "servertype": "external", 11 | // This may need to be arm-none-eabi-gdb depending on your system 12 | "gdbPath": "arm-none-eabi-gdb", 13 | // Connect to an already running OpenOCD instance 14 | "gdbTarget": "inkypi:3333", 15 | "svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd", 16 | //"runToEntryPoint": "main", 17 | "postLaunchCommands": [ 18 | // "continue" 19 | ] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /sdaudio/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.pio": "asm", 4 | "pio.h": "c", 5 | "sd_card.h": "c", 6 | "vga.h": "c", 7 | "stdlib.h": "c", 8 | "uart.h": "c", 9 | "stdio.h": "c", 10 | "pico.h": "c", 11 | "gpio.h": "c", 12 | "irq.h": "c", 13 | "audio_i2s.h": "c" 14 | } 15 | } -------------------------------------------------------------------------------- /sdaudio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.13) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | # Pull in Pico SDK (must be before project) 9 | include(pico_sdk_import.cmake) 10 | include(pico_extras_import.cmake) 11 | 12 | project(sdaudio C CXX ASM) 13 | 14 | # Initialise the Pico SDK 15 | pico_sdk_init() 16 | 17 | # Add executable. Default name is the project name, version 0.1 18 | 19 | add_executable(sdaudio sdaudio.c sdring.c) 20 | 21 | pico_set_program_name(sdaudio "sdaudio") 22 | pico_set_program_version(sdaudio "0.1") 23 | 24 | pico_enable_stdio_uart(sdaudio 0) 25 | pico_enable_stdio_usb(sdaudio 0) 26 | 27 | # Add the standard library to the build 28 | target_link_libraries(sdaudio pico_stdlib) 29 | 30 | # Add any user requested libraries 31 | target_link_libraries(sdaudio 32 | hardware_dma 33 | pico_sd_card 34 | pico_audio_i2s 35 | ) 36 | 37 | pico_add_extra_outputs(sdaudio) 38 | 39 | -------------------------------------------------------------------------------- /sdaudio/README.md: -------------------------------------------------------------------------------- 1 | # SD Audio player 2 | 3 | This plays a PCM audio file from an SD card. 4 | 5 | To extract audio in the right format from an mp4 do: 6 | ``` 7 | ffmpeg -i video.mp4 -ac 2 -f s16le -c:a pcm_s16le -ar 44100 audio.pcm 8 | ``` 9 | 10 | Then write the PCM file starting at a particular sector (in this case 3200000) on the SD card with a command like this: 11 | ``` 12 | dd if=audio.pcm of=/dev/sdX bs=512 seek=3200000 13 | ``` 14 | Adjusting the device to match you SD card device. Double check you got the device right because if you get it wrong you'll corrupt you main drive. 15 | 16 | Finally set the sector and file length in the defines in sdaudio.c, and hear the audio! 17 | -------------------------------------------------------------------------------- /sdaudio/pico_extras_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_extras_import.cmake 2 | 3 | # This can be dropped into an external project to help locate pico-extras 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH)) 7 | set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH}) 8 | message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT)) 12 | set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT}) 13 | message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH)) 17 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | if (NOT PICO_EXTRAS_PATH) 22 | if (PICO_EXTRAS_FETCH_FROM_GIT) 23 | include(FetchContent) 24 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 25 | if (PICO_EXTRAS_FETCH_FROM_GIT_PATH) 26 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 27 | endif () 28 | FetchContent_Declare( 29 | PICO_EXTRAS 30 | GIT_REPOSITORY https://github.com/raspberrypi/pico-extras 31 | GIT_TAG master 32 | ) 33 | if (NOT PICO_EXTRAS) 34 | message("Downloading PICO EXTRAS") 35 | FetchContent_Populate(PICO_EXTRAS) 36 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_SOURCE_DIR}) 37 | endif () 38 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 39 | else () 40 | if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras") 41 | set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras) 42 | message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}") 43 | else() 44 | message(FATAL_ERROR 45 | "PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif() 48 | endif () 49 | endif () 50 | 51 | set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS") 52 | set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable") 53 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS") 54 | 55 | get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 56 | if (NOT EXISTS ${PICO_EXTRAS_PATH}) 57 | message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found") 58 | endif () 59 | 60 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE) 61 | 62 | add_subdirectory(${PICO_EXTRAS_PATH} pico_extras) -------------------------------------------------------------------------------- /sdaudio/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /sdaudio/sdaudio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pico/stdlib.h" 3 | #include "hardware/dma.h" 4 | 5 | #include "pico/audio_i2s.h" 6 | 7 | #include "sdring.h" 8 | 9 | #define START_SECTOR 3200000 10 | #define FILE_LEN_BYTES 36171776 11 | 12 | #define SAMPLES_PER_BUFFER 256 13 | 14 | #define SDRING_INDEX_MASK_BYTES ((1 << SDRING_BUF_LOG_SIZE_BYTES) - 1) 15 | #define SDRING_INDEX_MASK_HALFWORDS ((1 << (SDRING_BUF_LOG_SIZE_BYTES - 1)) - 1) 16 | 17 | struct audio_buffer_pool *init_audio() { 18 | 19 | static audio_format_t audio_format = { 20 | .format = AUDIO_BUFFER_FORMAT_PCM_S16, 21 | .sample_freq = 44100, 22 | .channel_count = 2, 23 | }; 24 | 25 | static struct audio_buffer_format producer_format = { 26 | .format = &audio_format, 27 | .sample_stride = 4 28 | }; 29 | 30 | struct audio_buffer_pool *producer_pool = audio_new_producer_pool(&producer_format, 3, 31 | SAMPLES_PER_BUFFER); // todo correct size 32 | bool __unused ok; 33 | const struct audio_format *output_format; 34 | struct audio_i2s_config config = { 35 | .data_pin = 26, 36 | .clock_pin_base = 27, 37 | .dma_channel = 1, 38 | .pio_sm = 3, 39 | }; 40 | 41 | output_format = audio_i2s_setup(&audio_format, &config); 42 | if (!output_format) { 43 | panic("PicoAudio: Unable to open audio device.\n"); 44 | } 45 | 46 | ok = audio_i2s_connect(producer_pool); 47 | assert(ok); 48 | audio_i2s_set_enabled(true); 49 | 50 | return producer_pool; 51 | } 52 | 53 | unsigned char reverse(unsigned char b) { 54 | b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; 55 | b = (b & 0xCC) >> 2 | (b & 0x33) << 2; 56 | b = (b & 0xAA) >> 1 | (b & 0x55) << 1; 57 | return b; 58 | } 59 | 60 | int main() 61 | { 62 | //stdio_init_all(); 63 | 64 | 65 | sdring_init(false); 66 | sdring_set_stream(START_SECTOR, FILE_LEN_BYTES, true); 67 | 68 | struct audio_buffer_pool *ap = init_audio(); 69 | 70 | int32_t vol = 20; 71 | 72 | while (1) 73 | { 74 | struct audio_buffer *buffer = take_audio_buffer(ap, true); 75 | uint16_t* pcm_data_ptr = (uint16_t*)sdring_buffer; 76 | 77 | uint32_t sdring_idx = sdring_get_data_in_ringbuffer_blocking(buffer->max_sample_count) << 1; 78 | assert(buffer->buffer->size >= sizeof(uint16_t) * buffer->max_sample_count * 2); 79 | int16_t *samples = (int16_t*)buffer->buffer->bytes; 80 | for (uint i = 0; i < buffer->max_sample_count * 2; i++) { 81 | int16_t sample = pcm_data_ptr[(sdring_idx + i) & SDRING_INDEX_MASK_HALFWORDS]; 82 | samples[i] = (vol * sample) >> 8u; 83 | } 84 | sdring_release_ringbuffer(); 85 | buffer->sample_count = buffer->max_sample_count; 86 | give_audio_buffer(ap, buffer); 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /sdaudio/sdring.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pico/stdlib.h" 3 | 4 | #include "pico/sd_card.h" 5 | #include "hardware/dma.h" 6 | #include "hardware/pio.h" 7 | 8 | #include "sdring.h" 9 | 10 | #define SDRING_BLOCKS_PER_READ 32 11 | #define SDRING_WORDS_PER_READ (SDRING_BLOCKS_PER_READ * 128) 12 | 13 | uint32_t sdring_buffer[SDRING_BUF_LEN_WORDS] __attribute__((aligned(1 << SDRING_BUF_LOG_SIZE_BYTES))); 14 | 15 | // Location in buffer that was returned to the user on last read 16 | // We should write past here. 17 | static uint32_t* sdring_prev_buffer_ptr; 18 | 19 | // Location in buffer that will be returned to the user on next read 20 | static uint32_t* sdring_buffer_ptr; 21 | 22 | // Start and end of current transfer 23 | static uint32_t* sdring_start_write_addr; 24 | static uint32_t* sdring_target_write_addr; 25 | 26 | // Next block to request 27 | static uint32_t sdring_next_block_to_request; 28 | 29 | // Source data 30 | static uint32_t sdring_source_start_block; 31 | static uint32_t sdring_source_end_block; // Actually one past end 32 | static uint32_t sdring_source_data_len; // Note this is in bytes. 33 | 34 | inline static bool sdring_eof() 35 | { 36 | return ((sdring_next_block_to_request - sdring_source_start_block) << 9) >= sdring_source_data_len; 37 | } 38 | 39 | static void sdring_start_transfer() 40 | { 41 | if (!sd_scatter_read_complete(NULL, NULL)) 42 | return; 43 | 44 | // Previous transfer is complete 45 | sdring_start_write_addr = sdring_target_write_addr; 46 | 47 | if (sdring_eof()) 48 | return; 49 | 50 | // Set window for next transfer 51 | uint32_t next_idx = sdring_target_write_addr - sdring_buffer; 52 | next_idx = (next_idx + SDRING_WORDS_PER_READ) & SDRING_BUF_IDX_MASK; 53 | 54 | // Don't catch up with the read pointer. 55 | uint32_t read_idx = sdring_prev_buffer_ptr - sdring_buffer; 56 | uint32_t space_available = (read_idx - next_idx) & SDRING_BUF_IDX_MASK; 57 | if (space_available <= SDRING_WORDS_PER_READ) 58 | return; 59 | 60 | // Read 61 | uint32_t blocks_to_read = MIN(SDRING_BLOCKS_PER_READ, sdring_source_end_block - sdring_next_block_to_request); 62 | sd_readblocks_async(sdring_start_write_addr, sdring_next_block_to_request, blocks_to_read); 63 | sdring_target_write_addr = sdring_buffer + next_idx; 64 | sdring_next_block_to_request += SDRING_BLOCKS_PER_READ; 65 | } 66 | 67 | // Configure the flash streaming resources 68 | void sdring_init(bool byte_swap) 69 | { 70 | sd_init_4pins(); 71 | sd_set_clock_divider(2); 72 | sd_set_byteswap_on_read(byte_swap); 73 | } 74 | 75 | // Set the data to stream and optionally start streaming. 76 | // If start_streaming is false then start the stream by calling reset_stream. 77 | void sdring_set_stream(uint32_t start_block, uint32_t len_bytes, bool start_streaming) 78 | { 79 | sdring_source_start_block = start_block; 80 | sdring_source_end_block = start_block + (len_bytes + 511) / 512; 81 | sdring_source_data_len = len_bytes; 82 | 83 | sdring_reset_stream(); 84 | } 85 | 86 | // Reset the stream to the beginning. 87 | void sdring_reset_stream() 88 | { 89 | // Reset state 90 | sdring_next_block_to_request = sdring_source_start_block; 91 | sdring_start_write_addr = sdring_buffer; 92 | sdring_target_write_addr = sdring_buffer; 93 | sdring_prev_buffer_ptr = sdring_buffer + SDRING_BUF_LEN_WORDS - 1; 94 | sdring_buffer_ptr = sdring_buffer; 95 | 96 | // Wait for any active transfers to complete. 97 | while (!sd_scatter_read_complete(NULL, NULL)) 98 | { 99 | //__breakpoint(); 100 | } 101 | 102 | // Kick off first read 103 | sdring_start_transfer(); 104 | } 105 | 106 | // Request a fixed amount of data from flash and access in ring buffer. 107 | // The flash ring buffer is aligned and you may need to wrap the returned index 108 | // Total length requested at any given time must be <= FLASH_BUF_LEN_WORDS - 1, 109 | // and ideally you should hold less than FLASH_BUF_LEN_WORDS / 2 so that 110 | // new data can be buffered. 111 | // Blocks until the requested amount of data has been read from flash. 112 | // Calling this a subsequent time does *not* release the data, it must be 113 | // released explicitly. 114 | uint32_t sdring_get_data_in_ringbuffer_blocking(uint32_t len_req) 115 | { 116 | assert(len_req <= SDRING_BUF_LEN_WORDS / 2); 117 | assert(len_req > 0); 118 | 119 | uint32_t words_available = (sdring_start_write_addr - sdring_buffer_ptr) & SDRING_BUF_IDX_MASK; 120 | 121 | if (words_available < SDRING_BUF_LEN_WORDS / 4 && 122 | sd_scatter_read_complete(NULL, NULL) && 123 | !sdring_eof()) 124 | { 125 | assert(sdring_start_write_addr != sdring_prev_buffer_ptr); 126 | sdring_start_transfer(); 127 | } 128 | 129 | int sectors_read = 0; 130 | while (words_available + (128 * sectors_read) < len_req) { 131 | sectors_read = 0; 132 | if (sd_scatter_read_complete(NULL, §ors_read)) 133 | { 134 | assert(sdring_start_write_addr != sdring_prev_buffer_ptr); 135 | sdring_start_transfer(); 136 | } 137 | words_available = (sdring_start_write_addr - sdring_buffer_ptr) & SDRING_BUF_IDX_MASK; 138 | } 139 | 140 | uint32_t idx_out = sdring_buffer_ptr - sdring_buffer; 141 | uint32_t new_idx = (idx_out + len_req) & SDRING_BUF_IDX_MASK; 142 | sdring_buffer_ptr = sdring_buffer + new_idx; 143 | 144 | return idx_out; 145 | } 146 | 147 | // Release all data previously fetched with ringbuffer_blocking. 148 | void sdring_release_ringbuffer() 149 | { 150 | assert(sdring_prev_buffer_ptr != sdring_buffer_ptr); 151 | 152 | // Set prev pointer to one less than buffer pointer as buffer pointer must always be 153 | // one ahead of write pointer to prevent lock up. 154 | sdring_prev_buffer_ptr = sdring_buffer + (((sdring_buffer_ptr - sdring_buffer) - 1) & SDRING_BUF_IDX_MASK); 155 | 156 | // Check whether we should now restart the transfer 157 | if (sd_scatter_read_complete(NULL, NULL)) 158 | { 159 | sdring_start_transfer(); 160 | } 161 | } 162 | 163 | -------------------------------------------------------------------------------- /sdaudio/sdring.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Michael Bell 2 | // You may use this under the terms of the BSD 3 clause license. 3 | 4 | // Size of the ring buffer - must be a power of 2 >= 2^11 and <= 2^15 bytes. 5 | #define SDRING_BUF_LOG_SIZE_BYTES 16 6 | #define SDRING_BUF_LEN_WORDS (1 << (SDRING_BUF_LOG_SIZE_BYTES - 2)) 7 | #define SDRING_BUF_IDX_MASK (SDRING_BUF_LEN_WORDS - 1) 8 | 9 | // Configure the flash streaming resources 10 | void sdring_init(bool byte_swap); 11 | 12 | // Set the data to stream and optionally start streaming. 13 | // If start_streaming is false then start the stream by calling reset_stream. 14 | void sdring_set_stream(uint32_t start_block, uint32_t len_bytes, bool start_streaming); 15 | 16 | // Reset the stream to the beginning. 17 | void sdring_reset_stream(); 18 | 19 | // Request a fixed amount of data from flash and access in ring buffer. 20 | // The flash ring buffer is aligned and you may need to wrap the returned index 21 | // Total length requested at any given time must be <= FLASH_BUF_LEN_WORDS - 1, 22 | // and ideally you should hold less than FLASH_BUF_LEN_WORDS / 2 so that 23 | // new data can be buffered. 24 | // Blocks until the requested amount of data has been read from flash. 25 | // Calling this a subsequent time does *not* release the data, it must be 26 | // released explicitly. 27 | uint32_t sdring_get_data_in_ringbuffer_blocking(uint32_t len_req_in_words); 28 | 29 | // Release all data previously fetched with ringbuffer_blocking. 30 | void sdring_release_ringbuffer(); 31 | 32 | extern uint32_t sdring_buffer[SDRING_BUF_LEN_WORDS] __attribute__((aligned(1 << SDRING_BUF_LOG_SIZE_BYTES))); -------------------------------------------------------------------------------- /sdgif/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | duck.h 3 | *.gif 4 | -------------------------------------------------------------------------------- /sdgif/.vscode/.cortex-debug.peripherals.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /sdgif/.vscode/.cortex-debug.registers.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /sdgif/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [ 9 | "_DEBUG", 10 | "UNICODE", 11 | "_UNICODE" 12 | ], 13 | "windowsSdkVersion": "10.0.18362.0", 14 | "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.28.29333/bin/Hostx64/x64/cl.exe", 15 | "cStandard": "c17", 16 | "cppStandard": "c++17", 17 | "intelliSenseMode": "windows-msvc-x64", 18 | "configurationProvider": "ms-vscode.cmake-tools", 19 | "compileCommands": "${workspaceFolder}/build/compile_commands.json" 20 | } 21 | ], 22 | "version": 4 23 | } -------------------------------------------------------------------------------- /sdgif/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Pico Debug", 6 | "type":"cortex-debug", 7 | "cwd": "${workspaceRoot}", 8 | "executable": "${command:cmake.launchTargetPath}", 9 | "request": "launch", 10 | "servertype": "external", 11 | // This may need to be arm-none-eabi-gdb depending on your system 12 | "gdbPath": "arm-none-eabi-gdb", 13 | // Connect to an already running OpenOCD instance 14 | "gdbTarget": "inkypi:3333", 15 | "svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd", 16 | "runToMain": true, 17 | "postRestartCommands": [ 18 | "break main", 19 | "continue" 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /sdgif/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.pio": "asm", 4 | "pio.h": "c", 5 | "sd_card.h": "c" 6 | } 7 | } -------------------------------------------------------------------------------- /sdgif/AnimatedGIF.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 BitBank Software, Inc. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | //=========================================================================== 12 | 13 | #ifndef __ANIMATEDGIF__ 14 | #define __ANIMATEDGIF__ 15 | #if defined( PICO_BUILD ) || defined( __MACH__ ) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) 16 | #include 17 | #include 18 | #include 19 | #include 20 | #define memcpy_P memcpy 21 | #define PROGMEM 22 | #else 23 | #include 24 | #endif 25 | // 26 | // GIF Animator 27 | // Written by Larry Bank 28 | // Copyright (c) 2020 BitBank Software, Inc. 29 | // bitbank@pobox.com 30 | // 31 | // Designed to decode images up to 480x320 32 | // using less than 22K of RAM 33 | // 34 | 35 | /* GIF Defines and variables */ 36 | #define MAX_CHUNK_SIZE 256 37 | #define LZW_BUF_SIZE (8*MAX_CHUNK_SIZE) 38 | #define LZW_BUF_MASK ((1 << 11) - 1) 39 | #define MAX_WIDTH 320 40 | #define FILE_BUF_SIZE 8192 41 | 42 | #define PIXEL_FIRST 0 43 | #define PIXEL_LAST 4096 44 | #define LINK_UNUSED 5911 // 0x1717 to use memset 45 | #define LINK_END 5912 46 | #define MAX_HASH 5003 47 | #define MAXMAXCODE 4096 48 | 49 | // RGB565 pixel byte order in the palette 50 | #define BIG_ENDIAN_PIXELS 0 51 | #define LITTLE_ENDIAN_PIXELS 1 52 | 53 | enum { 54 | GIF_PALETTE_RGB565 = 0, // default 55 | GIF_PALETTE_RGB888 56 | }; 57 | // 58 | // Draw callback pixel type 59 | // RAW = 8-bit palettized pixels requiring transparent pixel handling 60 | // COOKED = 16 or 24-bpp fully rendered pixels ready for display 61 | // 62 | enum { 63 | GIF_DRAW_RAW = 0, 64 | GIF_DRAW_COOKED 65 | }; 66 | 67 | enum { 68 | GIF_SUCCESS = 0, 69 | GIF_DECODE_ERROR, 70 | GIF_TOO_WIDE, 71 | GIF_INVALID_PARAMETER, 72 | GIF_UNSUPPORTED_FEATURE, 73 | GIF_FILE_NOT_OPEN, 74 | GIF_EARLY_EOF, 75 | GIF_EMPTY_FRAME, 76 | GIF_BAD_FILE, 77 | GIF_ERROR_MEMORY 78 | }; 79 | 80 | typedef struct gif_file_tag 81 | { 82 | int32_t iPos; // current file position 83 | int32_t iSize; // file size 84 | uint8_t *pData; // memory file pointer 85 | void * fHandle; // class pointer to File/SdFat or whatever you want 86 | } GIFFILE; 87 | 88 | typedef struct gif_info_tag 89 | { 90 | int32_t iFrameCount; // total frames in file 91 | int32_t iDuration; // duration of animation in milliseconds 92 | int32_t iMaxDelay; // maximum frame delay 93 | int32_t iMinDelay; // minimum frame delay 94 | } GIFINFO; 95 | 96 | typedef struct gif_draw_tag 97 | { 98 | int iX, iY; // Corner offset of this frame on the canvas 99 | int y; // current line being drawn (0 = top line of image) 100 | int iWidth, iHeight; // size of this frame 101 | uint8_t *pPixels; // 8-bit source pixels for this line 102 | uint16_t *pPalette; // little or big-endian RGB565 palette entries (default) 103 | uint8_t *pPalette24; // RGB888 palette (optional) 104 | uint8_t ucTransparent; // transparent color 105 | uint8_t ucHasTransparency; // flag indicating the transparent color is in use 106 | uint8_t ucDisposalMethod; // frame disposal method 107 | uint8_t ucBackground; // background color 108 | } GIFDRAW; 109 | 110 | // Callback function prototypes 111 | typedef int32_t (GIF_READ_CALLBACK)(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen); 112 | typedef int32_t (GIF_SEEK_CALLBACK)(GIFFILE *pFile, int32_t iPosition); 113 | typedef void (GIF_DRAW_CALLBACK)(GIFDRAW *pDraw); 114 | typedef void * (GIF_OPEN_CALLBACK)(const char *szFilename, int32_t *pFileSize); 115 | typedef void (GIF_CLOSE_CALLBACK)(void *pHandle); 116 | typedef void * (GIF_ALLOC_CALLBACK)(uint32_t iSize); 117 | typedef void (GIF_FREE_CALLBACK)(void *buffer); 118 | // 119 | // our private structure to hold a GIF image decode state 120 | // 121 | typedef struct gif_image_tag 122 | { 123 | int iWidth, iHeight, iCanvasWidth, iCanvasHeight; 124 | int iX, iY; // GIF corner offset 125 | int iBpp; 126 | int iError; // last error 127 | int iFrameDelay; // delay in milliseconds for this frame 128 | int iXCount, iYCount; // decoding position in image (countdown values) 129 | int iLZWReadOff; // current LZW data read offset 130 | int iLZWWriteOff; // current LZW data write offset 131 | int iCommentPos; // file offset of start of comment data 132 | short sCommentLen; // length of comment 133 | GIF_READ_CALLBACK *pfnRead; 134 | GIF_SEEK_CALLBACK *pfnSeek; 135 | GIF_DRAW_CALLBACK *pfnDraw; 136 | GIF_OPEN_CALLBACK *pfnOpen; 137 | GIF_CLOSE_CALLBACK *pfnClose; 138 | GIFFILE GIFFile; 139 | unsigned char *pFrameBuffer; 140 | unsigned char *pPixels, *pOldPixels; 141 | unsigned char ucLineBuf[MAX_WIDTH]; // current line 142 | unsigned char ucFileBuf[FILE_BUF_SIZE]; // holds temp data and pixel stack 143 | unsigned short pPalette[384]; // can hold RGB565 or RGB888 - set in begin() 144 | unsigned short pLocalPalette[384]; // color palettes for GIF images 145 | unsigned char ucLZW[LZW_BUF_SIZE]; // holds 6 chunks (6x255) of GIF LZW data packed together 146 | unsigned short usGIFTable[4096]; 147 | unsigned char ucGIFPixels[8192]; 148 | unsigned char bEndOfFrame; 149 | unsigned char ucGIFBits, ucBackground, ucTransparent, ucCodeStart, ucMap, bUseLocalPalette, ucLittleEndian; 150 | unsigned char ucPaletteType; // RGB565 or RGB888 151 | unsigned char ucDrawType; // RAW or COOKED 152 | } GIFIMAGE; 153 | 154 | #ifdef __cplusplus 155 | // 156 | // The GIF class wraps portable C code which does the actual work 157 | // 158 | class AnimatedGIF 159 | { 160 | public: 161 | int open(uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw); 162 | int open(const char *szFilename, GIF_OPEN_CALLBACK *pfnOpen, GIF_CLOSE_CALLBACK *pfnClose, GIF_READ_CALLBACK *pfnRead, GIF_SEEK_CALLBACK *pfnSeek, GIF_DRAW_CALLBACK *pfnDraw); 163 | void close(); 164 | void reset(); 165 | void begin(int iEndian, unsigned char ucPaletteType = GIF_PALETTE_RGB565); 166 | int playFrame(bool bSync, int *delayMilliseconds); 167 | int getCanvasWidth(); 168 | int allocFrameBuf(GIF_ALLOC_CALLBACK *pfnAlloc); 169 | int setDrawType(int iType); 170 | int freeFrameBuf(GIF_FREE_CALLBACK *pfnFree); 171 | uint8_t *getFrameBuf(); 172 | int getCanvasHeight(); 173 | int getInfo(GIFINFO *pInfo); 174 | int getLastError(); 175 | int getComment(char *destBuffer); 176 | 177 | private: 178 | GIFIMAGE _gif; 179 | }; 180 | #else 181 | // C interface 182 | int GIF_openRAM(GIFIMAGE *pGIF, uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw); 183 | int GIF_openFile(GIFIMAGE *pGIF, const char *szFilename, GIF_DRAW_CALLBACK *pfnDraw); 184 | void GIF_close(GIFIMAGE *pGIF); 185 | void GIF_begin(GIFIMAGE *pGIF, int iEndian , unsigned char ucPaletteType); 186 | void GIF_reset(GIFIMAGE *pGIF); 187 | int GIF_playFrame(GIFIMAGE *pGIF, int *delayMilliseconds); 188 | int GIF_getCanvasWidth(GIFIMAGE *pGIF); 189 | int GIF_getCanvasHeight(GIFIMAGE *pGIF); 190 | int GIF_getComment(GIFIMAGE *pGIF, char *destBuffer); 191 | int GIF_getInfo(GIFIMAGE *pGIF, GIFINFO *pInfo); 192 | int GIF_getLastError(GIFIMAGE *pGIF); 193 | int GIFInit(GIFIMAGE *pGIF); 194 | #endif // __cplusplus 195 | 196 | // Due to unaligned memory causing an exception, we have to do these macros the slow way 197 | #define INTELSHORT(p) ((*p) + (*(p+1)<<8)) 198 | #define INTELLONG(p) ((*p) + (*(p+1)<<8) + (*(p+2)<<16) + (*(p+3)<<24)) 199 | #define MOTOSHORT(p) (((*(p))<<8) + (*(p+1))) 200 | #define MOTOLONG(p) (((*p)<<24) + ((*(p+1))<<16) + ((*(p+2))<<8) + (*(p+3))) 201 | 202 | // Must be a 32-bit target processor 203 | #define REGISTER_WIDTH 32 204 | 205 | #endif // __ANIMATEDGIF__ 206 | -------------------------------------------------------------------------------- /sdgif/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.13) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | # Pull in Pico SDK (must be before project) 9 | include(pico_sdk_import.cmake) 10 | include(pico_extras_import.cmake) 11 | 12 | project(sdgif C CXX ASM) 13 | 14 | # Initialise the Pico SDK 15 | pico_sdk_init() 16 | 17 | # Add executable. Default name is the project name, version 0.1 18 | 19 | add_executable(sdgif sdgif.c gif.c st7789_lcd.c ) 20 | 21 | pico_set_program_name(sdgif "sdgif") 22 | pico_set_program_version(sdgif "0.1") 23 | 24 | pico_enable_stdio_uart(sdgif 1) 25 | pico_enable_stdio_usb(sdgif 0) 26 | 27 | pico_generate_pio_header(sdgif ${CMAKE_CURRENT_LIST_DIR}/st7789_lcd.pio) 28 | 29 | # Add the standard library to the build 30 | target_link_libraries(sdgif pico_stdlib) 31 | 32 | # Add any user requested libraries 33 | target_link_libraries(sdgif 34 | hardware_dma 35 | hardware_pio 36 | pico_sd_card 37 | ) 38 | 39 | pico_add_extra_outputs(sdgif) 40 | 41 | -------------------------------------------------------------------------------- /sdgif/pico_extras_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_extras_import.cmake 2 | 3 | # This can be dropped into an external project to help locate pico-extras 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH)) 7 | set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH}) 8 | message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT)) 12 | set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT}) 13 | message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH)) 17 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | if (NOT PICO_EXTRAS_PATH) 22 | if (PICO_EXTRAS_FETCH_FROM_GIT) 23 | include(FetchContent) 24 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 25 | if (PICO_EXTRAS_FETCH_FROM_GIT_PATH) 26 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 27 | endif () 28 | FetchContent_Declare( 29 | PICO_EXTRAS 30 | GIT_REPOSITORY https://github.com/raspberrypi/pico-extras 31 | GIT_TAG master 32 | ) 33 | if (NOT PICO_EXTRAS) 34 | message("Downloading PICO EXTRAS") 35 | FetchContent_Populate(PICO_EXTRAS) 36 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_SOURCE_DIR}) 37 | endif () 38 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 39 | else () 40 | if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras") 41 | set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras) 42 | message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}") 43 | else() 44 | message(FATAL_ERROR 45 | "PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif() 48 | endif () 49 | endif () 50 | 51 | set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS") 52 | set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable") 53 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS") 54 | 55 | get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 56 | if (NOT EXISTS ${PICO_EXTRAS_PATH}) 57 | message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found") 58 | endif () 59 | 60 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE) 61 | 62 | add_subdirectory(${PICO_EXTRAS_PATH} pico_extras) -------------------------------------------------------------------------------- /sdgif/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /sdgif/sdgif.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pico/stdlib.h" 3 | 4 | #include "pico/sd_card.h" 5 | #include "hardware/dma.h" 6 | #include "hardware/pio.h" 7 | 8 | #include "AnimatedGIF.h" 9 | #include "st7789_lcd.h" 10 | 11 | GIFIMAGE gif; 12 | ST7789 st; 13 | 14 | #define DISPLAY_ROWS 240 15 | #define DISPLAY_COLS 240 16 | 17 | #define ST_DATA_LEN (32 + ((DISPLAY_COLS + 1) >> 1)) 18 | 19 | struct { 20 | int minx; 21 | int miny; 22 | int maxx; 23 | int maxy; 24 | uint32_t st_data[2][ST_DATA_LEN]; 25 | } frame; 26 | 27 | void sdgif_draw(GIFDRAW* pDraw) 28 | { 29 | int width = MIN(pDraw->iWidth, frame.maxx - frame.minx - pDraw->iX); 30 | int y = MIN(pDraw->iY + pDraw->y, frame.maxy - frame.miny) + frame.miny; 31 | 32 | st7789_wait_for_next_dma_chan(&st); 33 | uint8_t* dataPtr = pDraw->pPixels; 34 | uint32_t* stPtr = frame.st_data[st.chan_idx]; 35 | 36 | if (pDraw->ucDisposalMethod == 2) 37 | { 38 | stPtr += st7789_add_pixels_at_cmd(stPtr, pDraw->iX + frame.minx, y, width); 39 | uint16_t bgPixel = pDraw->pPalette[pDraw->ucBackground]; 40 | for (int x = 0; x < width; ++x) { 41 | uint8_t data = *dataPtr++; 42 | uint16_t pixel; 43 | if (data == pDraw->ucTransparent) 44 | pixel = bgPixel; 45 | else 46 | pixel = pDraw->pPalette[data]; 47 | if (x & 1) *stPtr++ |= pixel; 48 | else *stPtr = pixel << 16; 49 | } 50 | if (width & 1) stPtr++; 51 | } 52 | else 53 | { 54 | if (pDraw->ucHasTransparency) { 55 | uint8_t tData = pDraw->ucTransparent; 56 | int x = 0, count = 0; 57 | uint32_t* stEndPtr = frame.st_data[st.chan_idx] + ST_DATA_LEN - 6; 58 | 59 | while (x < width) { 60 | while (*dataPtr++ == tData && x < width) ++x; 61 | if (x == width) break; 62 | 63 | // Back up as we incremented past the first non-transparent pixel 64 | dataPtr--; 65 | 66 | int startx = x; 67 | uint32_t* stStartPtr = stPtr; 68 | stPtr += 5; 69 | for (; x < width && *dataPtr != tData; ++x) { 70 | uint16_t pixel = pDraw->pPalette[*dataPtr++]; 71 | if ((x - startx) & 1) { 72 | *stPtr++ |= pixel; 73 | if (stPtr >= stEndPtr) { 74 | ++x; 75 | break; 76 | } 77 | } 78 | else *stPtr = pixel << 16; 79 | } 80 | if ((x - startx) & 1) stPtr++; 81 | 82 | st7789_add_pixels_at_cmd(stStartPtr, pDraw->iX + frame.minx + startx, y, x - startx); 83 | 84 | if (stPtr >= stEndPtr && x < width) 85 | { 86 | st7789_dma_buffer(&st, frame.st_data[st.chan_idx], stPtr - frame.st_data[st.chan_idx]); 87 | st7789_wait_for_next_dma_chan(&st); 88 | stPtr = frame.st_data[st.chan_idx]; 89 | stEndPtr = frame.st_data[st.chan_idx] + ST_DATA_LEN - 6; 90 | } 91 | } 92 | } 93 | else 94 | { 95 | stPtr += st7789_add_pixels_at_cmd(stPtr, pDraw->iX + frame.minx, y, width); 96 | for (int x = 0; x < width; ++x) { 97 | uint16_t pixel = pDraw->pPalette[*dataPtr++]; 98 | if (x & 1) *stPtr++ |= pixel; 99 | else *stPtr = pixel << 16; 100 | } 101 | if (width & 1) stPtr++; 102 | } 103 | } 104 | 105 | st7789_dma_buffer(&st, frame.st_data[st.chan_idx], stPtr - frame.st_data[st.chan_idx]); 106 | } 107 | 108 | #define SD_SECTOR_START 81920 109 | #define SD_BUFFER_SECTORS 6 110 | #define FILE_LEN 5702746 111 | 112 | typedef struct { 113 | uint32_t buffer[128 * SD_BUFFER_SECTORS]; 114 | uint32_t buffer_start; 115 | } SDData; 116 | 117 | SDData sd_data[2]; 118 | SDData* sd_data_read = &sd_data[0]; 119 | SDData* sd_data_write = &sd_data[1]; 120 | 121 | uint32_t read_stall_time; 122 | uint32_t seek_stall_time; 123 | 124 | void sdgif_init() 125 | { 126 | sd_readblocks_sync(sd_data_read->buffer, SD_SECTOR_START, SD_BUFFER_SECTORS); 127 | sd_data_read->buffer_start = 0; 128 | sd_readblocks_async(sd_data_write->buffer, SD_SECTOR_START + SD_BUFFER_SECTORS, SD_BUFFER_SECTORS); 129 | sd_data_write->buffer_start = SD_BUFFER_SECTORS; 130 | 131 | read_stall_time = 0; 132 | seek_stall_time = 0; 133 | } 134 | 135 | int32_t sdgif_read(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen) 136 | { 137 | assert(iLen <= 2 * SD_SECTOR_SIZE); 138 | 139 | int32_t bytes_read = MIN(iLen, FILE_LEN - pFile->iPos); 140 | int32_t bytes_to_read = bytes_read; 141 | 142 | uint32_t offset_into_buffer = pFile->iPos - (sd_data_read->buffer_start << 9); 143 | 144 | if (offset_into_buffer < 0 || offset_into_buffer + bytes_to_read > SD_SECTOR_SIZE * SD_BUFFER_SECTORS) { 145 | uint32_t offset_into_wr_buffer = pFile->iPos - (sd_data_write->buffer_start << 9); 146 | if (offset_into_buffer >= 0 && offset_into_wr_buffer + bytes_to_read <= SD_SECTOR_SIZE * SD_BUFFER_SECTORS) { 147 | int rc; 148 | 149 | int32_t bytes_in_read_buffer = SD_SECTOR_SIZE * SD_BUFFER_SECTORS - offset_into_buffer; 150 | if (bytes_in_read_buffer > 0) 151 | { 152 | memcpy(pBuf, (uint8_t*)sd_data_read->buffer + offset_into_buffer, bytes_in_read_buffer); 153 | pBuf += bytes_in_read_buffer; 154 | bytes_to_read -= bytes_in_read_buffer; 155 | offset_into_buffer = 0; 156 | } 157 | else 158 | { 159 | offset_into_buffer = offset_into_wr_buffer; 160 | } 161 | 162 | if (!sd_scatter_read_complete(&rc)) 163 | { 164 | absolute_time_t start_stall = get_absolute_time(); 165 | while (!sd_scatter_read_complete(&rc)); 166 | read_stall_time += absolute_time_diff_us(start_stall, get_absolute_time()); 167 | } 168 | 169 | SDData* tmp = sd_data_read; 170 | sd_data_read = sd_data_write; 171 | sd_data_write = tmp; 172 | sd_data_write->buffer_start += 2*SD_BUFFER_SECTORS; 173 | sd_readblocks_async(sd_data_write->buffer, SD_SECTOR_START + sd_data_write->buffer_start, SD_BUFFER_SECTORS); 174 | } 175 | else 176 | { 177 | int rc; 178 | absolute_time_t start_stall = get_absolute_time(); 179 | while (!sd_scatter_read_complete(&rc)); 180 | 181 | sd_data_read->buffer_start = pFile->iPos >> 9; 182 | sd_readblocks_sync(sd_data_read->buffer, SD_SECTOR_START + sd_data_read->buffer_start, SD_BUFFER_SECTORS); 183 | offset_into_buffer = pFile->iPos & 0x1ffu; 184 | seek_stall_time += absolute_time_diff_us(start_stall, get_absolute_time()); 185 | 186 | sd_data_write->buffer_start = sd_data_read->buffer_start + SD_BUFFER_SECTORS; 187 | sd_readblocks_async(sd_data_write->buffer, SD_SECTOR_START + sd_data_write->buffer_start, SD_BUFFER_SECTORS); 188 | } 189 | assert(offset_into_buffer >= 0 && offset_into_buffer <= SD_SECTOR_SIZE * SD_BUFFER_SECTORS); 190 | } 191 | 192 | memcpy(pBuf, (uint8_t*)sd_data_read->buffer + offset_into_buffer, bytes_to_read); 193 | 194 | pFile->iPos += bytes_read; 195 | return bytes_read; 196 | } 197 | 198 | int32_t sdgif_seek(GIFFILE *pFile, int32_t iPosition) 199 | { 200 | if (iPosition < 0) iPosition = 0; 201 | else if (iPosition >= pFile->iSize) iPosition = pFile->iSize-1; 202 | 203 | pFile->iPos = iPosition; 204 | return pFile->iPos; 205 | } 206 | 207 | int main() 208 | { 209 | stdio_init_all(); 210 | 211 | sd_init_4pins(); 212 | sd_set_clock_divider(5); 213 | 214 | // Force the pico power supply into continuous mode 215 | // This helps reduce flickering when everything goes idle for 20ms 216 | // between frames. 217 | gpio_init(23); 218 | gpio_set_dir(23, true); 219 | gpio_put(23, true); 220 | 221 | st = st7789_init(pio0, 0); 222 | 223 | // Clear screen 224 | st7789_start_pixels(&st, DISPLAY_ROWS * DISPLAY_COLS); 225 | st7789_dma_repeat_pixel_one_channel(&st, 0x0000, DISPLAY_ROWS * DISPLAY_COLS); 226 | 227 | GIF_begin(&gif, LITTLE_ENDIAN_PIXELS, GIF_PALETTE_RGB565); 228 | while (1) { 229 | sdgif_init(); 230 | gif.iError = GIF_SUCCESS; 231 | gif.pfnRead = sdgif_read; 232 | gif.pfnSeek = sdgif_seek; 233 | gif.pfnDraw = sdgif_draw; 234 | gif.pfnOpen = NULL; 235 | gif.pfnClose = NULL; 236 | gif.GIFFile.iSize = FILE_LEN; 237 | if (!GIFInit(&gif)) 238 | { 239 | printf("Open failed\n"); 240 | } 241 | else 242 | { 243 | //gif.pfnRead = gifRead; 244 | frame.minx = MAX(0, (DISPLAY_COLS - gif.iCanvasWidth) >> 1); 245 | frame.maxx = MIN(DISPLAY_COLS - 1, frame.minx + gif.iCanvasWidth); 246 | frame.miny = MAX(0, (DISPLAY_ROWS - gif.iCanvasHeight) >> 1); 247 | frame.maxy = MIN(DISPLAY_ROWS - 1, frame.miny + gif.iCanvasHeight); 248 | 249 | int delay = 10; 250 | absolute_time_t start_time = get_absolute_time(); 251 | while (GIF_playFrame(&gif, &delay)) { 252 | uint32_t frame_time = absolute_time_diff_us(start_time, get_absolute_time()); 253 | 254 | printf("Frame time %dus \tRead stall: %dus \tSeek stall %dus\n", frame_time, read_stall_time, seek_stall_time); 255 | read_stall_time = 0; 256 | seek_stall_time = 0; 257 | 258 | delay *= 1000; 259 | delay -= absolute_time_diff_us(start_time, get_absolute_time()); 260 | if (delay > 0) 261 | sleep_us(delay); 262 | 263 | start_time = get_absolute_time(); 264 | } 265 | GIF_close(&gif); 266 | printf("Loop done\n"); 267 | } 268 | } 269 | 270 | return 0; 271 | } 272 | -------------------------------------------------------------------------------- /sdgif/st7789_lcd.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include "pico/stdlib.h" 11 | #include "hardware/pio.h" 12 | #include "hardware/gpio.h" 13 | #include "hardware/dma.h" 14 | 15 | #include "st7789_lcd.h" 16 | #include "st7789_lcd.pio.h" 17 | 18 | #define SCREEN_WIDTH 240 19 | #define SCREEN_HEIGHT 240 20 | 21 | #define PIN_DIN 0 22 | #define PIN_CLK 1 23 | #define PIN_CS 2 // Must be CLK + 1 24 | #define PIN_DC 3 // Must be CLK + 2 25 | #define PIN_RESET 4 26 | #define PIN_BL 5 27 | 28 | #define SERIAL_CLK_DIV 2.f 29 | 30 | // Format: cmd length (including cmd byte), post delay in units of 5 ms, then cmd payload 31 | // Note the delays have been shortened a little 32 | #if 1 33 | static const uint8_t st7789_init_seq[] = { 34 | 1, 20, 0x01, // Software reset 35 | 1, 10, 0x11, // Exit sleep mode 36 | 2, 2, 0x3a, 0x55, // Set colour mode to 16 bit 37 | 2, 0, 0x36, 0x00, // Set MADCTL: row then column, refresh is bottom to top ???? 38 | 5, 0, 0x2a, 0x00, 0x00, 0x00, 0xf0, // CASET: column addresses from 0 to 240 (f0) 39 | 5, 0, 0x2b, 0x00, 0x00, 0x00, 0xf0, // RASET: row addresses from 0 to 240 (f0) 40 | 1, 2, 0x21, // Inversion on, then 10 ms delay (supposedly a hack?) 41 | 1, 2, 0x13, // Normal display on, then 10 ms delay 42 | 1, 2, 0x29, // Main screen turn on, then wait 500 ms 43 | 0 // Terminate list 44 | }; 45 | #else 46 | // Modified sequence from Pimoroni's python library 47 | // This plays with some power settings which makes the image slightly brighter, 48 | // but if anything seems to make flickering slightly worse. 49 | static const uint8_t st7789_init_seq[] = { 50 | 1, 30, 0x01, 51 | 2, 0, 0x36, 0x00, 52 | 2, 0, 0x3A, 0x55, 53 | 2, 0, 0xB7, 0x14, 54 | 2, 0, 0xBB, 0x37, 55 | 3, 0, 0xC2, 0x01, 0xFF, 56 | 2, 0, 0xC3, 0x12, 57 | 2, 0, 0xC4, 0x20, 58 | 3, 0, 0xD0, 0xA4, 0XA1, 59 | 2, 0, 0xC6, 0x0F, 60 | 5, 0, 0x2a, 0x00, 0x00, 0x00, 0xf0, // CASET: column addresses from 0 to 240 (f0) 61 | 5, 0, 0x2b, 0x00, 0x00, 0x00, 0xf0, // RASET: row addresses from 0 to 240 (f0) 62 | 1, 2, 0x21, 63 | 1, 2, 0x13, 64 | 1, 10, 0x11, 65 | 1, 2, 0x29, 66 | 0 67 | }; 68 | #endif 69 | 70 | static inline uint32_t st7789_encode_cmd(uint8_t cmd, uint32_t data_count) 71 | { 72 | uint32_t instr = 0x100; 73 | if (data_count) instr = (data_count * 8) << 8; 74 | instr |= cmd; 75 | return instr; 76 | } 77 | 78 | static inline void lcd_write_cmd(PIO pio, uint sm, const uint8_t *cmd, int count) { 79 | st7789_lcd_wait_idle(pio, sm); 80 | 81 | uint32_t instr = st7789_encode_cmd(*cmd++, count - 1); 82 | pio_sm_put(pio, sm, instr); 83 | 84 | if (count >= 2) { 85 | count--; 86 | while (count > 0) { 87 | instr = 0; 88 | for (int i = 0; i < count && i < 4; ++i) { 89 | instr <<= 8; 90 | instr |= cmd[i]; 91 | } 92 | if (count < 4) 93 | instr <<= 8 * (4 - count); 94 | pio_sm_put(pio, sm, instr); 95 | count -= 4; 96 | } 97 | } 98 | } 99 | 100 | static void st7789_create_dma_channels(PIO pio, uint sm, uint chan[2]) 101 | { 102 | chan[0] = dma_claim_unused_channel(true); 103 | chan[1] = dma_claim_unused_channel(true); 104 | 105 | dma_channel_config c = dma_channel_get_default_config(chan[0]); 106 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 107 | channel_config_set_dreq(&c, pio_get_dreq(pio, sm, true)); 108 | channel_config_set_read_increment(&c, true); 109 | channel_config_set_write_increment(&c, false); 110 | 111 | dma_channel_configure( 112 | chan[0], // Channel to be configured 113 | &c, // The configuration we just created 114 | &pio->txf[sm], // The write address 115 | NULL, // The initial read address - set later 116 | 0, // Number of transfers - set later 117 | false // Don't start yet 118 | ); 119 | 120 | c = dma_channel_get_default_config(chan[1]); 121 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 122 | channel_config_set_dreq(&c, pio_get_dreq(pio, sm, true)); 123 | channel_config_set_read_increment(&c, true); 124 | channel_config_set_write_increment(&c, false); 125 | 126 | dma_channel_configure( 127 | chan[1], // Channel to be configured 128 | &c, // The configuration we just created 129 | &pio->txf[sm], // The write address 130 | NULL, // The initial read address - set later 131 | 0, // Number of transfers - set later 132 | false // Don't start yet 133 | ); 134 | } 135 | 136 | ST7789 st7789_init(PIO pio, uint sm) { 137 | ST7789 st; 138 | st.pio = pio; 139 | st.sm = sm; 140 | 141 | uint offset = pio_add_program(pio, &st7789_lcd_program); 142 | st7789_lcd_program_init(pio, sm, offset, PIN_DIN, PIN_CLK, SERIAL_CLK_DIV); 143 | 144 | gpio_init(PIN_RESET); 145 | gpio_init(PIN_BL); 146 | gpio_set_dir(PIN_RESET, GPIO_OUT); 147 | gpio_set_dir(PIN_BL, GPIO_OUT); 148 | 149 | gpio_put(PIN_RESET, 1); 150 | gpio_put(PIN_BL, 0); 151 | 152 | const uint8_t *cmd = st7789_init_seq; 153 | while (*cmd) { 154 | lcd_write_cmd(pio, sm, cmd + 2, *cmd); 155 | sleep_ms(*(cmd + 1) * 5); 156 | cmd += *cmd + 2; 157 | } 158 | 159 | gpio_put(PIN_BL, 1); 160 | 161 | st7789_create_dma_channels(st.pio, st.sm, st.chan); 162 | st.chan_idx = 0; 163 | 164 | return st; 165 | } 166 | 167 | void st7789_start_pixels(ST7789* st, uint32_t num_pixels) { 168 | st7789_lcd_wait_idle(st->pio, st->sm); 169 | 170 | // RAMWR 171 | uint32_t cmd = st7789_encode_cmd(0x2c, num_pixels * 2); 172 | pio_sm_put(st->pio, st->sm, cmd); 173 | } 174 | 175 | void st7789_start_pixels_at(ST7789* st, uint8_t x, uint8_t y, uint32_t num_pixels) { 176 | uint8_t ca_cmd[] = {0x2a, 0x00, x, 0x00, 0xf0}; // CASET: column addresses from 0 to 240 (f0) 177 | uint8_t ra_cmd[] = {0x2b, 0x00, y, 0x00, 0xf0}; // RASET: row addresses from 0 to 240 (f0) 178 | 179 | lcd_write_cmd(st->pio, st->sm, ca_cmd, 5); 180 | lcd_write_cmd(st->pio, st->sm, ra_cmd, 5); 181 | 182 | // RAMWR 183 | uint32_t cmd = st7789_encode_cmd(0x2c, num_pixels * 2); 184 | pio_sm_put(st->pio, st->sm, cmd); 185 | } 186 | 187 | uint32_t st7789_add_pixels_at_cmd(uint32_t* buffer, uint8_t x, uint8_t y, uint32_t num_pixels) { 188 | uint8_t ca_cmd[] = {0x2a, 0x00, x, 0x00, 0xf0}; 189 | uint8_t ra_cmd[] = {0x2b, 0x00, y, 0x00, 0xf0}; 190 | 191 | *buffer++ = st7789_encode_cmd(0x2a, 4); 192 | *buffer++ = 0xf0 | ((uint32_t)x << 16); 193 | *buffer++ = st7789_encode_cmd(0x2b, 4); 194 | *buffer++ = 0xf0 | ((uint32_t)y << 16); 195 | *buffer++ = st7789_encode_cmd(0x2c, num_pixels * 2); 196 | 197 | return 5; 198 | } 199 | 200 | static inline void st7789_chain_or_trigger(uint this_chan, uint other_chan, uint ctrl) 201 | { 202 | if (dma_channel_is_busy(other_chan)) { 203 | // Other channel is busy, chain this one to it 204 | dma_channel_hw_addr(this_chan)->al1_ctrl = ctrl; 205 | uint other_ctrl = dma_channel_hw_addr(other_chan)->ctrl_trig; 206 | other_ctrl &= ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS; 207 | other_ctrl |= this_chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB; 208 | dma_channel_hw_addr(other_chan)->al1_ctrl = other_ctrl; 209 | 210 | if (!dma_channel_is_busy(other_chan) && !dma_channel_is_busy(this_chan)) { 211 | // Manually start this channel 212 | dma_channel_hw_addr(this_chan)->ctrl_trig = ctrl; 213 | } 214 | } else { 215 | dma_channel_hw_addr(this_chan)->ctrl_trig = ctrl; 216 | } 217 | } 218 | 219 | void st7789_dma_buffer(ST7789* st, const uint32_t* data, uint len) 220 | { 221 | uint this_chan = st->chan[st->chan_idx]; 222 | uint other_chan = st->chan[st->chan_idx ^ 1]; 223 | 224 | // Ensure any previous transfer is finished. 225 | dma_channel_wait_for_finish_blocking(this_chan); 226 | 227 | dma_channel_hw_addr(this_chan)->read_addr = (uintptr_t)data; 228 | dma_channel_hw_addr(this_chan)->transfer_count = len; 229 | uint ctrl = dma_channel_hw_addr(this_chan)->ctrl_trig; 230 | ctrl &= ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS; 231 | ctrl |= DMA_CH0_CTRL_TRIG_INCR_READ_BITS | (this_chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB); 232 | 233 | st7789_chain_or_trigger(this_chan, other_chan, ctrl); 234 | 235 | st->chan_idx ^= 1; 236 | } 237 | 238 | void st7789_dma_buffer_one_channel(ST7789* st, const uint32_t* data, uint len) 239 | { 240 | uint chan = st->chan[st->chan_idx]; 241 | 242 | dma_channel_wait_for_finish_blocking(chan); 243 | dma_channel_hw_addr(chan)->read_addr = (uintptr_t)data; 244 | dma_channel_hw_addr(chan)->transfer_count = len; 245 | uint ctrl = dma_channel_hw_addr(chan)->ctrl_trig; 246 | ctrl &= ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS; 247 | ctrl |= DMA_CH0_CTRL_TRIG_INCR_READ_BITS | (chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB); 248 | dma_channel_hw_addr(chan)->ctrl_trig = ctrl; 249 | } 250 | 251 | static uint32_t pixel_to_dma[3]; 252 | 253 | void st7789_dma_repeat_pixel(ST7789* st, uint16_t pixel, uint repeats) 254 | { 255 | uint this_chan = st->chan[st->chan_idx]; 256 | uint other_chan = st->chan[st->chan_idx ^ 1]; 257 | 258 | dma_channel_wait_for_finish_blocking(this_chan); 259 | 260 | pixel_to_dma[st->chan_idx] = ((uint32_t)pixel << 16) | pixel; 261 | dma_channel_hw_addr(this_chan)->read_addr = (uintptr_t)&pixel_to_dma[st->chan_idx]; 262 | dma_channel_hw_addr(this_chan)->transfer_count = (repeats+1)>>1; 263 | uint ctrl = dma_channel_hw_addr(this_chan)->ctrl_trig; 264 | ctrl &= ~(DMA_CH0_CTRL_TRIG_INCR_READ_BITS | DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS); 265 | ctrl |= this_chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB; 266 | 267 | st7789_chain_or_trigger(this_chan, other_chan, ctrl); 268 | 269 | st->chan_idx ^= 1; 270 | } 271 | 272 | void st7789_dma_repeat_pixel_one_channel(ST7789* st, uint16_t pixel, uint repeats) 273 | { 274 | uint chan = st->chan[st->chan_idx]; 275 | 276 | dma_channel_wait_for_finish_blocking(chan); 277 | 278 | pixel_to_dma[2] = ((uint32_t)pixel << 16) | pixel; 279 | dma_channel_hw_addr(chan)->read_addr = (uintptr_t)&pixel_to_dma[2]; 280 | dma_channel_hw_addr(chan)->transfer_count = (repeats+1)>>1; 281 | uint ctrl = dma_channel_hw_addr(chan)->ctrl_trig; 282 | ctrl &= ~(DMA_CH0_CTRL_TRIG_INCR_READ_BITS | DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS); 283 | ctrl |= chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB; 284 | dma_channel_hw_addr(chan)->ctrl_trig = ctrl; 285 | } 286 | 287 | -------------------------------------------------------------------------------- /sdgif/st7789_lcd.h: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | PIO pio; 3 | uint sm; 4 | uint chan[2]; 5 | uint chan_idx; 6 | } ST7789; 7 | 8 | // Setup the display and return CB 9 | ST7789 st7789_init(PIO pio, uint sm); 10 | 11 | // Restart pixel output synchronously, pixels drawn at last location 12 | // (or full screen if no location ever given) 13 | void st7789_start_pixels(ST7789* st, uint32_t num_pixels); 14 | 15 | // Restart pixel output synchronously, at a given location 16 | void st7789_start_pixels_at(ST7789* st, uint8_t x, uint8_t y, uint32_t num_pixels); 17 | 18 | // Write a command to restart pixel output into a buffer for DMA 19 | uint32_t st7789_add_pixels_at_cmd(uint32_t* buffer, uint8_t x, uint8_t y, uint32_t num_pixels); 20 | 21 | // Send data to the display via DMA, chaining to the previous DMA 22 | void st7789_dma_buffer(ST7789* st, const uint32_t* data, uint len); 23 | 24 | // Send a single pixel multiple times, chaining to the previous DMA 25 | void st7789_dma_repeat_pixel(ST7789* st, uint16_t pixel, uint repeats); 26 | 27 | // Wait for next DMA channel to finish previous transfer, i.e. wait for last but one 28 | // transfer to finish. After this point you can reuse the buffer with index chan_idx. 29 | static inline void st7789_wait_for_next_dma_chan(ST7789* st) { 30 | dma_channel_wait_for_finish_blocking(st->chan[st->chan_idx]); 31 | } 32 | 33 | // Wait for previous DMA to finish, then send pixels 34 | void st7789_dma_buffer_one_channel(ST7789* st, const uint32_t* data, uint len); 35 | void st7789_dma_repeat_pixel_one_channel(ST7789* st, uint16_t pixel, uint repeats); 36 | -------------------------------------------------------------------------------- /sdgif/st7789_lcd.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | .program st7789_lcd 8 | .side_set 3 9 | 10 | ; This transmits 32-bit DMA'd data of the form: 11 | ; Header: | 24 bits | 8 bits | 12 | ; | Data bit count - 1 | Command | 13 | ; Data: (data bit count)/8 data bytes 14 | ; Bytes are sent from MSB first, so the last word should be right aligned 15 | ; if the data byte count isn't a multiple of 4. 16 | ; If the data byte count is not a multiple of 4 then the remaining part 17 | ; of the word is discarded. 18 | ; If no data is to be sent "data bit count - 1" should be 0, else it should 19 | ; be the number of bits to be sent minus one. 20 | 21 | ; Data on OUT pin 0 22 | ; Clock on side-set pin 0 23 | ; CS on side-set pin 1 24 | ; D/C on side-set pin 2 25 | 26 | top: 27 | out x, 24 side 0b110 ; Read data length 28 | jmp !x, top side 0b110 ; Discard if we just read all zeros 29 | 30 | set y, 7 side 0b110 ; Send 8 bit command 31 | cmd_loop: 32 | out pins, 1 side 0b000 33 | jmp y--, cmd_loop side 0b001 34 | 35 | jmp x--, data_next side 0b000 ; Data length 1 means no data, 36 | data_next: ; we never just have 1 bit 37 | jmp !x, top side 0b000 ; If no data go to next command 38 | 39 | data_loop: ; Send data 40 | out pins, 1 side 0b100 41 | jmp x--, data_loop side 0b101 42 | 43 | 44 | % c-sdk { 45 | 46 | static inline void st7789_lcd_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clk_pin, float clk_div) { 47 | pio_gpio_init(pio, data_pin); 48 | pio_gpio_init(pio, clk_pin); 49 | pio_gpio_init(pio, clk_pin + 1); 50 | pio_gpio_init(pio, clk_pin + 2); 51 | pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, true); 52 | pio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 3, true); 53 | pio_sm_config c = st7789_lcd_program_get_default_config(offset); 54 | sm_config_set_sideset_pins(&c, clk_pin); 55 | sm_config_set_out_pins(&c, data_pin, 1); 56 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 57 | sm_config_set_clkdiv(&c, clk_div); 58 | sm_config_set_out_shift(&c, false, true, 32); 59 | pio_sm_init(pio, sm, offset, &c); 60 | pio_sm_set_enabled(pio, sm, true); 61 | } 62 | 63 | // SM is done when it stalls on an empty FIFO 64 | 65 | static inline void st7789_lcd_wait_idle(PIO pio, uint sm) { 66 | uint32_t sm_stall_mask = 1u << (sm + PIO_FDEBUG_TXSTALL_LSB); 67 | pio->fdebug = sm_stall_mask; 68 | while (!(pio->fdebug & sm_stall_mask)) 69 | ; 70 | } 71 | %} 72 | --------------------------------------------------------------------------------