├── .github └── workflows │ └── build-release-on-tag.yml ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── boards ├── murmulator.h └── murmulator2.h ├── drivers ├── audio │ ├── CMakeLists.txt │ ├── audio.c │ ├── audio.h │ └── audio_i2s.pio ├── fatfs │ ├── 00history.txt │ ├── 00readme.txt │ ├── CMakeLists.txt │ ├── diskio.h │ ├── f_util.c │ ├── f_util.h │ ├── fatfs.cmake │ ├── ff.c │ ├── ff.h │ ├── ffconf.h │ ├── ffsystem.c │ └── ffunicode.c ├── graphics │ ├── CMakeLists.txt │ ├── font6x8.h │ ├── font8x16.h │ ├── font8x8.h │ ├── graphics.c │ └── graphics.h ├── hdmi │ ├── CMakeLists.txt │ ├── hdmi.c │ └── hdmi.h ├── nespad │ ├── CMakeLists.txt │ ├── nespad.cpp │ └── nespad.h ├── ps2 │ ├── CMakeLists.txt │ ├── ps2 (2).c │ ├── ps2.c │ └── ps2.h ├── ps2kbd │ ├── CMakeLists.txt │ ├── hid.h │ ├── ps2kbd_mrmltr.cpp │ ├── ps2kbd_mrmltr.h │ ├── ps2kbd_mrmltr.pio │ └── ps2kbd_mrmltr2.pio ├── sdcard │ ├── CMakeLists.txt │ ├── LICENSE │ ├── README.md │ ├── pio_spi.c │ ├── pio_spi.h │ ├── sdcard.c │ ├── sdcard.cmake │ ├── sdcard.h │ └── spi.pio ├── st7789 │ ├── CMakeLists.txt │ ├── st7789.c │ ├── st7789.h │ └── st7789.pio ├── tv-software │ ├── CMakeLists.txt │ ├── tv-software.c │ └── tv-software.h ├── tv │ ├── CMakeLists.txt │ ├── tv.c │ └── tv.h ├── usb │ ├── msc_disk.c │ ├── tusb_config.h │ ├── usb.c │ ├── usb.h │ └── usb_descriptors.c ├── usbfs │ ├── CMakeLists.txt │ ├── README.md │ ├── diskio.c │ ├── diskio.h │ ├── storage.c │ ├── tusb_config.h │ ├── usb.c │ ├── usb_descriptors.c │ ├── usbfs.c │ └── usbfs.h ├── vga-nextgen │ ├── CMakeLists.txt │ ├── vga.c │ └── vga.h └── ws2812 │ ├── CMakeLists.txt │ ├── ws2812.c │ ├── ws2812.h │ └── ws2812.pio ├── ext └── minigb_apu │ ├── LICENSE │ ├── minigb_apu.c │ └── minigb_apu.h ├── inc ├── gbcolors.h ├── hedley.h └── peanut_gb.h ├── memmap.ld ├── pico_sdk_import.cmake └── src ├── main.cpp └── tusb_config.h /.github/workflows/build-release-on-tag.yml: -------------------------------------------------------------------------------- 1 | name: Build and create a release when tag is pushed 2 | 3 | # Only deploy when a new tag is pushed 4 | on: 5 | push: 6 | tags: 7 | - "v*.*-alpha" 8 | - "v*.*.*" 9 | # branches: [ master ] 10 | # pull_request: 11 | # branches: [ master ] 12 | 13 | # Must match the project() name in CMakeLists.txt 14 | env: 15 | APP_NAME: gameboy-color 16 | 17 | # Allow this workflow to write back to the repository 18 | permissions: 19 | contents: write 20 | 21 | # Build binary and send to releases 22 | jobs: 23 | build-release: 24 | runs-on: ubuntu-latest 25 | name: Build and release 26 | steps: 27 | 28 | - name: Install dependencies 29 | run: | 30 | sudo apt update && \ 31 | sudo apt install -y git python3 && \ 32 | sudo apt install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential libusb-1.0-0-dev 33 | 34 | - name: Check out this repository 35 | uses: actions/checkout@v3 36 | 37 | - name: Print Working directory 38 | run: echo $HOME && pwd && ls -la 39 | 40 | - name: Update line containing pico_set_program_version() in CMakelists.txt with tag name. 41 | run: | 42 | # Extract the tag name that triggered the event and remove the 'refs/tags/' prefix 43 | input_string=${{ github.ref }} 44 | prefix="refs/tags/" 45 | tag="No versioninfo found" 46 | if [[ $input_string == $prefix* ]]; then 47 | echo "The string starts with 'refs/tags/'." 48 | tag="${input_string#$prefix}" 49 | echo "Tag is ${tag}" 50 | sed -i "s/^[[:space:]]*pico_set_program_version(.*/pico_set_program_version(${{ env.APP_NAME }} \"$tag\")/" CMakeLists.txt 51 | else 52 | echo "The string does not start with 'refs/tags/'." 53 | fi 54 | grep "pico_set_program_version" CMakeLists.txt 55 | 56 | - name: Install Pico SDk 57 | run: | 58 | cd $HOME && \ 59 | git clone --depth 1 https://github.com/raspberrypi/pico-sdk.git --branch master && \ 60 | cd pico-sdk/ && \ 61 | git submodule update --init 62 | 63 | - name: Build the basic version 64 | run: | 65 | export PICO_SDK_PATH=$HOME/pico-sdk && \ 66 | mkdir build && cd build && \ 67 | cmake .. && make 68 | 69 | - name: Build the overclocked versions 70 | run: | 71 | cd build && \ 72 | cmake -DOVERCLOCKING=288 .. && make && \ 73 | cmake -DOVERCLOCKING=300 .. && make && \ 74 | cmake -DOVERCLOCKING=312 .. && make && \ 75 | cmake -DOVERCLOCKING=333 .. && make && \ 76 | cmake -DOVERCLOCKING=366 .. && make && \ 77 | cmake -DOVERCLOCKING=396 .. && make && \ 78 | cmake -DOVERCLOCKING=412 .. && make 79 | 80 | 81 | - name: Create release 82 | uses: softprops/action-gh-release@v1 83 | if: startsWith(github.ref, 'refs/tags/') 84 | with: 85 | files: | 86 | bin/Release/**.uf2 87 | 88 | body_path: CHANGELOG.md 89 | 90 | 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | uf2 3 | bin/ 4 | .vscode/ 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v3.0.1 2 | - Software TV composite output instead of hardware ntsc/pal encoder 3 | 4 | # v3.0.0 5 | - VGA / HDMI / TFT / TV Composite outputs added 6 | - new filebrowser 7 | - speed improvements and optimizations 8 | - quality I2S sound output via TDA1387 or PCM5102 added 9 | 10 | Toxic palette fix 11 | 12 | # v2.2.0a 13 | 14 | Toxic palette fix 15 | 16 | # v2.2.0 17 | 18 | Added ability to save and load gamestate. Better menu system. 19 | 20 | 21 | # v2.1.0 22 | 23 | Various overclocking variants 24 | 25 | # v2.0.1 26 | 27 | GameBoy Color support, overall speed increase 28 | 29 | # v1.0.0 30 | 31 | Huge speedup, ingame menu on SELECT+START added 32 | 33 | # v0.7.0 34 | 35 | Speedup and cosmetics 36 | 37 | # v0.6.3 38 | 39 | Double keypress in menu fix 40 | 41 | # v0.6.2 42 | 43 | ROM loading bugfix 44 | 45 | 46 | # v0.6.1 47 | 48 | Fast load previous rom after reboot, prevents PICO flash wearoff also 49 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == 2 | if(WIN32) 3 | set(USERHOME $ENV{USERPROFILE}) 4 | else() 5 | set(USERHOME $ENV{HOME}) 6 | endif() 7 | set(sdkVersion 2.1.1) 8 | set(toolchainVersion 14_2_Rel1) 9 | set(picotoolVersion 2.1.1) 10 | set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) 11 | if (EXISTS ${picoVscode}) 12 | include(${picoVscode}) 13 | endif() 14 | # ==================================================================================== 15 | cmake_minimum_required(VERSION 3.13...3.23) 16 | set(PICO_BOARD_HEADER_DIRS ${CMAKE_CURRENT_LIST_DIR}/boards ) 17 | set(PICO_PLATFORM "rp2350" CACHE STRING "Chip type") 18 | #set(PICO_PLATFORM "rp2040" CACHE STRING "Chip type") 19 | #set(PICO_BOARD "murmulator" CACHE STRING "Board type") 20 | set(PICO_BOARD "murmulator2" CACHE STRING "Board type") 21 | include(pico_sdk_import.cmake) 22 | 23 | project(gameboy-color C CXX ASM) 24 | SET(BUILD_NAME "${PROJECT_NAME}") 25 | 26 | option(I2S "Enable I2S sound" OFF) 27 | option(I2S_CS4334 "Enable I2S CS4334 sound" OFF) 28 | option(VGA "Enable VGA" OFF) 29 | option(TFT "Enable TFT display" OFF) 30 | option(ILI9341 "Enable TFT ILI9341 display" OFF) 31 | option(HDMI "Enable HDMI display" OFF) 32 | option(TV "Enable TV composite output" OFF) 33 | option(SOFTTV "Enable TV soft composite output" OFF) 34 | if( ${PICO_PLATFORM} MATCHES "rp2350" ) 35 | option(m1p2launcher "Enable m1p2-launcher support" OFF) 36 | endif() 37 | 38 | #set(m1p2launcher ON) 39 | #set(I2S ON) 40 | #set(TFT ON) 41 | #set(ILI9341 ON) 42 | #set(HDMI ON) 43 | #set(TV ON) 44 | #set(SOFTTV ON) 45 | 46 | set(CMAKE_C_STANDARD 11) 47 | set(CMAKE_CXX_STANDARD 17) 48 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -funroll-loops -ffast-math -feliminate-unused-debug-types -ffunction-sections -fdata-sections -O2") 49 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funroll-loops -ffast-math -feliminate-unused-debug-types -ffunction-sections -fdata-sections -O2") 50 | 51 | pico_sdk_init() 52 | 53 | set(OUTPUT_DIR "${CMAKE_SOURCE_DIR}/bin/${PICO_PLATFORM}/${CMAKE_BUILD_TYPE}") 54 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${OUTPUT_DIR}") 55 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIR}") 56 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_DIR}") 57 | 58 | add_subdirectory(drivers/fatfs) 59 | add_subdirectory(drivers/sdcard) 60 | add_subdirectory(drivers/ps2kbd) 61 | add_subdirectory(drivers/nespad) 62 | add_subdirectory(drivers/audio) 63 | 64 | add_subdirectory(drivers/vga-nextgen) 65 | add_subdirectory(drivers/hdmi) 66 | add_subdirectory(drivers/st7789) 67 | add_subdirectory(drivers/tv) 68 | add_subdirectory(drivers/tv-software) 69 | 70 | add_subdirectory(drivers/graphics) 71 | 72 | add_executable(${PROJECT_NAME} 73 | ext/minigb_apu/minigb_apu.c 74 | src/main.cpp 75 | ) 76 | 77 | 78 | if( ${PICO_PLATFORM} MATCHES "rp2040" ) 79 | pico_define_boot_stage2(slower_boot2 ${PICO_DEFAULT_BOOT_STAGE2_FILE}) 80 | target_compile_definitions(slower_boot2 PRIVATE PICO_FLASH_SPI_CLKDIV=4) 81 | pico_set_boot_stage2(${PROJECT_NAME} slower_boot2) 82 | if ( ${PICO_BOARD} MATCHES "murmulator2") 83 | SET(BUILD_NAME "m2p1-${PROJECT_NAME}") 84 | else() 85 | SET(BUILD_NAME "m1p1-${PROJECT_NAME}") 86 | endif() 87 | else() 88 | if (m1p2launcher) 89 | pico_set_linker_script(${PROJECT_NAME} "${CMAKE_SOURCE_DIR}/memmap.ld") 90 | endif() 91 | if ( ${PICO_BOARD} MATCHES "murmulator2") 92 | SET(BUILD_NAME "m2p2-${PROJECT_NAME}") 93 | else() 94 | SET(BUILD_NAME "m1p2-${PROJECT_NAME}") 95 | endif() 96 | endif() 97 | 98 | if ( ${PICO_BOARD} MATCHES "murmulator2") 99 | pico_set_program_name(gameboy-color "Gameboy emulator by xrip for MURMULATOR 2.0 board") 100 | else() 101 | pico_set_program_name(gameboy-color "Gameboy emulator by xrip for MURMULATOR 1.x board") 102 | endif() 103 | pico_set_program_version(gameboy-color "3.0.3") 104 | 105 | target_include_directories(${PROJECT_NAME} PRIVATE 106 | src 107 | inc 108 | ext/minigb_apu 109 | ) 110 | 111 | IF(TFT) 112 | target_link_libraries(${PROJECT_NAME} PRIVATE st7789) 113 | target_compile_definitions(${PROJECT_NAME} PRIVATE TFT) 114 | SET(BUILD_NAME "${BUILD_NAME}-TFT") 115 | IF(ILI9341) 116 | SET(BUILD_NAME "${BUILD_NAME}-ILI9341") 117 | target_compile_definitions(${PROJECT_NAME} PRIVATE ILI9341) 118 | ELSE() 119 | SET(BUILD_NAME "${BUILD_NAME}-ST7789") 120 | ENDIF() 121 | ELSEIF(HDMI) 122 | target_link_libraries(${PROJECT_NAME} PRIVATE hdmi) 123 | target_compile_definitions(${PROJECT_NAME} PRIVATE HDMI) 124 | SET(BUILD_NAME "${BUILD_NAME}-HDMI") 125 | ELSEIF(TV) 126 | target_compile_definitions(${PROJECT_NAME} PRIVATE TV) 127 | target_link_libraries(${PROJECT_NAME} PRIVATE tv) 128 | SET(BUILD_NAME "${BUILD_NAME}-TV") 129 | ELSEIF(SOFTTV) 130 | target_compile_definitions(${PROJECT_NAME} PRIVATE SOFTTV) 131 | target_link_libraries(${PROJECT_NAME} PRIVATE tv-software) 132 | SET(BUILD_NAME "${BUILD_NAME}-TV-SOFT") 133 | ELSE() 134 | target_compile_definitions(${PROJECT_NAME} PRIVATE VGA) 135 | target_link_libraries(${PROJECT_NAME} PRIVATE vga-nextgen) 136 | SET(BUILD_NAME "${BUILD_NAME}-VGA") 137 | ENDIF() 138 | 139 | IF(NOT I2S) 140 | target_compile_definitions(${PROJECT_NAME} PRIVATE AUDIO_PWM) 141 | SET(BUILD_NAME "${BUILD_NAME}-PWM") 142 | ELSEIF(I2S_CS4334) 143 | target_compile_definitions(${PROJECT_NAME} PRIVATE I2S_CS4334) 144 | SET(BUILD_NAME "${BUILD_NAME}-I2S-CS4334") 145 | ELSE() 146 | SET(BUILD_NAME "${BUILD_NAME}-I2S-TDA1387") 147 | ENDIF() 148 | 149 | set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "${BUILD_NAME}") 150 | 151 | target_link_libraries(${PROJECT_NAME} PRIVATE 152 | audio 153 | graphics 154 | 155 | ps2kbd 156 | sdcard 157 | fatfs 158 | nespad 159 | 160 | pico_runtime 161 | pico_stdlib 162 | pico_stdio 163 | pico_bootrom 164 | pico_multicore 165 | pico_stdio 166 | pico_multicore 167 | hardware_pwm 168 | 169 | tinyusb_board 170 | tinyusb_device 171 | ) 172 | 173 | target_link_options(${PROJECT_NAME} PRIVATE -Xlinker --print-memory-usage --data-sections) 174 | pico_add_extra_outputs(${PROJECT_NAME}) 175 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ilya Maslennikov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi Pico GameBoy Emulator 2 | Raspberry Pi Pico GameBoy Emulator based on [Peanut-GB](https://github.com/deltabeard/Peanut-GB) for [MURMULATOR](https://github.com/AlexEkb4ever/MURMULATOR_classical_scheme) devboard 3 | 4 | Murmulator devboard have MicroSD card slot, PS/2 keyboard input and VGA output 5 | 6 | # Controls 7 | * UP, DOWN, LEFT, RIGHT - Arrow keys 8 | * A Button - Z key 9 | * B Button - X key 10 | * START - Enter key 11 | * SELECT - Backspace key 12 | 13 | SELECT+START in game menu 14 | 15 | # Hardware needed 16 | To get it working you should have an Murmulator (development) board with VGA output. Schematics available here at https://github.com/AlexEkb4ever/MURMULATOR_classical_scheme 17 | ![Murmulator Schematics](https://github.com/javavi/pico-infonesPlus/blob/main/assets/Murmulator-1_BSchem.JPG) 18 | 19 | # Flashing the firmware 20 | * Download gameboy.uf2 from the [releases page](https://github.com/xrip/pico-gameboy/releases) 21 | * Push and hold the BOOTSEL button on the Pico, then connect to your computer using a micro USB cable. Release BOOTSEL once the drive RPI-RP2 appears on your computer. 22 | * Drag and drop the UF2 file on to the RPI-RP2 drive. The Raspberry Pi Pico will reboot and will now run the emulator. 23 | 24 | # Preparing the SD card 25 | The SD card is used to store game roms and save game progress. For this project, you will need a FAT 32 formatted Micro SD card with roms you legally own. Roms must have the .gb extension. 26 | 27 | * Insert your MicroSD card in a Windows computer and format it as FAT 32 28 | * Copy your .gb files to the SD card root folder (subfolders are not supported at this time) 29 | * Insert the MicroSD card into the MicroSD card slot 30 | 31 | 32 | # Building from source 33 | The [Raspberry Pi Pico SDK](https://github.com/raspberrypi/pico-sdk) is required to build this project. Make sure you are able to compile an [example project](https://github.com/raspberrypi/pico-examples#first--examples) before continuing. 34 | ```bash 35 | cd ~ 36 | git clone https://github.com/xrip/pico-gameboy.git 37 | cd pico-gameboy 38 | mkdir build 39 | cd build 40 | cmake .. 41 | make -j4 42 | ``` 43 | 44 | 45 | # Known issues and limitations 46 | * No copyrighted games are included with Pico-GB / RP2040-GB. For this project, you will need a FAT 32 formatted Micro SD card with roms you legally own. Roms must have the .gb extension. 47 | * The RP2040-GB emulator is able to run at full speed on the Pico, at the expense of emulation accuracy. Some games may not work as expected or may not work at all. RP2040-GB is still experimental and not all features are guaranteed to work. 48 | * RP2040-GB is only compatible with [original Game Boy DMG games](https://en.wikipedia.org/wiki/List_of_Game_Boy_games) (not compatible with Game Boy Color or Game Boy Advance games) 49 | * Repeatedly flashing your Pico will eventually wear out the flash memory (Pico is qualified for min. 100K flash/erase cycles) 50 | * The emulator overclocks the Pico in order to get the emulator working fast enough. Overclocking can reduce the Pico’s lifespan. 51 | * Use this software and instructions at your own risk! I will not be responsible in any way for any damage to your Pico and/or connected peripherals caused by using this software. I also do not take responsibility in any way when damage is caused to the Pico or display due to incorrect wiring or voltages. 52 | 53 | # License 54 | MIT 55 | -------------------------------------------------------------------------------- /boards/murmulator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if PICO_RP2350 3 | #include "boards/pico2.h" 4 | 5 | #else 6 | #include "boards/pico.h" 7 | #endif 8 | 9 | // SDCARD 10 | #define SDCARD_PIN_SPI0_CS 5 11 | #define SDCARD_PIN_SPI0_SCK 2 12 | #define SDCARD_PIN_SPI0_MOSI 3 13 | #define SDCARD_PIN_SPI0_MISO 4 14 | 15 | // PS2KBD 16 | #define PS2KBD_GPIO_FIRST 0 17 | 18 | // NES Gamepad 19 | #define NES_GPIO_CLK 14 20 | #define NES_GPIO_DATA 16 21 | #define NES_GPIO_LAT 15 22 | 23 | // VGA 8 pins starts from pin: 24 | #define VGA_BASE_PIN 6 25 | 26 | // HDMI 8 pins starts from pin: 27 | #define HDMI_BASE_PIN 6 28 | 29 | // TFT 30 | #define TFT_CS_PIN 6 31 | #define TFT_RST_PIN 8 32 | #define TFT_LED_PIN 9 33 | #define TFT_DC_PIN 10 34 | #define TFT_DATA_PIN 12 35 | #define TFT_CLK_PIN 13 36 | 37 | 38 | // Sound 39 | #if defined(AUDIO_PWM) 40 | #define AUDIO_PWM_PIN 26 41 | #endif 42 | // I2S Sound 43 | #define AUDIO_DATA_PIN 26 44 | #define AUDIO_CLOCK_PIN 27 -------------------------------------------------------------------------------- /boards/murmulator2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if PICO_RP2350 3 | #include "boards/pico2.h" 4 | #else 5 | #include "boards/pico.h" 6 | #endif 7 | 8 | // 16MB flash 9 | #define PICO_FLASH_SIZE_BYTES 16777216 10 | // SDCARD 11 | #define SDCARD_PIN_SPI0_CS 5 12 | #define SDCARD_PIN_SPI0_SCK 6 13 | #define SDCARD_PIN_SPI0_MOSI 7 14 | #define SDCARD_PIN_SPI0_MISO 4 15 | 16 | // PS2KBD 17 | #define PS2KBD_GPIO_FIRST 2 18 | 19 | // NES Gamepad 20 | #define NES_GPIO_CLK 20 21 | #define NES_GPIO_DATA 26 22 | #define NES_GPIO_LAT 21 23 | 24 | // VGA 8 pins starts from pin: 25 | #define VGA_BASE_PIN 12 26 | 27 | // HDMI 8 pins starts from pin: 28 | #define HDMI_BASE_PIN 12 29 | 30 | // TFT 31 | #define TFT_CS_PIN 12 32 | #define TFT_RST_PIN 14 33 | #define TFT_LED_PIN 15 34 | #define TFT_DC_PIN 16 35 | #define TFT_DATA_PIN 18 36 | #define TFT_CLK_PIN 19 37 | 38 | 39 | // Sound 40 | #if defined(AUDIO_PWM) 41 | #define AUDIO_PWM_PIN 9 42 | #endif 43 | 44 | #define AUDIO_DATA_PIN 9 45 | #define AUDIO_CLOCK_PIN 10 -------------------------------------------------------------------------------- /drivers/audio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(audio INTERFACE) 2 | 3 | target_sources(audio INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/audio.c 5 | ${CMAKE_CURRENT_LIST_DIR}/audio.h 6 | ) 7 | 8 | target_link_libraries(audio INTERFACE hardware_pio hardware_clocks) 9 | 10 | target_include_directories(audio INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR} 12 | ) 13 | 14 | pico_generate_pio_header(audio 15 | ${CMAKE_CURRENT_LIST_DIR}/audio_i2s.pio 16 | ) 17 | -------------------------------------------------------------------------------- /drivers/audio/audio.c: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Vincent Mistler 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #define PWM_PIN0 (AUDIO_PWM_PIN&0xfe) 26 | #define PWM_PIN1 (PWM_PIN0+1) 27 | 28 | #include "audio.h" 29 | 30 | #ifdef AUDIO_PWM_PIN 31 | #include "hardware/pwm.h" 32 | #include "hardware/clocks.h" 33 | #endif 34 | 35 | /** 36 | * return the default i2s context used to store information about the setup 37 | */ 38 | i2s_config_t i2s_get_default_config(void) { 39 | i2s_config_t i2s_config = { 40 | .sample_freq = 44100, 41 | .channel_count = 2, 42 | .data_pin = AUDIO_DATA_PIN, 43 | .clock_pin_base = AUDIO_CLOCK_PIN, 44 | .pio = pio1, 45 | .sm = 0, 46 | .dma_channel = 0, 47 | .dma_buf = NULL, 48 | .dma_trans_count = 0, 49 | .volume = 0, 50 | }; 51 | 52 | return i2s_config; 53 | } 54 | 55 | /** 56 | * Initialize the I2S driver. Must be called before calling i2s_write or i2s_dma_write 57 | * i2s_config: I2S context obtained by i2s_get_default_config() 58 | */ 59 | 60 | 61 | void i2s_init(i2s_config_t *i2s_config) { 62 | 63 | 64 | #ifndef AUDIO_PWM_PIN 65 | 66 | uint8_t func=GPIO_FUNC_PIO1; // TODO: GPIO_FUNC_PIO0 for pio0 or GPIO_FUNC_PIO1 for pio1 67 | gpio_set_function(i2s_config->data_pin, func); 68 | gpio_set_function(i2s_config->clock_pin_base, func); 69 | gpio_set_function(i2s_config->clock_pin_base+1, func); 70 | 71 | i2s_config->sm = pio_claim_unused_sm(i2s_config->pio, true); 72 | 73 | /* Set PIO clock */ 74 | uint32_t system_clock_frequency = clock_get_hz(clk_sys); 75 | uint32_t divider = system_clock_frequency * 4 / i2s_config->sample_freq; // avoid arithmetic overflow 76 | 77 | #ifdef I2S_CS4334 78 | uint offset = pio_add_program(i2s_config->pio, &audio_i2s_cs4334_program); 79 | audio_i2s_cs4334_program_init(i2s_config->pio, i2s_config->sm , offset, i2s_config->data_pin , i2s_config->clock_pin_base); 80 | divider >>= 3; 81 | #else 82 | uint offset = pio_add_program(i2s_config->pio, &audio_i2s_program); 83 | audio_i2s_program_init(i2s_config->pio, i2s_config->sm , offset, i2s_config->data_pin , i2s_config->clock_pin_base); 84 | 85 | #endif 86 | 87 | pio_sm_set_clkdiv_int_frac(i2s_config->pio, i2s_config->sm , divider >> 8u, divider & 0xffu); 88 | 89 | pio_sm_set_enabled(i2s_config->pio, i2s_config->sm, false); 90 | #endif 91 | /* Allocate memory for the DMA buffer */ 92 | i2s_config->dma_buf=malloc(i2s_config->dma_trans_count*sizeof(uint32_t)); 93 | 94 | /* Direct Memory Access setup */ 95 | i2s_config->dma_channel = dma_claim_unused_channel(true); 96 | 97 | dma_channel_config dma_config = dma_channel_get_default_config(i2s_config->dma_channel); 98 | channel_config_set_read_increment(&dma_config, true); 99 | channel_config_set_write_increment(&dma_config, false); 100 | 101 | channel_config_set_transfer_data_size(&dma_config, DMA_SIZE_32); 102 | 103 | volatile uint32_t* addr_write_DMA=&(i2s_config->pio->txf[i2s_config->sm]); 104 | #ifdef AUDIO_PWM_PIN 105 | gpio_set_function(PWM_PIN0, GPIO_FUNC_PWM); 106 | gpio_set_function(PWM_PIN1, GPIO_FUNC_PWM); 107 | uint slice_num = pwm_gpio_to_slice_num(PWM_PIN0); 108 | 109 | 110 | 111 | pwm_config c_pwm=pwm_get_default_config(); 112 | pwm_config_set_clkdiv(&c_pwm,1.0); 113 | //pwm_config_set_wrap(&c_pwm,(1<<12)-1);//MAX PWM value 114 | pwm_config_set_wrap(&c_pwm,clock_get_hz(clk_sys)/(i2s_config->sample_freq));//MAX PWM value 115 | pwm_init(slice_num,&c_pwm,true); 116 | 117 | //Для синхронизации используем другой произвольный канал ШИМ 118 | 119 | 120 | channel_config_set_dreq(&dma_config, pwm_get_dreq(slice_num)); 121 | 122 | 123 | addr_write_DMA=(uint32_t*)&pwm_hw->slice[slice_num].cc; 124 | #else 125 | channel_config_set_dreq(&dma_config, pio_get_dreq(i2s_config->pio, i2s_config->sm, true)); 126 | #endif 127 | 128 | dma_channel_configure(i2s_config->dma_channel, 129 | &dma_config, 130 | addr_write_DMA, // Destination pointer 131 | i2s_config->dma_buf, // Source pointer 132 | i2s_config->dma_trans_count, // Number of 32 bits words to transfer 133 | false // Start immediately 134 | ); 135 | 136 | pio_sm_set_enabled(i2s_config->pio, i2s_config->sm , true); 137 | } 138 | 139 | /** 140 | * Write samples to I2S directly and wait for completion (blocking) 141 | * i2s_config: I2S context obtained by i2s_get_default_config() 142 | * sample: pointer to an array of len x 32 bits samples 143 | * Each 32 bits sample contains 2x16 bits samples, 144 | * one for the left channel and one for the right channel 145 | * len: length of sample in 32 bits words 146 | */ 147 | void i2s_write(const i2s_config_t *i2s_config,const int16_t *samples,const size_t len) { 148 | for(size_t i=0;ipio, i2s_config->sm, (uint32_t)samples[i]); 150 | } 151 | } 152 | 153 | /** 154 | * Write samples to DMA buffer and initiate DMA transfer (non blocking) 155 | * i2s_config: I2S context obtained by i2s_get_default_config() 156 | * sample: pointer to an array of dma_trans_count x 32 bits samples 157 | */ 158 | void i2s_dma_write(i2s_config_t *i2s_config,const int16_t *samples) { 159 | /* Wait the completion of the previous DMA transfer */ 160 | dma_channel_wait_for_finish_blocking(i2s_config->dma_channel); 161 | /* Copy samples into the DMA buffer */ 162 | 163 | #ifdef AUDIO_PWM_PIN 164 | for(uint16_t i=0;idma_trans_count*2;i++) { 165 | 166 | i2s_config->dma_buf[i] = (65536/2+(samples[i]))>>(4+i2s_config->volume); 167 | 168 | } 169 | #else 170 | 171 | if(i2s_config->volume==0) { 172 | memcpy(i2s_config->dma_buf,samples,i2s_config->dma_trans_count*sizeof(int32_t)); 173 | } else { 174 | for(uint16_t i=0;idma_trans_count*2;i++) { 175 | i2s_config->dma_buf[i] = samples[i]>>i2s_config->volume; 176 | } 177 | } 178 | #endif 179 | 180 | 181 | /* Initiate the DMA transfer */ 182 | dma_channel_transfer_from_buffer_now(i2s_config->dma_channel, 183 | i2s_config->dma_buf, 184 | i2s_config->dma_trans_count); 185 | } 186 | 187 | /** 188 | * Adjust the output volume 189 | * i2s_config: I2S context obtained by i2s_get_default_config() 190 | * volume: desired volume between 0 (highest. volume) and 16 (lowest volume) 191 | */ 192 | void i2s_volume(i2s_config_t *i2s_config,uint8_t volume) { 193 | if(volume>16) volume=16; 194 | i2s_config->volume=volume; 195 | } 196 | 197 | /** 198 | * Increases the output volume 199 | */ 200 | void i2s_increase_volume(i2s_config_t *i2s_config) { 201 | if(i2s_config->volume>0) { 202 | i2s_config->volume--; 203 | } 204 | } 205 | 206 | /** 207 | * Decreases the output volume 208 | */ 209 | void i2s_decrease_volume(i2s_config_t *i2s_config) { 210 | if(i2s_config->volume<16) { 211 | i2s_config->volume++; 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /drivers/audio/audio.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Vincent Mistler 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "audio_i2s.pio.h" 36 | 37 | typedef struct i2s_config 38 | { 39 | uint32_t sample_freq; 40 | uint16_t channel_count; 41 | uint8_t data_pin; 42 | uint8_t clock_pin_base; 43 | PIO pio; 44 | uint8_t sm; 45 | uint8_t dma_channel; 46 | uint16_t dma_trans_count; 47 | uint16_t *dma_buf; 48 | uint8_t volume; 49 | } i2s_config_t; 50 | 51 | 52 | i2s_config_t i2s_get_default_config(void); 53 | void i2s_init(i2s_config_t *i2s_config); 54 | void i2s_write(const i2s_config_t *i2s_config,const int16_t *samples,const size_t len); 55 | void i2s_dma_write(i2s_config_t *i2s_config,const int16_t *samples); 56 | void i2s_volume(i2s_config_t *i2s_config,uint8_t volume); 57 | void i2s_increase_volume(i2s_config_t *i2s_config); 58 | void i2s_decrease_volume(i2s_config_t *i2s_config); 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | -------------------------------------------------------------------------------- /drivers/audio/audio_i2s.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | ; Transmit a mono or stereo I2S audio stream as stereo 8 | ; This is 16 bits per sample; can be altered by modifying the "set" params, 9 | ; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register. 10 | ; 11 | ; Autopull must be enabled, with threshold set to 32. 12 | ; Since I2S is MSB-first, shift direction should be to left. 13 | ; Hence the format of the FIFO word is: 14 | ; 15 | ; | 31 : 16 | 15 : 0 | 16 | ; | sample ws=0 | sample ws=1 | 17 | ; 18 | ; Data is output at 1 bit per clock. Use clock divider to adjust frequency. 19 | ; Fractional divider will probably be needed to get correct bit clock period, 20 | ; but for common syslck freqs this should still give a constant word select period. 21 | ; 22 | ; One output pin is used for the data output. 23 | ; Two side-set pins are used. Bit 0 is clock, bit 1 is word select. 24 | 25 | ; Send 16 bit words to the PIO for mono, 32 bit words for stereo 26 | 27 | .program audio_i2s 28 | .side_set 2 29 | 30 | ; /--- LRCLK 31 | ; |/-- BCLK 32 | bitloop1: ; || 33 | out pins, 1 side 0b10 34 | jmp x-- bitloop1 side 0b11 35 | out pins, 1 side 0b00 36 | set x, 14 side 0b01 37 | 38 | bitloop0: 39 | out pins, 1 side 0b00 40 | jmp x-- bitloop0 side 0b01 41 | out pins, 1 side 0b10 42 | public entry_point: 43 | set x, 14 side 0b11 44 | 45 | .program audio_i2s_cs4334 46 | .side_set 2 47 | public entry_point: 48 | out pins, 31 side 0b11 49 | .wrap_target 50 | loop: 51 | set y,4 side 0b10 52 | set x,15 side 0b11 53 | l0: 54 | out pins, 1 side 0b10 55 | nop side 0b11 56 | l1: 57 | nop side 0b10 58 | jmp y--,l1 side 0b11 59 | set y,5 side 0b10 60 | jmp x--,l0 side 0b11 61 | 62 | set y,4 side 0b00 63 | set x,15 side 0b01 64 | 65 | l2: 66 | out pins, 1 side 0b00 67 | nop side 0b01 68 | l3: 69 | nop side 0b00 70 | jmp y--,l3 side 0b01 71 | set y,5 side 0b00 72 | 73 | jmp x--,l2 side 0b01 74 | 75 | % c-sdk { 76 | 77 | static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) { 78 | pio_sm_config sm_config = audio_i2s_program_get_default_config(offset); 79 | 80 | sm_config_set_out_pins(&sm_config, data_pin, 1); 81 | sm_config_set_sideset_pins(&sm_config, clock_pin_base); 82 | sm_config_set_out_shift(&sm_config, false, true, 32); 83 | 84 | pio_sm_init(pio, sm, offset, &sm_config); 85 | 86 | uint pin_mask = (1u << data_pin) | (3u << clock_pin_base); 87 | pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask); 88 | pio_sm_set_pins(pio, sm, 0); // clear pins 89 | 90 | pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point)); 91 | } 92 | 93 | 94 | static inline void audio_i2s_cs4334_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) { 95 | pio_sm_config sm_config = audio_i2s_cs4334_program_get_default_config(offset); 96 | 97 | sm_config_set_out_pins(&sm_config, data_pin, 1); 98 | sm_config_set_sideset_pins(&sm_config, clock_pin_base); 99 | sm_config_set_out_shift(&sm_config, false, true, 32); 100 | 101 | pio_sm_init(pio, sm, offset, &sm_config); 102 | 103 | uint pin_mask = (1u << data_pin) | (3u << clock_pin_base); 104 | pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask); 105 | pio_sm_set_pins(pio, sm, 0); // clear pins 106 | 107 | //pio_sm_exec(pio, sm, pio_encode_jmp(offset + 0)); 108 | } 109 | %} -------------------------------------------------------------------------------- /drivers/fatfs/00readme.txt: -------------------------------------------------------------------------------- 1 | FatFs Module Source Files R0.14b 2 | 3 | 4 | FILES 5 | 6 | 00readme.txt This file. 7 | 00history.txt Revision history. 8 | ff.c FatFs module. 9 | ffconf.h Configuration file of FatFs module. 10 | ff.h Common include file for FatFs and application module. 11 | diskio.h Common include file for FatFs and disk I/O module. 12 | diskio.c An example of glue function to attach existing disk I/O module to FatFs. 13 | ffunicode.c Optional Unicode utility functions. 14 | ffsystem.c An example of optional O/S related functions. 15 | 16 | 17 | Low level disk I/O module is not included in this archive because the FatFs 18 | module is only a generic file system layer and it does not depend on any specific 19 | storage device. You need to provide a low level disk I/O module written to 20 | control the storage device that attached to the target system. 21 | 22 | -------------------------------------------------------------------------------- /drivers/fatfs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(${CMAKE_CURRENT_LIST_DIR}/fatfs.cmake) -------------------------------------------------------------------------------- /drivers/fatfs/diskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface modlue include file (C)ChaN, 2019 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /* Status of Disk Functions */ 13 | typedef BYTE DSTATUS; 14 | 15 | /* Results of Disk Functions */ 16 | typedef enum { 17 | RES_OK = 0, /* 0: Successful */ 18 | RES_ERROR, /* 1: R/W Error */ 19 | RES_WRPRT, /* 2: Write Protected */ 20 | RES_NOTRDY, /* 3: Not Ready */ 21 | RES_PARERR /* 4: Invalid Parameter */ 22 | } DRESULT; 23 | 24 | 25 | /*---------------------------------------*/ 26 | /* Prototypes for disk control functions */ 27 | 28 | 29 | DSTATUS disk_initialize (BYTE pdrv); 30 | DSTATUS disk_status (BYTE pdrv); 31 | DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); 32 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count); 33 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); 34 | 35 | 36 | /* Disk Status Bits (DSTATUS) */ 37 | 38 | #define STA_NOINIT 0x01 /* Drive not initialized */ 39 | #define STA_NODISK 0x02 /* No medium in the drive */ 40 | #define STA_PROTECT 0x04 /* Write protected */ 41 | 42 | 43 | /* Command code for disk_ioctrl fucntion */ 44 | 45 | /* Generic command (Used by FatFs) */ 46 | #define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */ 47 | #define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */ 48 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */ 49 | #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */ 50 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ 51 | 52 | /* Generic command (Not used by FatFs) */ 53 | #define CTRL_POWER 5 /* Get/Set power status */ 54 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 55 | #define CTRL_EJECT 7 /* Eject media */ 56 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 57 | 58 | /* MMC/SDC specific ioctl command */ 59 | #define MMC_GET_TYPE 10 /* Get card type */ 60 | #define MMC_GET_CSD 11 /* Get CSD */ 61 | #define MMC_GET_CID 12 /* Get CID */ 62 | #define MMC_GET_OCR 13 /* Get OCR */ 63 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 64 | #define ISDIO_READ 55 /* Read data form SD iSDIO register */ 65 | #define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ 66 | #define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ 67 | 68 | /* ATA/CF specific ioctl command */ 69 | #define ATA_GET_REV 20 /* Get F/W revision */ 70 | #define ATA_GET_MODEL 21 /* Get model name */ 71 | #define ATA_GET_SN 22 /* Get serial number */ 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /drivers/fatfs/f_util.c: -------------------------------------------------------------------------------- 1 | /* f_util.c 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #include "ff.h" 15 | 16 | const char *FRESULT_str(FRESULT i) { 17 | switch (i) { 18 | case FR_OK: 19 | return "Succeeded"; 20 | case FR_DISK_ERR: 21 | return "A hard error occurred in the low level disk I/O layer"; 22 | case FR_INT_ERR: 23 | return "Assertion failed"; 24 | case FR_NOT_READY: 25 | return "The physical drive cannot work"; 26 | case FR_NO_FILE: 27 | return "Could not find the file"; 28 | case FR_NO_PATH: 29 | return "Could not find the path"; 30 | case FR_INVALID_NAME: 31 | return "The path name format is invalid"; 32 | case FR_DENIED: 33 | return "Access denied due to prohibited access or directory full"; 34 | case FR_EXIST: 35 | return "Access denied due to prohibited access (exists)"; 36 | case FR_INVALID_OBJECT: 37 | return "The file/directory object is invalid"; 38 | case FR_WRITE_PROTECTED: 39 | return "The physical drive is write protected"; 40 | case FR_INVALID_DRIVE: 41 | return "The logical drive number is invalid"; 42 | case FR_NOT_ENABLED: 43 | return "The volume has no work area (mount)"; 44 | case FR_NO_FILESYSTEM: 45 | return "There is no valid FAT volume"; 46 | case FR_MKFS_ABORTED: 47 | return "The f_mkfs() aborted due to any problem"; 48 | case FR_TIMEOUT: 49 | return "Could not get a grant to access the volume within defined " 50 | "period"; 51 | case FR_LOCKED: 52 | return "The operation is rejected according to the file sharing " 53 | "policy"; 54 | case FR_NOT_ENOUGH_CORE: 55 | return "LFN working buffer could not be allocated"; 56 | case FR_TOO_MANY_OPEN_FILES: 57 | return "Number of open files > FF_FS_LOCK"; 58 | case FR_INVALID_PARAMETER: 59 | return "Given parameter is invalid"; 60 | default: 61 | return "Unknown"; 62 | } 63 | } 64 | 65 | FRESULT delete_node ( 66 | TCHAR* path, /* Path name buffer with the sub-directory to delete */ 67 | UINT sz_buff, /* Size of path name buffer (items) */ 68 | FILINFO* fno /* Name read buffer */ 69 | ) 70 | { 71 | UINT i, j; 72 | FRESULT fr; 73 | DIR dir; 74 | 75 | 76 | fr = f_opendir(&dir, path); /* Open the sub-directory to make it empty */ 77 | if (fr != FR_OK) return fr; 78 | 79 | for (i = 0; path[i]; i++) ; /* Get current path length */ 80 | path[i++] = '/'; 81 | 82 | for (;;) { 83 | fr = f_readdir(&dir, fno); /* Get a directory item */ 84 | if (fr != FR_OK || !fno->fname[0]) break; /* End of directory? */ 85 | j = 0; 86 | do { /* Make a path name */ 87 | if (i + j >= sz_buff) { /* Buffer over flow? */ 88 | fr = 100; break; /* Fails with 100 when buffer overflow */ 89 | } 90 | path[i + j] = fno->fname[j]; 91 | } while (fno->fname[j++]); 92 | if (fno->fattrib & AM_DIR) { /* Item is a sub-directory */ 93 | fr = delete_node(path, sz_buff, fno); 94 | } else { /* Item is a file */ 95 | fr = f_unlink(path); 96 | } 97 | if (fr != FR_OK) break; 98 | } 99 | 100 | path[--i] = 0; /* Restore the path name */ 101 | f_closedir(&dir); 102 | 103 | if (fr == FR_OK) fr = f_unlink(path); /* Delete the empty sub-directory */ 104 | return fr; 105 | } 106 | -------------------------------------------------------------------------------- /drivers/fatfs/f_util.h: -------------------------------------------------------------------------------- 1 | /* f_util.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #pragma once 15 | #include "ff.h" 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | const char *FRESULT_str(FRESULT i); 22 | FRESULT delete_node ( 23 | TCHAR* path, /* Path name buffer with the sub-directory to delete */ 24 | UINT sz_buff, /* Size of path name buffer (items) */ 25 | FILINFO* fno /* Name read buffer */ 26 | ); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | -------------------------------------------------------------------------------- /drivers/fatfs/fatfs.cmake: -------------------------------------------------------------------------------- 1 | if (NOT TARGET fatfs) 2 | add_library(fatfs INTERFACE) 3 | 4 | target_sources(fatfs INTERFACE 5 | ${CMAKE_CURRENT_LIST_DIR}/ff.c 6 | ${CMAKE_CURRENT_LIST_DIR}/f_util.c 7 | ${CMAKE_CURRENT_LIST_DIR}/ffsystem.c 8 | ${CMAKE_CURRENT_LIST_DIR}/ffunicode.c 9 | ) 10 | 11 | target_link_libraries(fatfs INTERFACE pico_stdlib hardware_clocks hardware_spi) 12 | target_include_directories(fatfs INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 13 | 14 | endif () 15 | -------------------------------------------------------------------------------- /drivers/fatfs/ffconf.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------/ 2 | / FatFs Functional Configurations 3 | /---------------------------------------------------------------------------*/ 4 | 5 | #define FFCONF_DEF 86631 /* Revision ID */ 6 | 7 | /*---------------------------------------------------------------------------/ 8 | / Function Configurations 9 | /---------------------------------------------------------------------------*/ 10 | 11 | #define FF_FS_READONLY 0 12 | /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) 13 | / Read-only configuration removes writing API functions, f_write(), f_sync(), 14 | / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 15 | / and optional writing functions as well. */ 16 | 17 | 18 | #define FF_FS_MINIMIZE 0 19 | /* This option defines minimization level to remove some basic API functions. 20 | / 21 | / 0: Basic functions are fully enabled. 22 | / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() 23 | / are removed. 24 | / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. 25 | / 3: f_lseek() function is removed in addition to 2. */ 26 | 27 | 28 | #define FF_USE_FIND 0 29 | /* This option switches filtered directory read functions, f_findfirst() and 30 | / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ 31 | 32 | 33 | #define FF_USE_MKFS 0 34 | /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ 35 | 36 | 37 | #define FF_USE_FASTSEEK 1 38 | /* This option switches fast seek function. (0:Disable or 1:Enable) */ 39 | 40 | 41 | #define FF_USE_EXPAND 0 42 | /* This option switches f_expand function. (0:Disable or 1:Enable) */ 43 | 44 | 45 | #define FF_USE_CHMOD 0 46 | /* This option switches attribute manipulation functions, f_chmod() and f_utime(). 47 | / (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ 48 | 49 | 50 | #define FF_USE_LABEL 0 51 | /* This option switches volume label functions, f_getlabel() and f_setlabel(). 52 | / (0:Disable or 1:Enable) */ 53 | 54 | 55 | #define FF_USE_FORWARD 0 56 | /* This option switches f_forward() function. (0:Disable or 1:Enable) */ 57 | 58 | 59 | #define FF_USE_STRFUNC 0 60 | #define FF_PRINT_LLI 0 61 | #define FF_PRINT_FLOAT 0 62 | #define FF_STRF_ENCODE 3 63 | /* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and 64 | / f_printf(). 65 | / 66 | / 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. 67 | / 1: Enable without LF-CRLF conversion. 68 | / 2: Enable with LF-CRLF conversion. 69 | / 70 | / FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2 71 | makes f_printf() support floating point argument. These features want C99 or later. 72 | / When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character 73 | / encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE 74 | / to be read/written via those functions. 75 | / 76 | / 0: ANSI/OEM in current CP 77 | / 1: Unicode in UTF-16LE 78 | / 2: Unicode in UTF-16BE 79 | / 3: Unicode in UTF-8 80 | */ 81 | 82 | 83 | /*---------------------------------------------------------------------------/ 84 | / Locale and Namespace Configurations 85 | /---------------------------------------------------------------------------*/ 86 | 87 | #define FF_CODE_PAGE 866 88 | /* This option specifies the OEM code page to be used on the target system. 89 | / Incorrect code page setting can cause a file open failure. 90 | / 91 | / 437 - U.S. 92 | / 720 - Arabic 93 | / 737 - Greek 94 | / 771 - KBL 95 | / 775 - Baltic 96 | / 850 - Latin 1 97 | / 852 - Latin 2 98 | / 855 - Cyrillic 99 | / 857 - Turkish 100 | / 860 - Portuguese 101 | / 861 - Icelandic 102 | / 862 - Hebrew 103 | / 863 - Canadian French 104 | / 864 - Arabic 105 | / 865 - Nordic 106 | / 866 - Russian 107 | / 869 - Greek 2 108 | / 932 - Japanese (DBCS) 109 | / 936 - Simplified Chinese (DBCS) 110 | / 949 - Korean (DBCS) 111 | / 950 - Traditional Chinese (DBCS) 112 | / 0 - Include all code pages above and configured by f_setcp() 113 | */ 114 | 115 | 116 | #define FF_USE_LFN 1 117 | #define FF_MAX_LFN 255 118 | /* The FF_USE_LFN switches the support for LFN (long file name). 119 | / 120 | / 0: Disable LFN. FF_MAX_LFN has no effect. 121 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 122 | / 2: Enable LFN with dynamic working buffer on the STACK. 123 | / 3: Enable LFN with dynamic working buffer on the HEAP. 124 | / 125 | / To enable the LFN, ffunicode.c needs to be added to the project. The LFN function 126 | / requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and 127 | / additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. 128 | / The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can 129 | / be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN 130 | / specification. 131 | / When use stack for the working buffer, take care on stack overflow. When use heap 132 | / memory for the working buffer, memory management functions, ff_memalloc() and 133 | / ff_memfree() exemplified in ffsystem.c, need to be added to the project. */ 134 | 135 | 136 | #define FF_LFN_UNICODE 0 137 | /* This option switches the character encoding on the API when LFN is enabled. 138 | / 139 | / 0: ANSI/OEM in current CP (TCHAR = char) 140 | / 1: Unicode in UTF-16 (TCHAR = WCHAR) 141 | / 2: Unicode in UTF-8 (TCHAR = char) 142 | / 3: Unicode in UTF-32 (TCHAR = DWORD) 143 | / 144 | / Also behavior of string I/O functions will be affected by this option. 145 | / When LFN is not enabled, this option has no effect. */ 146 | 147 | 148 | #define FF_LFN_BUF 255 149 | #define FF_SFN_BUF 12 150 | /* This set of options defines size of file name members in the FILINFO structure 151 | / which is used to read out directory items. These values should be suffcient for 152 | / the file names to read. The maximum possible length of the read file name depends 153 | / on character encoding. When LFN is not enabled, these options have no effect. */ 154 | 155 | 156 | #define FF_FS_RPATH 0 157 | /* This option configures support for relative path. 158 | / 159 | / 0: Disable relative path and remove related functions. 160 | / 1: Enable relative path. f_chdir() and f_chdrive() are available. 161 | / 2: f_getcwd() function is available in addition to 1. 162 | */ 163 | 164 | 165 | /*---------------------------------------------------------------------------/ 166 | / Drive/Volume Configurations 167 | /---------------------------------------------------------------------------*/ 168 | 169 | // TODO: calulate really required volumes 170 | #define FF_VOLUMES 1 171 | /* Number of volumes (logical drives) to be used. (1-10) */ 172 | 173 | 174 | #define FF_STR_VOLUME_ID 0 175 | #define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" 176 | /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. 177 | / When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive 178 | / number in the path name. FF_VOLUME_STRS defines the volume ID strings for each 179 | / logical drives. Number of items must not be less than FF_VOLUMES. Valid 180 | / characters for the volume ID strings are A-Z, a-z and 0-9, however, they are 181 | / compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is 182 | / not defined, a user defined volume string table needs to be defined as: 183 | / 184 | / const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... 185 | */ 186 | 187 | 188 | #define FF_MULTI_PARTITION 0 189 | /* This option switches support for multiple volumes on the physical drive. 190 | / By default (0), each logical drive number is bound to the same physical drive 191 | / number and only an FAT volume found on the physical drive will be mounted. 192 | / When this function is enabled (1), each logical drive number can be bound to 193 | / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() 194 | / funciton will be available. */ 195 | 196 | 197 | #define FF_MIN_SS 512 198 | #define FF_MAX_SS 512 199 | /* This set of options configures the range of sector size to be supported. (512, 200 | / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and 201 | / harddisk, but a larger value may be required for on-board flash memory and some 202 | / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured 203 | / for variable sector size mode and disk_ioctl() function needs to implement 204 | / GET_SECTOR_SIZE command. */ 205 | 206 | 207 | #define FF_LBA64 0 208 | /* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) 209 | / To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ 210 | 211 | 212 | #define FF_MIN_GPT 0x10000000 213 | /* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and 214 | / f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */ 215 | 216 | 217 | #define FF_USE_TRIM 0 218 | /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) 219 | / To enable Trim function, also CTRL_TRIM command should be implemented to the 220 | / disk_ioctl() function. */ 221 | 222 | 223 | 224 | /*---------------------------------------------------------------------------/ 225 | / System Configurations 226 | /---------------------------------------------------------------------------*/ 227 | 228 | #define FF_FS_TINY 0 229 | /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) 230 | / At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. 231 | / Instead of private sector buffer eliminated from the file object, common sector 232 | / buffer in the filesystem object (FATFS) is used for the file data transfer. */ 233 | 234 | 235 | #define FF_FS_EXFAT 1 236 | /* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) 237 | / To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) 238 | / Note that enabling exFAT discards ANSI C (C89) compatibility. */ 239 | 240 | 241 | #define FF_FS_NORTC 1 242 | #define FF_NORTC_MON 1 243 | #define FF_NORTC_MDAY 1 244 | #define FF_NORTC_YEAR 2024 245 | /* The option FF_FS_NORTC switches timestamp function. If the system does not have 246 | / any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable 247 | / the timestamp function. Every object modified by FatFs will have a fixed timestamp 248 | / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. 249 | / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be 250 | / added to the project to read current time form real-time clock. FF_NORTC_MON, 251 | / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. 252 | / These options have no effect in read-only configuration (FF_FS_READONLY = 1). */ 253 | 254 | 255 | #define FF_FS_NOFSINFO 0 256 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 257 | / option, and f_getfree() function at first time after volume mount will force 258 | / a full FAT scan. Bit 1 controls the use of last allocated cluster number. 259 | / 260 | / bit0=0: Use free cluster count in the FSINFO if available. 261 | / bit0=1: Do not trust free cluster count in the FSINFO. 262 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 263 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 264 | */ 265 | 266 | 267 | #define FF_FS_LOCK 0 268 | /* The option FF_FS_LOCK switches file lock function to control duplicated file open 269 | / and illegal operation to open objects. This option must be 0 when FF_FS_READONLY 270 | / is 1. 271 | / 272 | / 0: Disable file lock function. To avoid volume corruption, application program 273 | / should avoid illegal open, remove and rename to the open objects. 274 | / >0: Enable file lock function. The value defines how many files/sub-directories 275 | / can be opened simultaneously under file lock control. Note that the file 276 | / lock control is independent of re-entrancy. */ 277 | 278 | 279 | /* #include // O/S definitions */ 280 | #define FF_FS_REENTRANT 0 281 | #define FF_FS_TIMEOUT 1000 282 | #define FF_SYNC_t HANDLE 283 | /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs 284 | / module itself. Note that regardless of this option, file access to different 285 | / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() 286 | / and f_fdisk() function, are always not re-entrant. Only file/directory access 287 | / to the same volume is under control of this function. 288 | / 289 | / 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. 290 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 291 | / ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() 292 | / function, must be added to the project. Samples are available in 293 | / option/syscall.c. 294 | / 295 | / The FF_FS_TIMEOUT defines timeout period in unit of time tick. 296 | / The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, 297 | / SemaphoreHandle_t and etc. A header file for O/S definitions needs to be 298 | / included somewhere in the scope of ff.h. */ 299 | 300 | 301 | 302 | /*--- End of configuration options ---*/ 303 | -------------------------------------------------------------------------------- /drivers/fatfs/ffsystem.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Sample Code of OS Dependent Functions for FatFs */ 3 | /* (C)ChaN, 2018 */ 4 | /*------------------------------------------------------------------------*/ 5 | 6 | 7 | #include "ff.h" 8 | 9 | 10 | #if FF_USE_LFN == 3 /* Dynamic memory allocation */ 11 | 12 | /*------------------------------------------------------------------------*/ 13 | /* Allocate a memory block */ 14 | /*------------------------------------------------------------------------*/ 15 | 16 | void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */ 17 | UINT msize /* Number of bytes to allocate */ 18 | ) 19 | { 20 | return malloc(msize); /* Allocate a new memory block with POSIX API */ 21 | } 22 | 23 | 24 | /*------------------------------------------------------------------------*/ 25 | /* Free a memory block */ 26 | /*------------------------------------------------------------------------*/ 27 | 28 | void ff_memfree ( 29 | void* mblock /* Pointer to the memory block to free (nothing to do if null) */ 30 | ) 31 | { 32 | free(mblock); /* Free the memory block with POSIX API */ 33 | } 34 | 35 | #endif 36 | 37 | 38 | 39 | #if FF_FS_REENTRANT /* Mutal exclusion */ 40 | 41 | /*------------------------------------------------------------------------*/ 42 | /* Create a Synchronization Object */ 43 | /*------------------------------------------------------------------------*/ 44 | /* This function is called in f_mount() function to create a new 45 | / synchronization object for the volume, such as semaphore and mutex. 46 | / When a 0 is returned, the f_mount() function fails with FR_INT_ERR. 47 | */ 48 | 49 | //const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */ 50 | 51 | 52 | int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */ 53 | BYTE vol, /* Corresponding volume (logical drive number) */ 54 | FF_SYNC_t* sobj /* Pointer to return the created sync object */ 55 | ) 56 | { 57 | /* Win32 */ 58 | *sobj = CreateMutex(NULL, FALSE, NULL); 59 | return (int)(*sobj != INVALID_HANDLE_VALUE); 60 | 61 | /* uITRON */ 62 | // T_CSEM csem = {TA_TPRI,1,1}; 63 | // *sobj = acre_sem(&csem); 64 | // return (int)(*sobj > 0); 65 | 66 | /* uC/OS-II */ 67 | // OS_ERR err; 68 | // *sobj = OSMutexCreate(0, &err); 69 | // return (int)(err == OS_NO_ERR); 70 | 71 | /* FreeRTOS */ 72 | // *sobj = xSemaphoreCreateMutex(); 73 | // return (int)(*sobj != NULL); 74 | 75 | /* CMSIS-RTOS */ 76 | // *sobj = osMutexCreate(&Mutex[vol]); 77 | // return (int)(*sobj != NULL); 78 | } 79 | 80 | 81 | /*------------------------------------------------------------------------*/ 82 | /* Delete a Synchronization Object */ 83 | /*------------------------------------------------------------------------*/ 84 | /* This function is called in f_mount() function to delete a synchronization 85 | / object that created with ff_cre_syncobj() function. When a 0 is returned, 86 | / the f_mount() function fails with FR_INT_ERR. 87 | */ 88 | 89 | int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */ 90 | FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ 91 | ) 92 | { 93 | /* Win32 */ 94 | return (int)CloseHandle(sobj); 95 | 96 | /* uITRON */ 97 | // return (int)(del_sem(sobj) == E_OK); 98 | 99 | /* uC/OS-II */ 100 | // OS_ERR err; 101 | // OSMutexDel(sobj, OS_DEL_ALWAYS, &err); 102 | // return (int)(err == OS_NO_ERR); 103 | 104 | /* FreeRTOS */ 105 | // vSemaphoreDelete(sobj); 106 | // return 1; 107 | 108 | /* CMSIS-RTOS */ 109 | // return (int)(osMutexDelete(sobj) == osOK); 110 | } 111 | 112 | 113 | /*------------------------------------------------------------------------*/ 114 | /* Request Grant to Access the Volume */ 115 | /*------------------------------------------------------------------------*/ 116 | /* This function is called on entering file functions to lock the volume. 117 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 118 | */ 119 | 120 | int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ 121 | FF_SYNC_t sobj /* Sync object to wait */ 122 | ) 123 | { 124 | /* Win32 */ 125 | return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0); 126 | 127 | /* uITRON */ 128 | // return (int)(wai_sem(sobj) == E_OK); 129 | 130 | /* uC/OS-II */ 131 | // OS_ERR err; 132 | // OSMutexPend(sobj, FF_FS_TIMEOUT, &err)); 133 | // return (int)(err == OS_NO_ERR); 134 | 135 | /* FreeRTOS */ 136 | // return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE); 137 | 138 | /* CMSIS-RTOS */ 139 | // return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK); 140 | } 141 | 142 | 143 | /*------------------------------------------------------------------------*/ 144 | /* Release Grant to Access the Volume */ 145 | /*------------------------------------------------------------------------*/ 146 | /* This function is called on leaving file functions to unlock the volume. 147 | */ 148 | 149 | void ff_rel_grant ( 150 | FF_SYNC_t sobj /* Sync object to be signaled */ 151 | ) 152 | { 153 | /* Win32 */ 154 | ReleaseMutex(sobj); 155 | 156 | /* uITRON */ 157 | // sig_sem(sobj); 158 | 159 | /* uC/OS-II */ 160 | // OSMutexPost(sobj); 161 | 162 | /* FreeRTOS */ 163 | // xSemaphoreGive(sobj); 164 | 165 | /* CMSIS-RTOS */ 166 | // osMutexRelease(sobj); 167 | } 168 | 169 | #endif 170 | 171 | -------------------------------------------------------------------------------- /drivers/graphics/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(graphics INTERFACE) 2 | 3 | target_sources(graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR}/graphics.c) 4 | 5 | #target_link_libraries(graphics INTERFACE 6 | #vga-nextgen 7 | #hdmi 8 | #tft 9 | #) 10 | 11 | target_include_directories(graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 12 | 13 | #pico_generate_pio_header(st7789 14 | # ${CMAKE_CURRENT_LIST_DIR}/st7789.pio 15 | #) 16 | -------------------------------------------------------------------------------- /drivers/graphics/graphics.c: -------------------------------------------------------------------------------- 1 | #include "graphics.h" 2 | #include 3 | 4 | void draw_text(const char string[TEXTMODE_COLS + 1], uint32_t x, uint32_t y, uint8_t color, uint8_t bgcolor) { 5 | uint8_t* t_buf = text_buffer + TEXTMODE_COLS * 2 * y + 2 * x; 6 | for (int xi = TEXTMODE_COLS * 2; xi--;) { 7 | if (!*string) break; 8 | *t_buf++ = *string++; 9 | *t_buf++ = bgcolor << 4 | color & 0xF; 10 | } 11 | } 12 | 13 | void draw_window(const char title[TEXTMODE_COLS + 1], uint32_t x, uint32_t y, uint32_t width, uint32_t height) { 14 | char line[width + 1]; 15 | memset(line, 0, sizeof line); 16 | width--; 17 | height--; 18 | // Рисуем рамки 19 | 20 | memset(line, 0xCD, width); // ═══ 21 | 22 | 23 | line[0] = 0xC9; // ╔ 24 | line[width] = 0xBB; // ╗ 25 | draw_text(line, x, y, 11, 1); 26 | 27 | line[0] = 0xC8; // ╚ 28 | line[width] = 0xBC; // ╝ 29 | draw_text(line, x, height + y, 11, 1); 30 | 31 | memset(line, ' ', width); 32 | line[0] = line[width] = 0xBA; 33 | 34 | for (int i = 1; i < height; i++) { 35 | draw_text(line, x, y + i, 11, 1); 36 | } 37 | 38 | snprintf(line, width - 1, " %s ", title); 39 | draw_text(line, x + (width - strlen(line)) / 2, y, 14, 3); 40 | } 41 | -------------------------------------------------------------------------------- /drivers/graphics/graphics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef __cplusplus 3 | extern "C" { 4 | #endif 5 | 6 | #include "stdbool.h" 7 | #include "stdio.h" 8 | #include "stdint.h" 9 | 10 | #ifdef TFT 11 | #include "st7789.h" 12 | #endif 13 | #ifdef HDMI 14 | #include "hdmi.h" 15 | #endif 16 | #ifdef VGA 17 | #include "vga.h" 18 | #endif 19 | #ifdef TV 20 | #include "tv.h" 21 | #endif 22 | #ifdef SOFTTV 23 | #include "tv-software.h" 24 | #endif 25 | 26 | 27 | #include "font6x8.h" 28 | #include "font8x8.h" 29 | #include "font8x16.h" 30 | 31 | enum graphics_mode_t { 32 | TEXTMODE_DEFAULT, 33 | GRAPHICSMODE_DEFAULT, 34 | 35 | TEXTMODE_53x30, 36 | 37 | TEXTMODE_160x100, 38 | 39 | CGA_160x200x16, 40 | CGA_320x200x4, 41 | CGA_640x200x2, 42 | 43 | TGA_320x200x16, 44 | EGA_320x200x16x4, 45 | VGA_320x240x256, 46 | VGA_320x200x256x4, 47 | // planar VGA 48 | }; 49 | 50 | // Буффер текстового режима 51 | extern uint8_t* text_buffer; 52 | 53 | void graphics_init(); 54 | 55 | void graphics_set_mode(enum graphics_mode_t mode); 56 | 57 | void graphics_set_buffer(uint8_t* buffer, uint16_t width, uint16_t height); 58 | 59 | void graphics_set_offset(int x, int y); 60 | 61 | void graphics_set_palette(uint8_t i, uint32_t color); 62 | 63 | void graphics_set_textbuffer(uint8_t* buffer); 64 | 65 | void graphics_set_bgcolor(uint32_t color888); 66 | 67 | void graphics_set_flashmode(bool flash_line, bool flash_frame); 68 | 69 | void draw_text(const char string[TEXTMODE_COLS + 1], uint32_t x, uint32_t y, uint8_t color, uint8_t bgcolor); 70 | void draw_window(const char title[TEXTMODE_COLS + 1], uint32_t x, uint32_t y, uint32_t width, uint32_t height); 71 | 72 | void clrScr(uint8_t color); 73 | 74 | #ifdef __cplusplus 75 | } 76 | #endif 77 | -------------------------------------------------------------------------------- /drivers/hdmi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(hdmi INTERFACE) 2 | 3 | target_sources(hdmi INTERFACE ${CMAKE_CURRENT_LIST_DIR}/hdmi.c) 4 | 5 | target_link_libraries(hdmi INTERFACE hardware_pio hardware_clocks hardware_dma) 6 | 7 | target_include_directories(hdmi INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR} 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /drivers/hdmi/hdmi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include "inttypes.h" 8 | #include "stdbool.h" 9 | 10 | #include "hardware/pio.h" 11 | 12 | #define PIO_VIDEO pio0 13 | #define PIO_VIDEO_ADDR pio0 14 | #define VIDEO_DMA_IRQ (DMA_IRQ_0) 15 | 16 | #ifndef HDMI_BASE_PIN 17 | #define HDMI_BASE_PIN (6) 18 | #endif 19 | 20 | #define HDMI_PIN_invert_diffpairs (1) 21 | #define HDMI_PIN_RGB_notBGR (1) 22 | #define beginHDMI_PIN_data (HDMI_BASE_PIN+2) 23 | #define beginHDMI_PIN_clk (HDMI_BASE_PIN) 24 | 25 | #define TEXTMODE_COLS 53 26 | #define TEXTMODE_ROWS 30 27 | 28 | #define RGB888(r, g, b) ((r<<16) | (g << 8 ) | b ) 29 | 30 | // TODO: Сделать настраиваемо 31 | static const uint8_t textmode_palette[16] = { 32 | 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215 33 | }; 34 | 35 | 36 | static void graphics_set_flashmode(bool flash_line, bool flash_frame) { 37 | // dummy 38 | } 39 | 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | -------------------------------------------------------------------------------- /drivers/nespad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(nespad INTERFACE) 2 | 3 | target_sources(nespad INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/nespad.cpp 5 | ${CMAKE_CURRENT_LIST_DIR}/nespad.h 6 | ) 7 | 8 | target_link_libraries(nespad INTERFACE hardware_pio) 9 | 10 | target_include_directories(nespad INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR} 12 | ) 13 | -------------------------------------------------------------------------------- /drivers/nespad/nespad.cpp: -------------------------------------------------------------------------------- 1 | #include "hardware/pio.h" 2 | 3 | #define nespad_wrap_target 0 4 | #define nespad_wrap 6 5 | 6 | static const uint16_t nespad_program_instructions[] = { 7 | // .wrap_target 8 | 0x80a0, // 0: pull block 9 | 0xea01, // 1: set pins, 1 side 0 [10] 10 | 0xe02f, // 2: set x, 15 side 0 11 | 0xe000, // 3: set pins, 0 side 0 12 | 0x4402, // 4: in pins, 2 side 0 [4] <--- 2 13 | 0xf500, // 5: set pins, 0 side 1 [5] 14 | 0x0044, // 6: jmp x--, 4 side 0 15 | // .wrap 16 | }; 17 | 18 | static const struct pio_program nespad_program = { 19 | .instructions = nespad_program_instructions, 20 | .length = 7, 21 | .origin = -1, 22 | }; 23 | 24 | static inline pio_sm_config nespad_program_get_default_config(uint offset) { 25 | pio_sm_config c = pio_get_default_sm_config(); 26 | sm_config_set_wrap(&c, offset + nespad_wrap_target, offset + nespad_wrap); 27 | sm_config_set_sideset(&c, 1, false, false); 28 | return c; 29 | } 30 | 31 | static PIO pio = pio1; 32 | static uint8_t sm = -1; 33 | uint32_t nespad_state = 0; // Joystick 1 34 | uint32_t nespad_state2 = 0; // Joystick 2 35 | 36 | bool nespad_begin(uint32_t cpu_khz, uint8_t clkPin, uint8_t dataPin,uint8_t latPin) { 37 | if (pio_can_add_program(pio, &nespad_program) && 38 | ((sm = pio_claim_unused_sm(pio, true)) >= 0)) { 39 | uint offset = pio_add_program(pio, &nespad_program); 40 | pio_sm_config c = nespad_program_get_default_config(offset); 41 | 42 | sm_config_set_sideset_pins(&c, clkPin); 43 | sm_config_set_in_pins(&c, dataPin); 44 | sm_config_set_set_pins(&c, latPin, 1); 45 | pio_gpio_init(pio, clkPin); 46 | pio_gpio_init(pio, dataPin); 47 | pio_gpio_init(pio, dataPin+1); // +1 Pin for Joystick2 48 | pio_gpio_init(pio, latPin); 49 | gpio_set_pulls(dataPin, true, false); // Pull data high, 0xFF if unplugged 50 | gpio_set_pulls(dataPin+1, true, false); // Pull data high, 0xFF if unplugged for Joystick2 51 | 52 | pio_sm_set_pindirs_with_mask(pio, sm, 53 | (1 << clkPin) | (1 << latPin), // Outputs 54 | (1 << clkPin) | (1 << latPin) | 55 | (1 << dataPin) | (1 << (dataPin+1)) 56 | ); // All pins 57 | sm_config_set_in_shift(&c, true, true, 32); // R shift, autopush @ 8 bits (@ 16 bits for 2 Joystick) 58 | 59 | sm_config_set_clkdiv_int_frac(&c, cpu_khz / 1000, 0); // 1 MHz clock 60 | 61 | 62 | 63 | pio_sm_clear_fifos(pio, sm); 64 | 65 | pio_sm_init(pio, sm, offset, &c); 66 | pio_sm_set_enabled(pio, sm, true); 67 | pio->txf[sm]=0; 68 | return true; // Success 69 | } 70 | return false; 71 | } 72 | 73 | 74 | 75 | // nespad read. Ideally should be called ~100 uS after 76 | // nespad_read_start(), but can be sooner (will block until ready), or later 77 | // (will introduce latency). Sets value of global nespad_state variable, a 78 | // bitmask of button/D-pad state (1 = pressed). 0x80=Right, 0x40=Left, 79 | // 0x20=Down, 0x10=Up, 0x08=Start, 0x04=Select, 0x02=B, 0x01=A. Must first 80 | // call nespad_begin() once to set up PIO. Result will be 0 if PIO failed to 81 | // init (e.g. no free state machine). 82 | 83 | void nespad_read() 84 | { 85 | if (sm<0) return; 86 | if (pio_sm_is_rx_fifo_empty(pio, sm)) return; 87 | 88 | // Right-shift was used in sm config so bit order matches NES controller 89 | // bits used elsewhere in picones, but does require shifting down... 90 | uint32_t temp=pio->rxf[sm]^ 0xFFFFFFFF; 91 | pio->txf[sm]=0; 92 | nespad_state = temp & 0x555555; // Joy1 93 | nespad_state2 = temp >> 1 & 0x555555; // Joy2 94 | } 95 | 96 | 97 | -------------------------------------------------------------------------------- /drivers/nespad/nespad.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DPAD_LEFT 0x001000 4 | #define DPAD_RIGHT 0x004000 5 | #define DPAD_DOWN 0x000400 6 | #define DPAD_UP 0x000100 7 | #define DPAD_START 0x000040 8 | #define DPAD_SELECT 0x000010 9 | #define DPAD_B 0x000004 //Y on SNES 10 | #define DPAD_A 0x000001 //B on SNES 11 | 12 | #define DPAD_Y 0x010000 //A on SNES 13 | #define DPAD_X 0x040000 14 | #define DPAD_LT 0x100000 15 | #define DPAD_RT 0x400000 16 | 17 | extern uint32_t nespad_state; // (S)NES Joystick1 18 | extern uint32_t nespad_state2; // (S)NES Joystick2 19 | 20 | extern bool nespad_begin(uint32_t cpu_khz, uint8_t clkPin, uint8_t dataPin, 21 | uint8_t latPin); 22 | 23 | 24 | extern void nespad_read(); -------------------------------------------------------------------------------- /drivers/ps2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ps2 INTERFACE) 2 | 3 | target_sources(ps2 INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/ps2.c 5 | ${CMAKE_CURRENT_LIST_DIR}/ps2.h 6 | ) 7 | 8 | target_link_libraries(ps2 INTERFACE hardware_pio hardware_clocks) 9 | 10 | target_include_directories(ps2 INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR} 12 | ) 13 | 14 | 15 | #pico_generate_pio_header(ps2 16 | # ${CMAKE_CURRENT_LIST_DIR}/ps2_mrmltr.pio 17 | #) 18 | -------------------------------------------------------------------------------- /drivers/ps2/ps2.c: -------------------------------------------------------------------------------- 1 | #include "ps2.h" 2 | #include 3 | #include 4 | #include "string.h" 5 | #include "hardware/irq.h" 6 | 7 | 8 | volatile int bitcount; 9 | static uint8_t ps2bufsize = 0; 10 | uint8_t ps2buffer[KBD_BUFFER_SIZE]; 11 | uint8_t kbloop = 0; 12 | 13 | uint8_t led_status = 0b000; 14 | 15 | #define PS2_ERR_NONE 0 16 | 17 | volatile int16_t ps2_error = PS2_ERR_NONE; 18 | 19 | void ps2poll(); 20 | 21 | static void clock_lo(void) { 22 | gpio_set_dir(KBD_CLOCK_PIN, GPIO_OUT); 23 | gpio_put(KBD_CLOCK_PIN, 0); 24 | } 25 | 26 | static inline void clock_hi(void) { 27 | gpio_set_dir(KBD_CLOCK_PIN, GPIO_OUT); 28 | gpio_put(KBD_CLOCK_PIN, 1); 29 | } 30 | 31 | static bool clock_in(void) { 32 | gpio_set_dir(KBD_CLOCK_PIN, GPIO_IN); 33 | asm("nop"); 34 | return gpio_get(KBD_CLOCK_PIN); 35 | } 36 | 37 | static void data_lo(void) { 38 | gpio_set_dir(KBD_DATA_PIN, GPIO_OUT); 39 | gpio_put(KBD_DATA_PIN, 0); 40 | } 41 | 42 | static void data_hi(void) { 43 | gpio_set_dir(KBD_DATA_PIN, GPIO_OUT); 44 | gpio_put(KBD_DATA_PIN, 1); 45 | } 46 | 47 | static inline bool data_in(void) { 48 | gpio_set_dir(KBD_DATA_PIN, GPIO_IN); 49 | asm("nop"); 50 | return gpio_get(KBD_DATA_PIN); 51 | } 52 | 53 | static void inhibit(void) { 54 | clock_lo(); 55 | data_hi(); 56 | } 57 | 58 | static void idle(void) { 59 | clock_hi(); 60 | data_hi(); 61 | } 62 | 63 | #define wait_us(us) busy_wait_us_32(us) 64 | #define wait_ms(ms) busy_wait_ms(ms) 65 | 66 | static inline uint16_t wait_clock_lo(uint16_t us) { 67 | while (clock_in() && us) { 68 | asm(""); 69 | wait_us(1); 70 | us--; 71 | } 72 | return us; 73 | } 74 | 75 | static inline uint16_t wait_clock_hi(uint16_t us) { 76 | while (!clock_in() && us) { 77 | asm(""); 78 | wait_us(1); 79 | us--; 80 | } 81 | return us; 82 | } 83 | 84 | static inline uint16_t wait_data_lo(uint16_t us) { 85 | while (data_in() && us) { 86 | asm(""); 87 | wait_us(1); 88 | us--; 89 | } 90 | return us; 91 | } 92 | 93 | static inline uint16_t wait_data_hi(uint16_t us) { 94 | while (!data_in() && us) { 95 | asm(""); 96 | wait_us(1); 97 | us--; 98 | } 99 | return us; 100 | } 101 | 102 | #define WAIT(stat, us, err) do { \ 103 | if (!wait_##stat(us)) { \ 104 | ps2_error = err; \ 105 | goto ERROR; \ 106 | } \ 107 | } while (0) 108 | 109 | static void int_on(void) { 110 | gpio_set_dir(KBD_CLOCK_PIN, GPIO_IN); 111 | gpio_set_dir(KBD_DATA_PIN, GPIO_IN); 112 | gpio_set_irq_enabled(KBD_CLOCK_PIN, GPIO_IRQ_EDGE_FALL, true); 113 | } 114 | 115 | static void int_off(void) { 116 | gpio_set_irq_enabled(KBD_CLOCK_PIN, GPIO_IRQ_EDGE_FALL, false); 117 | } 118 | 119 | static int16_t ps2_recv_response(void) { 120 | // Command may take 25ms/20ms at most([5]p.46, [3]p.21) 121 | uint8_t retry = 25; 122 | int16_t c = -1; 123 | while (retry-- && (c = ps2buffer[ps2bufsize]) == -1) { 124 | wait_ms(1); 125 | } 126 | return c; 127 | } 128 | 129 | int16_t keyboard_send(uint8_t data) { 130 | bool parity = true; 131 | ps2_error = PS2_ERR_NONE; 132 | 133 | //printf("KBD set s%02X \r\n", data); 134 | 135 | int_off(); 136 | 137 | /* terminate a transmission if we have */ 138 | inhibit(); 139 | wait_us(200); 140 | 141 | /* 'Request to Send' and Start bit */ 142 | data_lo(); 143 | wait_us(200); 144 | clock_hi(); 145 | WAIT(clock_lo, 15000, 1); // 10ms [5]p.50 146 | 147 | /* Data bit[2-9] */ 148 | for (uint8_t i = 0; i < 8; i++) { 149 | wait_us(15); 150 | if (data & (1 << i)) { 151 | parity = !parity; 152 | data_hi(); 153 | } 154 | else { 155 | data_lo(); 156 | } 157 | WAIT(clock_hi, 100, (int16_t) (2 + i * 0x10)); 158 | WAIT(clock_lo, 100, (int16_t) (3 + i * 0x10)); 159 | } 160 | 161 | /* Parity bit */ 162 | wait_us(15); 163 | if (parity) { data_hi(); } 164 | else { data_lo(); } 165 | WAIT(clock_hi, 100, 4); 166 | WAIT(clock_lo, 100, 5); 167 | 168 | /* Stop bit */ 169 | wait_us(15); 170 | data_hi(); 171 | 172 | /* Ack */ 173 | WAIT(data_lo, 100, 6); // check Ack 174 | WAIT(data_hi, 100, 7); 175 | WAIT(clock_hi, 100, 8); 176 | 177 | memset(ps2buffer, 0x00, sizeof ps2buffer); 178 | //ringbuf_reset(&rbuf); // clear buffer 179 | idle(); 180 | int_on(); 181 | return ps2_recv_response(); 182 | ERROR: 183 | printf("KBD error %02X \r\n", ps2_error); 184 | ps2_error = 0; 185 | idle(); 186 | int_on(); 187 | return -0xf; 188 | } 189 | 190 | void keyboard_toggle_led(uint8_t led) { 191 | led_status ^= led; 192 | 193 | keyboard_send(0xED); 194 | busy_wait_ms(50); 195 | keyboard_send(led_status); 196 | } 197 | 198 | uint8_t ps2_to_xt_1(uint32_t val) { 199 | uint8_t i; 200 | for (i = 0; i < 85; i++) { 201 | if (ps2_group1[i].make == val) return ps2_group1[i].xt_make; 202 | } 203 | return 0; 204 | } 205 | 206 | uint8_t ps2_to_xt_2(uint32_t val) { 207 | uint8_t i; 208 | for (i = 0; i < 16; i++) { 209 | if (ps2_group2[i].xt_make == val) return ps2_group2[i].make; 210 | } 211 | return 0; 212 | } 213 | 214 | uint32_t ps2getcode() { 215 | uint32_t retval, i, len; 216 | if (!ps2bufsize) return 0; 217 | switch (ps2buffer[0]) { 218 | case 0xF0: 219 | case 0xE0: 220 | case 0xE1: 221 | len = 2; 222 | break; 223 | default: 224 | len = 1; 225 | break; 226 | } 227 | if (ps2bufsize < len) return 0; 228 | if (ps2buffer[0] == 0xE0) { 229 | if (ps2buffer[1] == 0xF0) len = 3; 230 | } 231 | if (ps2bufsize < len) return 0; 232 | retval = 0; 233 | 234 | //translate code 235 | if (len == 1) { 236 | retval = ps2_to_xt_1(ps2buffer[0]); 237 | } 238 | if (len == 2) { 239 | if (ps2buffer[0] == 0xF0) retval = ps2_to_xt_1(ps2buffer[1]) | 0x80; 240 | if (ps2buffer[0] == 0xE0) retval = ps2_to_xt_2(ps2buffer[1]); 241 | } 242 | if (len == 3) { 243 | if ((ps2buffer[0] == 0xE0) && (ps2buffer[1] == 0xF0)) retval = ps2_to_xt_2(ps2buffer[2]) | 0x80; 244 | } 245 | //end translate code 246 | 247 | for (i = len; i < KBD_BUFFER_SIZE; i++) { 248 | ps2buffer[i - len] = ps2buffer[i]; 249 | } 250 | 251 | ps2bufsize -= len; 252 | 253 | // NUMLOCK 254 | 255 | switch (retval) { 256 | case 0x45: 257 | keyboard_toggle_led(PS2_LED_NUM_LOCK); 258 | break; 259 | case 0x46: 260 | keyboard_toggle_led(PS2_LED_SCROLL_LOCK); 261 | break; 262 | case 0x3A: 263 | keyboard_toggle_led(PS2_LED_CAPS_LOCK); 264 | break; 265 | } 266 | return retval; 267 | } 268 | 269 | void KeyboardHandler(void) { 270 | static uint8_t incoming = 0; 271 | static uint32_t prev_ms = 0; 272 | uint32_t now_ms; 273 | uint8_t n, val; 274 | 275 | val = gpio_get(KBD_DATA_PIN); 276 | now_ms = time_us_64(); 277 | if (now_ms - prev_ms > 250) { 278 | bitcount = 0; 279 | incoming = 0; 280 | } 281 | prev_ms = now_ms; 282 | n = bitcount - 1; 283 | if (n <= 7) { 284 | incoming |= (val << n); 285 | } 286 | bitcount++; 287 | if (bitcount == 11) { 288 | if (ps2bufsize < KBD_BUFFER_SIZE) { 289 | ps2buffer[ps2bufsize++] = incoming; 290 | ps2poll(); 291 | } 292 | bitcount = 0; 293 | incoming = 0; 294 | } 295 | kbloop = 1; 296 | } 297 | 298 | void keyboard_init(void) { 299 | bitcount = 0; 300 | memset(ps2buffer, 0, KBD_BUFFER_SIZE); 301 | 302 | gpio_init(KBD_CLOCK_PIN); 303 | gpio_init(KBD_DATA_PIN); 304 | gpio_disable_pulls(KBD_CLOCK_PIN); 305 | gpio_disable_pulls(KBD_DATA_PIN); 306 | gpio_set_drive_strength(KBD_CLOCK_PIN, GPIO_DRIVE_STRENGTH_12MA); 307 | gpio_set_drive_strength(KBD_DATA_PIN, GPIO_DRIVE_STRENGTH_12MA); 308 | gpio_set_dir(KBD_CLOCK_PIN, GPIO_IN); 309 | gpio_set_dir(KBD_DATA_PIN, GPIO_IN); 310 | 311 | gpio_set_irq_enabled_with_callback(KBD_CLOCK_PIN, GPIO_IRQ_EDGE_FALL, true, 312 | (gpio_irq_callback_t)&KeyboardHandler); // 313 | 314 | // Blink all 3 leds 315 | //ps2_send(0xFF); //Reset and start self-test 316 | //sleep_ms(400); // Why so long? 317 | 318 | //ps2_send(0xF2); // Get keyvoard id https://wiki.osdev.org/PS/2_Keyboard 319 | //sleep_ms(250); 320 | 321 | /* 322 | ps2_send(0xED); 323 | sleep_ms(50); 324 | ps2_send(2); // NUM 325 | 326 | ps2_send(0xED); 327 | sleep_ms(50); 328 | ps2_send(3); // SCROLL 329 | */ 330 | /* ps2_send(0xED); 331 | sleep_ms(50); 332 | ps2_send(7);*/ 333 | 334 | return; 335 | } 336 | 337 | extern uint16_t portram[256]; 338 | 339 | extern void doirq(uint8_t irqnum); 340 | 341 | extern bool handleScancode(uint32_t ps2scancode); 342 | 343 | void ps2poll() { 344 | uint32_t ps2scancode = ps2getcode(); 345 | if (!ps2scancode) { 346 | return; 347 | } 348 | 349 | if (handleScancode(ps2scancode)) { 350 | return; 351 | } 352 | #if 0 353 | portram[0x60] = ps2scancode; 354 | // char tmp[20]; sprintf(tmp, "sc: 0x%X", ps2scancode); logMsg(tmp); 355 | portram[0x64] |= 2; 356 | doirq(1); 357 | #endif 358 | } 359 | -------------------------------------------------------------------------------- /drivers/ps2/ps2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "strings.h" 4 | #include "stdio.h" 5 | 6 | #include 7 | 8 | #ifndef KBD_CLOCK_PIN 9 | #define KBD_CLOCK_PIN (0) 10 | #endif 11 | #ifndef KBD_DATA_PIN 12 | #define KBD_DATA_PIN (1) 13 | #endif 14 | #define KBD_BUFFER_SIZE 16 15 | 16 | #define PS2_LED_SCROLL_LOCK 1 17 | #define PS2_LED_NUM_LOCK 2 18 | #define PS2_LED_CAPS_LOCK 4 19 | 20 | extern uint8_t kbloop; 21 | 22 | void KeyboardHandler(); //uint /*gpio*/, uint32_t /*event_mask*/ 23 | uint32_t ps2getcode(void); 24 | 25 | 26 | void keyboard_init(void); 27 | 28 | void Deinit_kbd(void); 29 | 30 | void keyboard_toggle_led(uint8_t led); 31 | 32 | int16_t keyboard_send(uint8_t data); 33 | 34 | struct ps2_struct_group { 35 | unsigned char character; 36 | unsigned char make; 37 | unsigned is_char; 38 | unsigned char xt_make; 39 | }; 40 | 41 | 42 | static struct ps2_struct_group ps2_group1[] = 43 | { 44 | { 'a', 0x1C, 1, 0x1E }, 45 | { 'b', 0x32, 1, 0x30 }, 46 | { 'c', 0x21, 1, 0x2E }, 47 | { 'd', 0x23, 1, 0x20 }, 48 | { 'e', 0x24, 1, 0x12 }, 49 | { 'f', 0x2B, 1, 0x21 }, 50 | { 'g', 0x34, 1, 0x22 }, 51 | { 'h', 0x33, 1, 0x23 }, 52 | { 'i', 0x43, 1, 0x17 }, 53 | { 'j', 0x3B, 1, 0x24 }, 54 | { 'k', 0x42, 1, 0x25 }, 55 | { 'l', 0x4B, 1, 0x26 }, 56 | { 'm', 0x3A, 1, 0x32 }, 57 | { 'n', 0x31, 1, 0x31 }, 58 | { 'o', 0x44, 1, 0x18 }, 59 | { 'p', 0x4D, 1, 0x19 }, 60 | { 'q', 0x15, 1, 0x10 }, 61 | { 'r', 0x2D, 1, 0x13 }, 62 | { 's', 0x1B, 1, 0x1F }, 63 | { 't', 0x2C, 1, 0x14 }, 64 | { 'u', 0x3C, 1, 0x16 }, 65 | { 'v', 0x2A, 1, 0x2F }, 66 | { 'w', 0x1D, 1, 0x11 }, 67 | { 'x', 0x22, 1, 0x2D }, 68 | { 'y', 0x35, 1, 0x15 }, 69 | { 'z', 0x1A, 1, 0x2C }, 70 | { '0', 0x45, 1, 0x0B }, 71 | { '1', 0x16, 1, 0x02 }, 72 | { '2', 0x1E, 1, 0x03 }, 73 | { '3', 0x26, 1, 0x04 }, 74 | { '4', 0x25, 1, 0x05 }, 75 | { '5', 0x2E, 1, 0x06 }, 76 | { '6', 0x36, 1, 0x07 }, 77 | { '7', 0x3D, 1, 0x08 }, 78 | { '8', 0x3E, 1, 0x09 }, 79 | { '9', 0x46, 1, 0x0A }, 80 | { '`', 0x0E, 1, 0x29 }, 81 | { '-', 0x4E, 1, 0x0C }, 82 | { '=', 0x55, 1, 0x0D }, 83 | { '\\', 0x5D, 1, 0x2B }, 84 | { '\b', 0x66, 0, 0x0E }, // backsapce 85 | { ' ', 0x29, 1, 0x39 }, // space 86 | { '\t', 0x0D, 0, 0x0F }, // tab 87 | { ' ', 0x58, 0, 0x3A }, // caps 88 | { ' ', 0x12, 0, 0x2A }, // left shift 89 | { ' ', 0x14, 0, 0x1D }, // left ctrl 90 | { ' ', 0x11, 0, 0x38 }, // left alt 91 | { ' ', 0x59, 0, 0x36 }, // right shift 92 | { '\n', 0x5A, 1, 0x1C }, // enter 93 | { ' ', 0x76, 0, 0x01 }, // esc 94 | { ' ', 0x05, 0, 0x3B }, // F1 95 | { ' ', 0x06, 0, 0x3C }, // F2 96 | { ' ', 0x04, 0, 0x3D }, // F3 97 | { ' ', 0x0C, 0, 0x3E }, // F4 98 | { ' ', 0x03, 0, 0x3F }, // F5 99 | { ' ', 0x0B, 0, 0x40 }, // F6 100 | { ' ', 0x83, 0, 0x41 }, // F7 101 | { ' ', 0x0A, 0, 0x42 }, // F8 102 | { ' ', 0x01, 0, 0x43 }, // f9 103 | { ' ', 0x09, 0, 0x44 }, // f10 104 | { ' ', 0x78, 0, 0x57 }, // f11 105 | { ' ', 0x07, 0, 0x58 }, // f12 106 | { ' ', 0x7E, 0, 0x46 }, // SCROLL 107 | { '[', 0x54, 1, 0x1A }, 108 | { ' ', 0x77, 0, 0x45 }, // Num Lock 109 | { '*', 0x7C, 1, 0x37 }, // Keypad * 110 | { '-', 0x7B, 1, 0x4A }, // Keypad - 111 | { '+', 0x79, 1, 0x4E }, // Keypad + 112 | { '.', 0x71, 1, 0x53 }, // Keypad . 113 | { '0', 0x70, 1, 0x52 }, // Keypad 0 114 | { '1', 0x69, 1, 0x4F }, // Keypad 1 115 | { '2', 0x72, 1, 0x50 }, // Keypad 2 116 | { '3', 0x7A, 1, 0x51 }, // Keypad 3 117 | { '4', 0x6B, 1, 0x4B }, // Keypad 4 118 | { '5', 0x73, 1, 0x4C }, // Keypad 5 119 | { '6', 0x74, 1, 0x4D }, // Keypad 6 120 | { '7', 0x6C, 1, 0x47 }, // Keypad 7 121 | { '8', 0x75, 1, 0x48 }, // Keypad 8 122 | { '9', 0x7D, 1, 0x49 }, // Keypad 9 123 | { ']', 0x5B, 1, 0x1B }, 124 | { ';', 0x4C, 1, 0x27 }, 125 | { '\'', 0x52, 1, 0x28 }, 126 | { ',', 0x41, 1, 0x33 }, 127 | { '.', 0x49, 1, 0x34 }, 128 | { '/', 0x4A, 1, 0x35 }, 129 | }; 130 | 131 | static struct ps2_struct_group ps2_group2[] = 132 | { 133 | { ' ', 0x5B, 0, 0x1F }, // left gui 134 | { ' ', 0x1D, 0, 0x14 }, // right ctrl 135 | { ' ', 0x5C, 0, 0x27 }, // right gui 136 | { ' ', 0x38, 0, 0x11 }, // right alt 137 | { ' ', 0x5D, 0, 0x2F }, // apps 138 | { ' ', 0x52, 0, 0x70 }, // insert 139 | { ' ', 0x47, 0, 0x6C }, // home 140 | { ' ', 0x49, 0, 0x7D }, // page up 141 | { ' ', 0x53, 0, 0x71 }, // delete 142 | { ' ', 0x4F, 0, 0x69 }, // end 143 | { ' ', 0x51, 0, 0x7A }, // page down 144 | { ' ', 0x48, 0, 0x75 }, // u arrow 145 | { ' ', 0x4B, 0, 0x6B }, // l arrow 146 | { ' ', 0x50, 0, 0x72 }, // d arrow 147 | { ' ', 0x4D, 0, 0x74 }, // r arrow 148 | { ' ', 0x1C, 0, 0x5A }, // kp en 149 | }; 150 | -------------------------------------------------------------------------------- /drivers/ps2kbd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ps2kbd INTERFACE) 2 | 3 | target_sources(ps2kbd INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/ps2kbd_mrmltr.cpp 5 | ${CMAKE_CURRENT_LIST_DIR}/ps2kbd_mrmltr.h 6 | ) 7 | 8 | target_link_libraries(ps2kbd INTERFACE hardware_pio hardware_clocks) 9 | 10 | target_include_directories(ps2kbd INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR} 12 | ) 13 | 14 | 15 | pico_generate_pio_header(ps2kbd 16 | ${CMAKE_CURRENT_LIST_DIR}/ps2kbd_mrmltr.pio 17 | ) 18 | 19 | pico_generate_pio_header(ps2kbd 20 | ${CMAKE_CURRENT_LIST_DIR}/ps2kbd_mrmltr2.pio 21 | ) 22 | -------------------------------------------------------------------------------- /drivers/ps2kbd/ps2kbd_mrmltr.h: -------------------------------------------------------------------------------- 1 | // You may use, distribute and modify this code under the 2 | // terms of the GPLv2 license, which unfortunately won't be 3 | // written for another century. 4 | // 5 | // SPDX-License-Identifier: GPL-2.0-or-later 6 | // 7 | #pragma once 8 | 9 | #ifndef _PS2KBD_H 10 | #define _PS2KBD_H 11 | 12 | #include "tusb.h" 13 | #include "hardware/pio.h" 14 | #include "hardware/gpio.h" 15 | #include 16 | 17 | 18 | typedef struct { 19 | uint8_t code; 20 | bool release; 21 | uint8_t page; 22 | } Ps2KbdAction_; 23 | 24 | 25 | class Ps2Kbd_Mrmltr { 26 | private: 27 | PIO _pio; // pio0 or pio1 28 | uint _sm; // pio state machine index 29 | uint _base_gpio; // data signal gpio 30 | hid_keyboard_report_t _report; // HID report structure 31 | Ps2KbdAction_ _actions[2]; 32 | uint _action; 33 | bool _double; 34 | bool _overflow; 35 | 36 | std::function _keyHandler; 37 | 38 | inline void clearActions() { 39 | _actions[0].page = 0; 40 | _actions[0].release = false; 41 | _actions[0].code = 0; 42 | _actions[1].page = 0; 43 | _actions[1].release = false; 44 | _actions[1].code = 0; 45 | _action = 0; 46 | } 47 | 48 | void __not_in_flash_func(handleHidKeyPress)(uint8_t hidKeyCode); 49 | void __not_in_flash_func(handleHidKeyRelease)(uint8_t hidKeyCode); 50 | 51 | void __not_in_flash_func(handleActions)(); 52 | uint8_t __not_in_flash_func(hidCodePage0)(uint8_t ps2code); 53 | uint8_t __not_in_flash_func(hidCodePage1)(uint8_t ps2code); 54 | void clearHidKeys(); 55 | 56 | public: 57 | 58 | Ps2Kbd_Mrmltr( 59 | PIO pio, 60 | uint base_gpio, 61 | std::function keyHandler); 62 | 63 | void init_gpio(); 64 | 65 | void __not_in_flash_func(tick)(); 66 | }; 67 | 68 | #endif -------------------------------------------------------------------------------- /drivers/ps2kbd/ps2kbd_mrmltr.pio: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 1883 Thomas Edison - All Rights Reserved 2 | ; You may use, distribute and modify this code under the 3 | ; terms of the GPLv2 license, which unfortunately won't be 4 | ; written for another century. 5 | ; 6 | ; SPDX-License-Identifier: GPL-2.0-or-later 7 | ; 8 | .program ps2kbd 9 | 10 | ; Ps2 for MURMULATOR 0->CLK 1->DAT 11 | ;================================================ 12 | wait 0 gpio 0 ; skip start bit 13 | wait 1 gpio 0 14 | ;---------------------- 15 | set x, 9 ; 8 bit counter 16 | bitloop: 17 | wait 0 gpio 0 [1] ; wait negative clock edge 18 | ;---------------------- 19 | in pins, 1 ; sample data 20 | wait 1 gpio 0 ; wait for positive edge 21 | ;---------------------- 22 | jmp x-- bitloop 23 | 24 | -------------------------------------------------------------------------------- /drivers/ps2kbd/ps2kbd_mrmltr2.pio: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 1883 Thomas Edison - All Rights Reserved 2 | ; You may use, distribute and modify this code under the 3 | ; terms of the GPLv2 license, which unfortunately won't be 4 | ; written for another century. 5 | ; 6 | ; SPDX-License-Identifier: GPL-2.0-or-later 7 | ; 8 | .program m2ps2kbd 9 | 10 | ; Ps2 for MURMULATOR 2->CLK 3->DAT 11 | ;================================================ 12 | wait 0 gpio 2 ; skip start bit 13 | wait 1 gpio 2 14 | ;---------------------- 15 | set x, 9 ; 8 bit counter 16 | bitloop: 17 | wait 0 gpio 2 [1] ; wait negative clock edge 18 | ;---------------------- 19 | in pins, 1 ; sample data 20 | wait 1 gpio 2 ; wait for positive edge 21 | ;---------------------- 22 | jmp x-- bitloop 23 | 24 | -------------------------------------------------------------------------------- /drivers/sdcard/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(${CMAKE_CURRENT_LIST_DIR}/sdcard.cmake) -------------------------------------------------------------------------------- /drivers/sdcard/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2021, Elehobica 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 17 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 19 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 21 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 22 | OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /drivers/sdcard/README.md: -------------------------------------------------------------------------------- 1 | # Pico SDK SD Card bindings for Fat FS 2 | 3 | All of the hard work to bring up these bindings was done by @elohobica - https://github.com/elehobica/pico_fatfs_test 4 | 5 | `sdcard.c` is a hard fork of their `tf_card.c` to add re-usable SDCard support to the Pimoroni Pico libraries. 6 | 7 | It's licensed under the BSD 2-Clause license. 8 | -------------------------------------------------------------------------------- /drivers/sdcard/pio_spi.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "pio_spi.h" 8 | 9 | // Just 8 bit functions provided here. The PIO program supports any frame size 10 | // 1...32, but the software to do the necessary FIFO shuffling is left as an 11 | // exercise for the reader :) 12 | // 13 | // Likewise we only provide MSB-first here. To do LSB-first, you need to 14 | // - Do shifts when reading from the FIFO, for general case n != 8, 16, 32 15 | // - Do a narrow read at a one halfword or 3 byte offset for n == 16, 8 16 | // in order to get the read data correctly justified. 17 | 18 | void __time_critical_func(pio_spi_write8_blocking)(const pio_spi_inst_t *spi, const uint8_t *src, size_t len) { 19 | size_t tx_remain = len, rx_remain = len; 20 | // Do 8 bit accesses on FIFO, so that write data is byte-replicated. This 21 | // gets us the left-justification for free (for MSB-first shift-out) 22 | io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; 23 | io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; 24 | while (tx_remain || rx_remain) { 25 | if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { 26 | *txfifo = *src++; 27 | --tx_remain; 28 | } 29 | if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { 30 | (void) *rxfifo; 31 | --rx_remain; 32 | } 33 | } 34 | } 35 | 36 | void __time_critical_func(pio_spi_read8_blocking)(const pio_spi_inst_t *spi, uint8_t *dst, size_t len) { 37 | size_t tx_remain = len, rx_remain = len; 38 | io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; 39 | io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; 40 | while (tx_remain || rx_remain) { 41 | if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { 42 | *txfifo = 0; 43 | --tx_remain; 44 | } 45 | if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { 46 | *dst++ = *rxfifo; 47 | --rx_remain; 48 | } 49 | } 50 | } 51 | 52 | void __time_critical_func(pio_spi_write8_read8_blocking)(const pio_spi_inst_t *spi, uint8_t *src, uint8_t *dst, 53 | size_t len) { 54 | size_t tx_remain = len, rx_remain = len; 55 | io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; 56 | io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; 57 | while (tx_remain || rx_remain) { 58 | if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { 59 | *txfifo = *src++; 60 | --tx_remain; 61 | } 62 | if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { 63 | *dst++ = *rxfifo; 64 | --rx_remain; 65 | } 66 | } 67 | } 68 | 69 | void __time_critical_func(pio_spi_repeat8_read8_blocking)(const pio_spi_inst_t *spi, uint8_t src, uint8_t *dst, 70 | size_t len) { 71 | size_t tx_remain = len, rx_remain = len; 72 | io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; 73 | io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; 74 | while (tx_remain || rx_remain) { 75 | if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { 76 | *txfifo = src; 77 | --tx_remain; 78 | } 79 | if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { 80 | *dst++ = *rxfifo; 81 | --rx_remain; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /drivers/sdcard/pio_spi.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | #ifndef _PIO_SPI_H 7 | #define _PIO_SPI_H 8 | 9 | #include "hardware/pio.h" 10 | #include "spi.pio.h" 11 | 12 | typedef struct pio_spi_inst { 13 | PIO pio; 14 | uint sm; 15 | uint cs_pin; 16 | } pio_spi_inst_t; 17 | 18 | void pio_spi_write8_blocking(const pio_spi_inst_t *spi, const uint8_t *src, size_t len); 19 | 20 | void pio_spi_read8_blocking(const pio_spi_inst_t *spi, uint8_t *dst, size_t len); 21 | 22 | void pio_spi_write8_read8_blocking(const pio_spi_inst_t *spi, uint8_t *src, uint8_t *dst, size_t len); 23 | 24 | void pio_spi_repeat8_read8_blocking(const pio_spi_inst_t *spi, uint8_t src, uint8_t *dst, size_t len); 25 | 26 | #endif -------------------------------------------------------------------------------- /drivers/sdcard/sdcard.cmake: -------------------------------------------------------------------------------- 1 | if (NOT TARGET sdcard) 2 | add_library(sdcard INTERFACE) 3 | 4 | pico_generate_pio_header(sdcard ${CMAKE_CURRENT_LIST_DIR}/spi.pio) 5 | 6 | target_sources(sdcard INTERFACE 7 | ${CMAKE_CURRENT_LIST_DIR}/sdcard.c 8 | ${CMAKE_CURRENT_LIST_DIR}/pio_spi.c 9 | ) 10 | 11 | target_link_libraries(sdcard INTERFACE fatfs pico_stdlib hardware_clocks hardware_spi hardware_pio) 12 | target_include_directories(sdcard INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 13 | endif () 14 | -------------------------------------------------------------------------------- /drivers/sdcard/sdcard.h: -------------------------------------------------------------------------------- 1 | #ifndef _SDCARD_H_ 2 | #define _SDCARD_H_ 3 | 4 | /* SPI pin assignment */ 5 | 6 | /* Pico Wireless */ 7 | #ifndef SDCARD_SPI_BUS 8 | #define SDCARD_SPI_BUS spi0 9 | #endif 10 | 11 | #ifndef SDCARD_PIN_SPI0_CS 12 | #define SDCARD_PIN_SPI0_CS 22 13 | #endif 14 | 15 | #ifndef SDCARD_PIN_SPI0_SCK 16 | #define SDCARD_PIN_SPI0_SCK 18 17 | #endif 18 | 19 | #ifndef SDCARD_PIN_SPI0_MOSI 20 | #define SDCARD_PIN_SPI0_MOSI 19 21 | #endif 22 | 23 | #ifndef SDCARD_PIN_SPI0_MISO 24 | #define SDCARD_PIN_SPI0_MISO 16 25 | #endif 26 | 27 | #endif // _SDCARD_H_ 28 | -------------------------------------------------------------------------------- /drivers/sdcard/spi.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | ; These programs implement full-duplex SPI, with a SCK period of 4 clock 8 | ; cycles. A different program is provided for each value of CPHA, and CPOL is 9 | ; achieved using the hardware GPIO inversion available in the IO controls. 10 | ; 11 | ; Transmit-only SPI can go twice as fast -- see the ST7789 example! 12 | 13 | 14 | .program spi_cpha0 15 | .side_set 1 16 | 17 | ; Pin assignments: 18 | ; - SCK is side-set pin 0 19 | ; - MOSI is OUT pin 0 20 | ; - MISO is IN pin 0 21 | ; 22 | ; Autopush and autopull must be enabled, and the serial frame size is set by 23 | ; configuring the push/pull threshold. Shift left/right is fine, but you must 24 | ; justify the data yourself. This is done most conveniently for frame sizes of 25 | ; 8 or 16 bits by using the narrow store replication and narrow load byte 26 | ; picking behaviour of RP2040's IO fabric. 27 | 28 | ; Clock phase = 0: data is captured on the leading edge of each SCK pulse, and 29 | ; transitions on the trailing edge, or some time before the first leading edge. 30 | 31 | out pins, 1 side 0 [1] ; Stall here on empty (sideset proceeds even if 32 | in pins, 1 side 1 [1] ; instruction stalls, so we stall with SCK low) 33 | 34 | .program spi_cpha1 35 | .side_set 1 36 | 37 | ; Clock phase = 1: data transitions on the leading edge of each SCK pulse, and 38 | ; is captured on the trailing edge. 39 | 40 | out x, 1 side 0 ; Stall here on empty (keep SCK deasserted) 41 | mov pins, x side 1 [1] ; Output data, assert SCK (mov pins uses OUT mapping) 42 | in pins, 1 side 0 ; Input data, deassert SCK 43 | 44 | % c-sdk { 45 | #include "hardware/gpio.h" 46 | static inline void pio_spi_init(PIO pio, uint sm, uint prog_offs, uint n_bits, 47 | float clkdiv, bool cpha, bool cpol, uint pin_sck, uint pin_mosi, uint pin_miso) { 48 | pio_sm_config c = cpha ? spi_cpha1_program_get_default_config(prog_offs) : spi_cpha0_program_get_default_config(prog_offs); 49 | sm_config_set_out_pins(&c, pin_mosi, 1); 50 | sm_config_set_in_pins(&c, pin_miso); 51 | sm_config_set_sideset_pins(&c, pin_sck); 52 | // Only support MSB-first in this example code (shift to left, auto push/pull, threshold=nbits) 53 | sm_config_set_out_shift(&c, false, true, n_bits); 54 | sm_config_set_in_shift(&c, false, true, n_bits); 55 | sm_config_set_clkdiv(&c, clkdiv); 56 | 57 | // MOSI, SCK output are low, MISO is input 58 | pio_sm_set_pins_with_mask(pio, sm, 0, (1u << pin_sck) | (1u << pin_mosi)); 59 | pio_sm_set_pindirs_with_mask(pio, sm, (1u << pin_sck) | (1u << pin_mosi), (1u << pin_sck) | (1u << pin_mosi) | (1u << pin_miso)); 60 | pio_gpio_init(pio, pin_mosi); 61 | pio_gpio_init(pio, pin_miso); 62 | pio_gpio_init(pio, pin_sck); 63 | 64 | // The pin muxes can be configured to invert the output (among other things 65 | // and this is a cheesy way to get CPOL=1 66 | gpio_set_outover(pin_sck, cpol ? GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); 67 | // SPI is synchronous, so bypass input synchroniser to reduce input delay. 68 | hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); 69 | 70 | pio_sm_init(pio, sm, prog_offs, &c); 71 | pio_sm_set_enabled(pio, sm, true); 72 | } 73 | %} 74 | 75 | ; SPI with Chip Select 76 | ; ----------------------------------------------------------------------------- 77 | ; 78 | ; For your amusement, here are some SPI programs with an automatic chip select 79 | ; (asserted once data appears in TX FIFO, deasserts when FIFO bottoms out, has 80 | ; a nice front/back porch). 81 | ; 82 | ; The number of bits per FIFO entry is configured via the Y register 83 | ; and the autopush/pull threshold. From 2 to 32 bits. 84 | ; 85 | ; Pin assignments: 86 | ; - SCK is side-set bit 0 87 | ; - CSn is side-set bit 1 88 | ; - MOSI is OUT bit 0 (host-to-device) 89 | ; - MISO is IN bit 0 (device-to-host) 90 | ; 91 | ; This program only supports one chip select -- use GPIO if more are needed 92 | ; 93 | ; Provide a variation for each possibility of CPHA; for CPOL we can just 94 | ; invert SCK in the IO muxing controls (downstream from PIO) 95 | 96 | 97 | ; CPHA=0: data is captured on the leading edge of each SCK pulse (including 98 | ; the first pulse), and transitions on the trailing edge 99 | 100 | .program spi_cpha0_cs 101 | .side_set 2 102 | 103 | .wrap_target 104 | bitloop: 105 | out pins, 1 side 0x0 [1] 106 | in pins, 1 side 0x1 107 | jmp x-- bitloop side 0x1 108 | 109 | out pins, 1 side 0x0 110 | mov x, y side 0x0 ; Reload bit counter from Y 111 | in pins, 1 side 0x1 112 | jmp !osre bitloop side 0x1 ; Fall-through if TXF empties 113 | 114 | nop side 0x0 [1] ; CSn back porch 115 | public entry_point: ; Must set X,Y to n-2 before starting! 116 | pull ifempty side 0x2 [1] ; Block with CSn high (minimum 2 cycles) 117 | .wrap ; Note ifempty to avoid time-of-check race 118 | 119 | ; CPHA=1: data transitions on the leading edge of each SCK pulse, and is 120 | ; captured on the trailing edge 121 | 122 | .program spi_cpha1_cs 123 | .side_set 2 124 | 125 | .wrap_target 126 | bitloop: 127 | out pins, 1 side 0x1 [1] 128 | in pins, 1 side 0x0 129 | jmp x-- bitloop side 0x0 130 | 131 | out pins, 1 side 0x1 132 | mov x, y side 0x1 133 | in pins, 1 side 0x0 134 | jmp !osre bitloop side 0x0 135 | 136 | public entry_point: ; Must set X,Y to n-2 before starting! 137 | pull ifempty side 0x2 [1] ; Block with CSn high (minimum 2 cycles) 138 | nop side 0x0 [1]; CSn front porch 139 | .wrap 140 | 141 | % c-sdk { 142 | #include "hardware/gpio.h" 143 | static inline void pio_spi_cs_init(PIO pio, uint sm, uint prog_offs, uint n_bits, float clkdiv, bool cpha, bool cpol, 144 | uint pin_sck, uint pin_mosi, uint pin_miso) { 145 | pio_sm_config c = cpha ? spi_cpha1_cs_program_get_default_config(prog_offs) : spi_cpha0_cs_program_get_default_config(prog_offs); 146 | sm_config_set_out_pins(&c, pin_mosi, 1); 147 | sm_config_set_in_pins(&c, pin_miso); 148 | sm_config_set_sideset_pins(&c, pin_sck); 149 | sm_config_set_out_shift(&c, false, true, n_bits); 150 | sm_config_set_in_shift(&c, false, true, n_bits); 151 | sm_config_set_clkdiv(&c, clkdiv); 152 | 153 | pio_sm_set_pins_with_mask(pio, sm, (2u << pin_sck), (3u << pin_sck) | (1u << pin_mosi)); 154 | pio_sm_set_pindirs_with_mask(pio, sm, (3u << pin_sck) | (1u << pin_mosi), (3u << pin_sck) | (1u << pin_mosi) | (1u << pin_miso)); 155 | pio_gpio_init(pio, pin_mosi); 156 | pio_gpio_init(pio, pin_miso); 157 | pio_gpio_init(pio, pin_sck); 158 | pio_gpio_init(pio, pin_sck + 1); 159 | gpio_set_outover(pin_sck, cpol ? GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); 160 | hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); 161 | 162 | uint entry_point = prog_offs + (cpha ? spi_cpha1_cs_offset_entry_point : spi_cpha0_cs_offset_entry_point); 163 | pio_sm_init(pio, sm, entry_point, &c); 164 | pio_sm_exec(pio, sm, pio_encode_set(pio_x, n_bits - 2)); 165 | pio_sm_exec(pio, sm, pio_encode_set(pio_y, n_bits - 2)); 166 | pio_sm_set_enabled(pio, sm, true); 167 | } 168 | %} -------------------------------------------------------------------------------- /drivers/st7789/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(st7789 INTERFACE) 2 | 3 | target_sources(st7789 INTERFACE ${CMAKE_CURRENT_LIST_DIR}/st7789.c) 4 | 5 | target_link_libraries(st7789 INTERFACE hardware_pio hardware_clocks hardware_dma) 6 | 7 | target_include_directories(st7789 INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR} 9 | ) 10 | 11 | pico_generate_pio_header(st7789 12 | ${CMAKE_CURRENT_LIST_DIR}/st7789.pio 13 | ) 14 | -------------------------------------------------------------------------------- /drivers/st7789/st7789.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 | 14 | #include "graphics.h" 15 | 16 | #include 17 | #include 18 | 19 | #include "st7789.pio.h" 20 | #include "hardware/dma.h" 21 | 22 | #ifndef SCREEN_WIDTH 23 | #define SCREEN_WIDTH 320 24 | #endif 25 | 26 | #ifndef SCREEN_HEIGHT 27 | #define SCREEN_HEIGHT 240 28 | #endif 29 | 30 | // 126MHz SPI 31 | #define SERIAL_CLK_DIV 3.0f 32 | #define MADCTL_BGR_PIXEL_ORDER (1<<3) 33 | #define MADCTL_ROW_COLUMN_EXCHANGE (1<<5) 34 | #define MADCTL_COLUMN_ADDRESS_ORDER_SWAP (1<<6) 35 | 36 | 37 | #define CHECK_BIT(var, pos) (((var)>>(pos)) & 1) 38 | 39 | static uint sm = 0; 40 | static PIO pio = pio0; 41 | static uint st7789_chan; 42 | 43 | uint16_t __scratch_y("tft_palette") palette[256]; 44 | 45 | uint8_t* text_buffer = NULL; 46 | static uint8_t* graphics_buffer = NULL; 47 | 48 | static uint graphics_buffer_width = 0; 49 | static uint graphics_buffer_height = 0; 50 | static int graphics_buffer_shift_x = 0; 51 | static int graphics_buffer_shift_y = 0; 52 | 53 | enum graphics_mode_t graphics_mode = GRAPHICSMODE_DEFAULT; 54 | 55 | static const uint8_t init_seq[] = { 56 | 1, 20, 0x01, // Software reset 57 | 1, 10, 0x11, // Exit sleep mode 58 | 2, 2, 0x3a, 0x55, // Set colour mode to 16 bit 59 | #ifdef ILI9341 60 | // ILI9341 61 | 2, 0, 0x36, MADCTL_ROW_COLUMN_EXCHANGE | MADCTL_BGR_PIXEL_ORDER, // Set MADCTL 62 | #else 63 | // ST7789 64 | 2, 0, 0x36, MADCTL_COLUMN_ADDRESS_ORDER_SWAP | MADCTL_ROW_COLUMN_EXCHANGE, // Set MADCTL 65 | #endif 66 | 5, 0, 0x2a, 0x00, 0x00, SCREEN_WIDTH >> 8, SCREEN_WIDTH & 0xff, // CASET: column addresses 67 | 5, 0, 0x2b, 0x00, 0x00, SCREEN_HEIGHT >> 8, SCREEN_HEIGHT & 0xff, // RASET: row addresses 68 | 1, 2, 0x20, // Inversion OFF 69 | 1, 2, 0x13, // Normal display on, then 10 ms delay 70 | 1, 2, 0x29, // Main screen turn on, then wait 500 ms 71 | 0 // Terminate list 72 | }; 73 | // Format: cmd length (including cmd byte), post delay in units of 5 ms, then cmd payload 74 | // Note the delays have been shortened a little 75 | 76 | static inline void lcd_set_dc_cs(const bool dc, const bool cs) { 77 | sleep_us(5); 78 | gpio_put_masked((1u << TFT_DC_PIN) | (1u << TFT_CS_PIN), !!dc << TFT_DC_PIN | !!cs << TFT_CS_PIN); 79 | sleep_us(5); 80 | } 81 | 82 | static inline void lcd_write_cmd(const uint8_t* cmd, size_t count) { 83 | st7789_lcd_wait_idle(pio, sm); 84 | lcd_set_dc_cs(0, 0); 85 | st7789_lcd_put(pio, sm, *cmd++); 86 | if (count >= 2) { 87 | st7789_lcd_wait_idle(pio, sm); 88 | lcd_set_dc_cs(1, 0); 89 | for (size_t i = 0; i < count - 1; ++i) 90 | st7789_lcd_put(pio, sm, *cmd++); 91 | } 92 | st7789_lcd_wait_idle(pio, sm); 93 | lcd_set_dc_cs(1, 1); 94 | } 95 | 96 | static inline void lcd_set_window(const uint16_t x, 97 | const uint16_t y, 98 | const uint16_t width, 99 | const uint16_t height) { 100 | static uint8_t screen_width_cmd[] = { 0x2a, 0x00, 0x00, SCREEN_WIDTH >> 8, SCREEN_WIDTH & 0xff }; 101 | static uint8_t screen_height_command[] = { 0x2b, 0x00, 0x00, SCREEN_HEIGHT >> 8, SCREEN_HEIGHT & 0xff }; 102 | screen_width_cmd[2] = x; 103 | screen_width_cmd[4] = x + width - 1; 104 | 105 | screen_height_command[2] = y; 106 | screen_height_command[4] = y + height - 1; 107 | lcd_write_cmd(screen_width_cmd, 5); 108 | lcd_write_cmd(screen_height_command, 5); 109 | } 110 | 111 | static inline void lcd_init(const uint8_t* init_seq) { 112 | const uint8_t* cmd = init_seq; 113 | while (*cmd) { 114 | lcd_write_cmd(cmd + 2, *cmd); 115 | sleep_ms(*(cmd + 1) * 5); 116 | cmd += *cmd + 2; 117 | } 118 | } 119 | 120 | static inline void start_pixels() { 121 | const uint8_t cmd = 0x2c; // RAMWR 122 | st7789_lcd_wait_idle(pio, sm); 123 | st7789_set_pixel_mode(pio, sm, false); 124 | lcd_write_cmd(&cmd, 1); 125 | st7789_set_pixel_mode(pio, sm, true); 126 | lcd_set_dc_cs(1, 0); 127 | } 128 | 129 | void stop_pixels() { 130 | st7789_lcd_wait_idle(pio, sm); 131 | lcd_set_dc_cs(1, 1); 132 | st7789_set_pixel_mode(pio, sm, false); 133 | } 134 | 135 | void create_dma_channel() { 136 | st7789_chan = dma_claim_unused_channel(true); 137 | 138 | dma_channel_config c = dma_channel_get_default_config(st7789_chan); 139 | channel_config_set_transfer_data_size(&c, DMA_SIZE_16); 140 | channel_config_set_dreq(&c, pio_get_dreq(pio, sm, true)); 141 | channel_config_set_read_increment(&c, true); 142 | channel_config_set_write_increment(&c, false); 143 | 144 | dma_channel_configure( 145 | st7789_chan, // Channel to be configured 146 | &c, // The configuration we just created 147 | &pio->txf[sm], // The write address 148 | NULL, // The initial read address - set later 149 | 0, // Number of transfers - set later 150 | false // Don't start yet 151 | ); 152 | } 153 | 154 | void graphics_init() { 155 | const uint offset = pio_add_program(pio, &st7789_lcd_program); 156 | sm = pio_claim_unused_sm(pio, true); 157 | st7789_lcd_program_init(pio, sm, offset, TFT_DATA_PIN, TFT_CLK_PIN, SERIAL_CLK_DIV); 158 | 159 | gpio_init(TFT_CS_PIN); 160 | gpio_init(TFT_DC_PIN); 161 | gpio_init(TFT_RST_PIN); 162 | gpio_init(TFT_LED_PIN); 163 | gpio_set_dir(TFT_CS_PIN, GPIO_OUT); 164 | gpio_set_dir(TFT_DC_PIN, GPIO_OUT); 165 | gpio_set_dir(TFT_RST_PIN, GPIO_OUT); 166 | gpio_set_dir(TFT_LED_PIN, GPIO_OUT); 167 | 168 | gpio_put(TFT_CS_PIN, 1); 169 | gpio_put(TFT_RST_PIN, 1); 170 | lcd_init(init_seq); 171 | gpio_put(TFT_LED_PIN, 1); 172 | 173 | for (int i = 0; i < sizeof palette; i++) { 174 | graphics_set_palette(i, 0x0000); 175 | } 176 | clrScr(0); 177 | 178 | create_dma_channel(); 179 | } 180 | 181 | void inline graphics_set_mode(const enum graphics_mode_t mode) { 182 | graphics_mode = -1; 183 | sleep_ms(16); 184 | clrScr(0); 185 | graphics_mode = mode; 186 | } 187 | 188 | void graphics_set_buffer(uint8_t* buffer, const uint16_t width, const uint16_t height) { 189 | graphics_buffer = buffer; 190 | graphics_buffer_width = width; 191 | graphics_buffer_height = height; 192 | } 193 | 194 | void graphics_set_textbuffer(uint8_t* buffer) { 195 | text_buffer = buffer; 196 | } 197 | 198 | void graphics_set_offset(const int x, const int y) { 199 | graphics_buffer_shift_x = x; 200 | graphics_buffer_shift_y = y; 201 | } 202 | 203 | void clrScr(const uint8_t color) { 204 | memset(&graphics_buffer[0], 0, graphics_buffer_height * graphics_buffer_width); 205 | lcd_set_window(0, 0,SCREEN_WIDTH,SCREEN_HEIGHT); 206 | uint32_t i = SCREEN_WIDTH * SCREEN_HEIGHT; 207 | start_pixels(); 208 | while (--i) { 209 | st7789_lcd_put_pixel(pio, sm, 0x0000); 210 | } 211 | stop_pixels(); 212 | } 213 | 214 | void st7789_dma_pixels(const uint16_t* pixels, const uint num_pixels) { 215 | // Ensure any previous transfer is finished. 216 | dma_channel_wait_for_finish_blocking(st7789_chan); 217 | 218 | dma_channel_hw_addr(st7789_chan)->read_addr = (uintptr_t)pixels; 219 | dma_channel_hw_addr(st7789_chan)->transfer_count = num_pixels; 220 | const uint ctrl = dma_channel_hw_addr(st7789_chan)->ctrl_trig; 221 | dma_channel_hw_addr(st7789_chan)->ctrl_trig = ctrl | DMA_CH0_CTRL_TRIG_INCR_READ_BITS; 222 | } 223 | 224 | void __inline __scratch_y("refresh_lcd") refresh_lcd() { 225 | switch (graphics_mode) { 226 | case TEXTMODE_DEFAULT: 227 | lcd_set_window(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); 228 | start_pixels(); 229 | for (int y = 0; y < SCREEN_HEIGHT; y++) { 230 | // TODO add auto adjustable padding? 231 | st7789_lcd_put_pixel(pio, sm, 0x0000); 232 | 233 | for (int x = 0; x < TEXTMODE_COLS; x++) { 234 | const uint16_t offset = (y / 8) * (TEXTMODE_COLS * 2) + x * 2; 235 | const uint8_t c = text_buffer[offset]; 236 | const uint8_t colorIndex = text_buffer[offset + 1]; 237 | const uint8_t glyph_row = font_6x8[c * 8 + y % 8]; 238 | 239 | for (uint8_t bit = 0; bit < 6; bit++) { 240 | st7789_lcd_put_pixel(pio, sm, textmode_palette[(c && CHECK_BIT(glyph_row, bit)) 241 | ? colorIndex & 0x0F 242 | : colorIndex >> 4 & 0x0F]); 243 | } 244 | } 245 | st7789_lcd_put_pixel(pio, sm, 0x0000); 246 | } 247 | stop_pixels(); 248 | break; 249 | case GRAPHICSMODE_DEFAULT: { 250 | const uint8_t* bitmap = graphics_buffer + 12 * 160; 251 | lcd_set_window(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); 252 | 253 | start_pixels(); 254 | for (int y = 0; y < 240; y++) 255 | for (int x = 0; x < 160; x++) { 256 | const uint16_t color = palette[bitmap[x + y / 2 * 160]]; 257 | 258 | st7789_lcd_put_pixel(pio, sm, color); 259 | st7789_lcd_put_pixel(pio, sm, color); 260 | } 261 | 262 | stop_pixels(); 263 | } 264 | } 265 | 266 | // st7789_lcd_wait_idle(pio, sm); 267 | } 268 | 269 | 270 | void graphics_set_palette(const uint8_t i, const uint32_t color) { 271 | palette[i] = (uint16_t)color; 272 | } 273 | -------------------------------------------------------------------------------- /drivers/st7789/st7789.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #ifndef TFT_RST_PIN 5 | #define TFT_RST_PIN 8 6 | #endif 7 | 8 | 9 | #ifndef TFT_CS_PIN 10 | #define TFT_CS_PIN 6 11 | #endif 12 | 13 | 14 | #ifndef TFT_LED_PIN 15 | #define TFT_LED_PIN 9 16 | #endif 17 | 18 | 19 | #ifndef TFT_CLK_PIN 20 | #define TFT_CLK_PIN 13 21 | #endif 22 | 23 | #ifndef TFT_DATA_PIN 24 | #define TFT_DATA_PIN 12 25 | #endif 26 | 27 | #ifndef TFT_DC_PIN 28 | #define TFT_DC_PIN 10 29 | #endif 30 | 31 | #define TEXTMODE_COLS 53 32 | #define TEXTMODE_ROWS 30 33 | 34 | #define RGB888(r, g, b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3)) 35 | static const uint16_t textmode_palette[16] = { 36 | //R, G, B 37 | RGB888(0x00,0x00, 0x00), //black 38 | RGB888(0x00,0x00, 0xC4), //blue 39 | RGB888(0x00,0xC4, 0x00), //green 40 | RGB888(0x00,0xC4, 0xC4), //cyan 41 | RGB888(0xC4,0x00, 0x00), //red 42 | RGB888(0xC4,0x00, 0xC4), //magenta 43 | RGB888(0xC4,0x7E, 0x00), //brown 44 | RGB888(0xC4,0xC4, 0xC4), //light gray 45 | RGB888(0x4E,0x4E, 0x4E), //dark gray 46 | RGB888(0x4E,0x4E, 0xDC), //light blue 47 | RGB888(0x4E,0xDC, 0x4E), //light green 48 | RGB888(0x4E,0xF3, 0xF3), //light cyan 49 | RGB888(0xDC,0x4E, 0x4E), //light red 50 | RGB888(0xF3,0x4E, 0xF3), //light magenta 51 | RGB888(0xF3,0xF3, 0x4E), //yellow 52 | RGB888(0xFF,0xFF, 0xFF), //white 53 | }; 54 | 55 | inline static void graphics_set_bgcolor(uint32_t color888) { 56 | // dummy 57 | } 58 | inline static void graphics_set_flashmode(bool flash_line, bool flash_frame) { 59 | // dummy 60 | } 61 | void refresh_lcd(); 62 | -------------------------------------------------------------------------------- /drivers/st7789/st7789.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 1 9 | 10 | ; This is just a simple clocked serial TX. At 125 MHz system clock we can 11 | ; sustain up to 62.5 Mbps. 12 | ; Data on OUT pin 0 13 | ; Clock on side-set pin 0 14 | 15 | .wrap_target 16 | out pins, 1 side 0 ; stall here if no data (clock low) 17 | nop side 1 18 | .wrap 19 | 20 | % c-sdk { 21 | // For optimal use of DMA bandwidth we would use an autopull threshold of 32, 22 | // but we are using a threshold of 8 here (consume 1 byte from each FIFO entry 23 | // and discard the remainder) to make things easier for software on the other side 24 | 25 | static inline void st7789_lcd_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clk_pin, float clk_div) { 26 | pio_gpio_init(pio, data_pin); 27 | pio_gpio_init(pio, clk_pin); 28 | pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, true); 29 | pio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 1, true); 30 | pio_sm_config c = st7789_lcd_program_get_default_config(offset); 31 | sm_config_set_sideset_pins(&c, clk_pin); 32 | sm_config_set_out_pins(&c, data_pin, 1); 33 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 34 | sm_config_set_clkdiv(&c, clk_div); 35 | sm_config_set_out_shift(&c, false, true, 8); 36 | pio_sm_init(pio, sm, offset, &c); 37 | pio_sm_set_enabled(pio, sm, true); 38 | } 39 | 40 | // Making use of the narrow store replication behaviour on RP2040 to get the 41 | // data left-justified (as we are using shift-to-left to get MSB-first serial) 42 | 43 | static inline void st7789_lcd_put(PIO pio, uint sm, uint8_t x) { 44 | while (pio_sm_is_tx_fifo_full(pio, sm)) 45 | ; 46 | *(volatile uint8_t*)&pio->txf[sm] = x; 47 | } 48 | 49 | static inline void st7789_set_pixel_mode(PIO pio, uint sm, bool pixel_mode) { 50 | uint32_t shiftctrl = pio->sm[sm].shiftctrl; 51 | shiftctrl &= ~PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS; 52 | shiftctrl |= (pixel_mode ? 16u : 8u) << PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB; 53 | pio->sm[sm].shiftctrl = shiftctrl; 54 | } 55 | 56 | static inline void st7789_lcd_put_pixel(PIO pio, uint sm, uint16_t x) { 57 | while (pio_sm_is_tx_fifo_full(pio, sm)) 58 | ; 59 | *(volatile uint16_t*)&pio->txf[sm] = x; 60 | } 61 | 62 | // SM is done when it stalls on an empty FIFO 63 | static inline void st7789_lcd_wait_idle(PIO pio, uint sm) { 64 | uint32_t sm_stall_mask = 1u << (sm + PIO_FDEBUG_TXSTALL_LSB); 65 | pio->fdebug = sm_stall_mask; 66 | while (!(pio->fdebug & sm_stall_mask)) 67 | ; 68 | } 69 | %} 70 | -------------------------------------------------------------------------------- /drivers/tv-software/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(tv-software INTERFACE) 2 | 3 | target_sources(tv-software INTERFACE ${CMAKE_CURRENT_LIST_DIR}/tv-software.c) 4 | 5 | target_link_libraries(tv-software INTERFACE hardware_pio hardware_clocks hardware_dma) 6 | 7 | target_include_directories(tv-software INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR} 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /drivers/tv-software/tv-software.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "inttypes.h" 4 | #include "stdbool.h" 5 | 6 | #define PIO_VIDEO pio0 7 | 8 | #define TV_BASE_PIN (6) 9 | 10 | #define TEXTMODE_COLS 40 11 | #define TEXTMODE_ROWS 30 12 | #define RGB888(r, g, b) ((r<<16) | (g << 8 ) | b ) 13 | 14 | typedef enum g_out_TV_t { 15 | g_TV_OUT_PAL, 16 | g_TV_OUT_NTSC 17 | } g_out_TV_t; 18 | 19 | 20 | typedef enum NUM_TV_LINES_t { 21 | _624_lines, _625_lines, _524_lines, _525_lines, 22 | } NUM_TV_LINES_t; 23 | 24 | typedef enum COLOR_FREQ_t { 25 | _3579545, _4433619 26 | } COLOR_FREQ_t; 27 | 28 | // TODO: Сделать настраиваемо 29 | static const uint8_t textmode_palette[16] = { 30 | 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215 31 | }; 32 | static void graphics_set_flashmode(bool flash_line, bool flash_frame) { 33 | // dummy 34 | } 35 | 36 | static void graphics_set_bgcolor(uint32_t color888) { 37 | // dummy 38 | } 39 | -------------------------------------------------------------------------------- /drivers/tv/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(tv INTERFACE) 2 | 3 | target_sources(tv INTERFACE ${CMAKE_CURRENT_LIST_DIR}/tv.c) 4 | 5 | target_link_libraries(tv INTERFACE hardware_pio hardware_clocks hardware_dma) 6 | 7 | target_include_directories(tv INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR} 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /drivers/tv/tv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdbool.h" 4 | 5 | #define PIO_VIDEO pio0 6 | #define PIO_VIDEO_ADDR pio0 7 | 8 | #define TV_BASE_PIN (6) 9 | 10 | // TODO: Сделать настраиваемо 11 | static const uint8_t textmode_palette[16] = { 12 | 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215 13 | }; 14 | 15 | #define TEXTMODE_COLS 53 16 | #define TEXTMODE_ROWS 30 17 | 18 | #define RGB888(r, g, b) ((r<<16) | (g << 8 ) | b ) 19 | 20 | typedef enum { 21 | TV_OUT_PAL, 22 | TV_OUT_NTSC 23 | } output_format_e; 24 | 25 | 26 | static void graphics_set_flashmode(bool flash_line, bool flash_frame) { 27 | // dummy 28 | } 29 | 30 | static void graphics_set_bgcolor(uint32_t color888) { 31 | // dummy 32 | } 33 | -------------------------------------------------------------------------------- /drivers/usb/msc_disk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "bsp/board_api.h" 27 | #include "tusb.h" 28 | #include "usb.h" 29 | 30 | // whether host does safe-eject 31 | static bool ejectedDrv = false; 32 | 33 | // Invoked when received SCSI_CMD_INQUIRY 34 | // Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively 35 | void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { 36 | // char tmp[81]; sprintf(tmp, "tud_msc_inquiry_cb: %d", lun); logMsg(tmp); 37 | switch (lun) { 38 | case 3: { 39 | const char vid[] = "Pico SD-Card"; 40 | memcpy(vendor_id, vid, strlen(vid)); 41 | } 42 | break; 43 | } 44 | const char pid[] = "Mass Storage"; 45 | const char rev[] = "1.0"; 46 | memcpy(product_id, pid, strlen(pid)); 47 | memcpy(product_rev, rev, strlen(rev)); 48 | } 49 | 50 | // Invoked when received Test Unit Ready command. 51 | // return true allowing host to read/write this LUN e.g SD card inserted 52 | bool tud_msc_test_unit_ready_cb(uint8_t lun) { 53 | // char tmp[80]; sprintf(tmp, "tud_msc_test_unit_ready_cb(%d)", lun); logMsg(tmp); 54 | // RAM disk is ready until ejected 55 | if (ejectedDrv) { 56 | // Additional Sense 3A-00 is NOT_FOUND 57 | tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); 58 | return false; 59 | } 60 | return true; 61 | } 62 | 63 | inline bool tud_msc_ejected() { 64 | return ejectedDrv; 65 | } 66 | 67 | void set_tud_msc_ejected(bool v) { 68 | // char tmp[80]; sprintf(tmp, "set_tud_msc_ejected: %s", v ? "true" : "false"); logMsg(tmp); 69 | ejectedDrv = v; 70 | } 71 | 72 | // Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size 73 | // Application update block count and block size 74 | void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) { 75 | // char tmp[80]; sprintf(tmp, "tud_msc_capacity_cb(%d) block_count: %d block_size: %d r: %d", lun, block_count, block_size); logMsg(tmp); 76 | DWORD dw; 77 | DRESULT dio = disk_ioctl(0, GET_SECTOR_COUNT, &dw); 78 | if (dio == RES_OK) { 79 | *block_count = dw; 80 | } 81 | else { 82 | //char tmp[80]; sprintf(tmp, "disk_ioctl(GET_SECTOR_COUNT) failed: %d", dio); logMsg(tmp); 83 | *block_count = 0; 84 | return; 85 | } 86 | *block_size = FF_MAX_SS; 87 | } 88 | 89 | // Invoked when received Start Stop Unit command 90 | // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage 91 | // - Start = 1 : active mode, if load_eject = 1 : load disk storage 92 | bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { 93 | //char tmp[81]; sprintf(tmp, "power_condition: 0x%X start: %d load_eject: %d", power_condition, start, load_eject); logMsg(tmp); 94 | (void)lun; 95 | (void)power_condition; 96 | if (load_eject) { 97 | if (start) { 98 | // load disk storage 99 | } 100 | else { 101 | // unload disk storage 102 | ejectedDrv = true; 103 | } 104 | } 105 | return true; 106 | } 107 | 108 | // Callback invoked when received READ10 command. 109 | // Copy disk's data to buffer (up to bufsize) and return number of copied bytes. 110 | int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { 111 | // char tmp[80]; sprintf(tmp, "tud_msc_read10_cb(%d, %d, %d, %d)", lun, lba, offset, bufsize); logMsg(tmp); 112 | return disk_read(0, buffer, lba, 1) == RES_OK ? bufsize : -1; 113 | } 114 | 115 | inline static bool sd_card_writable() { 116 | DSTATUS ds = disk_status(0); 117 | DSTATUS rs = ds & 0x04/*STA_PROTECT*/; 118 | // char tmp[80]; sprintf(tmp, "tud_msc_is_writable_cb(1) ds: %d rs: %d r: %d", ds, rs, !rs); logMsg(tmp); 119 | return !rs; // TODO: sd-card write protected ioctl? 120 | } 121 | 122 | bool tud_msc_is_writable_cb(uint8_t lun) { 123 | return sd_card_writable(); 124 | } 125 | 126 | // Callback invoked when received WRITE10 command. 127 | // Process data in buffer to disk's storage and return number of written bytes 128 | int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { 129 | // char tmp[80]; sprintf(tmp, "tud_msc_write10_cb(%d, %d, %d, %d)", lun, lba, offset, bufsize); logMsg(tmp); 130 | return disk_write(0, buffer, lba, 1) == 0 ? bufsize : -1; 131 | } 132 | 133 | // Callback invoked when received an SCSI command not in built-in list below 134 | // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE 135 | // - READ10 and WRITE10 has their own callbacks 136 | int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { 137 | // char tmp[81]; sprintf(tmp, "scsi_cmd0(%d) 0x%X 1: 0x%X 2: 0x%X 3: 0x%X ...", lun, scsi_cmd[0], scsi_cmd[1], scsi_cmd[2], scsi_cmd[3]); logMsg(tmp); 138 | // read10 & write10 has their own callback and MUST not be handled here 139 | void const* response = NULL; 140 | int32_t resplen = 0; 141 | // most scsi handled is input 142 | bool in_xfer = true; 143 | switch (scsi_cmd[0]) { 144 | default: 145 | // Set Sense = Invalid Command Operation 146 | tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); 147 | // negative means error -> tinyusb could stall and/or response with failed status 148 | resplen = -1; 149 | break; 150 | } 151 | // return resplen must not larger than bufsize 152 | if (resplen > bufsize) resplen = bufsize; 153 | if (response && (resplen > 0)) { 154 | if (in_xfer) { 155 | memcpy(buffer, response, (size_t)resplen); 156 | } 157 | else { 158 | // SCSI output 159 | } 160 | } 161 | return (int32_t)resplen; 162 | } 163 | -------------------------------------------------------------------------------- /drivers/usb/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | // Enable Host stack 34 | #define CFG_TUH_ENABLED 0 35 | 36 | #if CFG_TUSB_MCU == OPT_MCU_RP2040 37 | // change to 1 if using pico-pio-usb as host controller for raspberry rp2040 38 | #define CFG_TUH_RPI_PIO_USB 0 39 | #define BOARD_TUH_RHPORT CFG_TUH_RPI_PIO_USB 40 | #endif 41 | 42 | 43 | // RHPort number used for host can be defined by board.mk, default to port 0 44 | #ifndef BOARD_TUH_RHPORT 45 | #define BOARD_TUH_RHPORT 0 46 | #endif 47 | 48 | // RHPort max operational speed can defined by board.mk 49 | #ifndef BOARD_TUH_MAX_SPEED 50 | #define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED 51 | #endif 52 | 53 | // Default is max speed that hardware controller could support with on-chip PHY 54 | #define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED 55 | 56 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 57 | * Tinyusb use follows macros to declare transferring memory so that they can be put 58 | * into those specific section. 59 | * e.g 60 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 61 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 62 | */ 63 | #ifndef CFG_TUH_MEM_SECTION 64 | #define CFG_TUH_MEM_SECTION 65 | #endif 66 | 67 | #ifndef CFG_TUH_MEM_ALIGN 68 | #define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4))) 69 | #endif 70 | 71 | //--------------------------------------------------------------------+ 72 | // Board Specific Configuration 73 | //--------------------------------------------------------------------+ 74 | #if CFG_TUSB_MCU == OPT_MCU_RP2040 75 | // change to 1 if using pico-pio-usb as host controller for raspberry rp2040 76 | #define CFG_TUH_RPI_PIO_USB 0 77 | #define BOARD_TUH_RHPORT CFG_TUH_RPI_PIO_USB 78 | #endif 79 | 80 | // RHPort number used for device can be defined by board.mk, default to port 0 81 | #ifndef BOARD_TUD_RHPORT 82 | #define BOARD_TUD_RHPORT 0 83 | #endif 84 | 85 | // RHPort max operational speed can defined by board.mk 86 | #ifndef BOARD_TUD_MAX_SPEED 87 | #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED 88 | #endif 89 | 90 | //-------------------------------------------------------------------- 91 | // Common Configuration 92 | //-------------------------------------------------------------------- 93 | 94 | // defined by compiler flags for flexibility 95 | #ifndef CFG_TUSB_MCU 96 | #error CFG_TUSB_MCU must be defined 97 | #endif 98 | 99 | #ifndef CFG_TUSB_OS 100 | #define CFG_TUSB_OS OPT_OS_NONE 101 | #endif 102 | 103 | #ifndef CFG_TUSB_DEBUG 104 | #define CFG_TUSB_DEBUG 0 105 | #endif 106 | 107 | // Enable Device stack 108 | #define CFG_TUD_ENABLED 1 109 | 110 | // Default is max speed that hardware controller could support with on-chip PHY 111 | #define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED 112 | 113 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 114 | * Tinyusb use follows macros to declare transferring memory so that they can be put 115 | * into those specific section. 116 | * e.g 117 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 118 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 119 | */ 120 | #ifndef CFG_TUSB_MEM_SECTION 121 | #define CFG_TUSB_MEM_SECTION 122 | #endif 123 | 124 | #ifndef CFG_TUSB_MEM_ALIGN 125 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 126 | #endif 127 | 128 | //-------------------------------------------------------------------- 129 | // DEVICE CONFIGURATION 130 | //-------------------------------------------------------------------- 131 | 132 | #ifndef CFG_TUD_ENDPOINT0_SIZE 133 | #define CFG_TUD_ENDPOINT0_SIZE 64 134 | #endif 135 | 136 | // Size of buffer to hold descriptors and other data used for enumeration 137 | #define CFG_TUH_ENUMERATION_BUFSIZE 256 138 | 139 | //------------- CLASS -------------// 140 | // as hub (host) 141 | #define CFG_TUH_HUB 0 // number of supported hubs 142 | #define CFG_TUH_MSC 0 143 | // as device 144 | #define CFG_TUD_CDC 1 145 | #define CFG_TUD_MSC 1 146 | #define CFG_TUD_HID 0 147 | #define CFG_TUD_MIDI 0 148 | #define CFG_TUD_VENDOR 0 149 | 150 | // max device support (excluding hub device): 1 hub typically has 4 ports 151 | #define CFG_TUH_DEVICE_MAX (3 * CFG_TUH_HUB + 1) 152 | //------------- MSC -------------// 153 | #define CFG_TUH_MSC_MAXLUN 4 // typical for most card reader 154 | 155 | // CDC FIFO size of TX and RX 156 | #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 157 | #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 158 | 159 | // CDC Endpoint transfer buffer size, more is faster 160 | #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 161 | 162 | // MSC Buffer size of Device Mass storage 163 | #define CFG_TUD_MSC_EP_BUFSIZE 512 164 | 165 | #ifdef __cplusplus 166 | } 167 | #endif 168 | 169 | #endif /* _TUSB_CONFIG_H_ */ 170 | -------------------------------------------------------------------------------- /drivers/usb/usb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "tusb.h" 31 | #include "bsp/board_api.h" 32 | #include "usb.h" 33 | 34 | //--------------------------------------------------------------------+ 35 | // MACRO CONSTANT TYPEDEF PROTYPES 36 | //--------------------------------------------------------------------+ 37 | 38 | /* Blink pattern 39 | * - 250 ms : device not mounted 40 | * - 1000 ms : device mounted 41 | * - 2500 ms : device is suspended 42 | */ 43 | enum { 44 | BLINK_NOT_MOUNTED = 250, 45 | BLINK_MOUNTED = 1000, 46 | BLINK_SUSPENDED = 2500, 47 | }; 48 | 49 | static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; 50 | 51 | void led_blinking_task(void); 52 | void cdc_task(void); 53 | 54 | /*------------- MAIN -------------*/ 55 | inline void init_pico_usb_drive() { 56 | set_tud_msc_ejected(false); 57 | board_init(); 58 | // init device stack on configured roothub port 59 | tud_init(BOARD_TUD_RHPORT); 60 | if (board_init_after_tusb) { 61 | board_init_after_tusb(); 62 | } 63 | } 64 | 65 | inline void pico_usb_drive_heartbeat() { 66 | tud_task(); // tinyusb device task 67 | led_blinking_task(); 68 | cdc_task(); 69 | } 70 | 71 | void in_flash_drive() { 72 | init_pico_usb_drive(); 73 | while(!tud_msc_ejected()) { 74 | pico_usb_drive_heartbeat(); 75 | //if_swap_drives(); 76 | } 77 | for (int i = 0; i < 10; ++i) { // sevaral hb till end of cycle, TODO: care eject 78 | pico_usb_drive_heartbeat(); 79 | sleep_ms(50); 80 | } 81 | } 82 | 83 | //--------------------------------------------------------------------+ 84 | // Device callbacks 85 | //--------------------------------------------------------------------+ 86 | // Invoked when device is mounted 87 | void tud_mount_cb(void) { 88 | blink_interval_ms = BLINK_MOUNTED; 89 | } 90 | 91 | // Invoked when device is unmounted 92 | void tud_umount_cb(void) { 93 | blink_interval_ms = BLINK_NOT_MOUNTED; 94 | } 95 | 96 | // Invoked when usb bus is suspended 97 | // remote_wakeup_en : if host allow us to perform remote wakeup 98 | // Within 7ms, device must draw an average of current less than 2.5 mA from bus 99 | void tud_suspend_cb(bool remote_wakeup_en) { 100 | (void) remote_wakeup_en; 101 | blink_interval_ms = BLINK_SUSPENDED; 102 | } 103 | 104 | // Invoked when usb bus is resumed 105 | void tud_resume_cb(void) { 106 | blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; 107 | } 108 | 109 | // Invoked to determine max LUN 110 | uint8_t tud_msc_get_maxlun_cb(void) { 111 | return 1; 112 | } 113 | 114 | //--------------------------------------------------------------------+ 115 | // USB CDC 116 | //--------------------------------------------------------------------+ 117 | void cdc_task(void) 118 | { 119 | // connected() check for DTR bit 120 | // Most but not all terminal client set this when making connection 121 | // if ( tud_cdc_connected() ) 122 | { 123 | // connected and there are data available 124 | if ( tud_cdc_available() ) 125 | { 126 | // read data 127 | char buf[64]; 128 | uint32_t count = tud_cdc_read(buf, sizeof(buf)); 129 | (void) count; 130 | 131 | // Echo back 132 | // Note: Skip echo by commenting out write() and write_flush() 133 | // for throughput test e.g 134 | // $ dd if=/dev/zero of=/dev/ttyACM0 count=10000 135 | tud_cdc_write(buf, count); 136 | tud_cdc_write_flush(); 137 | } 138 | } 139 | } 140 | 141 | // Invoked when cdc when line state changed e.g connected/disconnected 142 | void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) 143 | { 144 | (void) itf; 145 | (void) rts; 146 | 147 | // TODO set some indicator 148 | if ( dtr ) 149 | { 150 | // Terminal connected 151 | }else 152 | { 153 | // Terminal disconnected 154 | } 155 | } 156 | 157 | // Invoked when CDC interface received data from host 158 | void tud_cdc_rx_cb(uint8_t itf) 159 | { 160 | (void) itf; 161 | } 162 | 163 | //--------------------------------------------------------------------+ 164 | // BLINKING TASK 165 | //--------------------------------------------------------------------+ 166 | void led_blinking_task(void) 167 | { 168 | static uint32_t start_ms = 0; 169 | static bool led_state = false; 170 | 171 | // Blink every interval ms 172 | if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time 173 | start_ms += blink_interval_ms; 174 | 175 | board_led_write(led_state); 176 | led_state = 1 - led_state; // toggle 177 | } 178 | -------------------------------------------------------------------------------- /drivers/usb/usb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef PICO_USB_DRIVE 3 | #define PICO_USB_DRIVE 4 | 5 | #include "ff.h" 6 | #include "diskio.h" 7 | 8 | void init_pico_usb_drive(); 9 | void pico_usb_drive_heartbeat(); 10 | 11 | // msc_disk.c 12 | bool tud_msc_ejected(); 13 | void set_tud_msc_ejected(bool v); 14 | 15 | enum { 16 | DISK_BLOCK_SIZE = 512, 17 | FAT_OFFSET = 0x1000 18 | }; 19 | int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize); 20 | void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /drivers/usb/usb_descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "bsp/board_api.h" 27 | #include "tusb.h" 28 | 29 | /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. 30 | * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. 31 | * 32 | * Auto ProductID layout's Bitmap: 33 | * [MSB] HID | MSC | CDC [LSB] 34 | */ 35 | #define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) 36 | #define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ 37 | _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) 38 | 39 | #define USB_VID 0xCafe 40 | #define USB_BCD 0x0200 41 | 42 | //--------------------------------------------------------------------+ 43 | // Device Descriptors 44 | //--------------------------------------------------------------------+ 45 | tusb_desc_device_t const desc_device = { 46 | .bLength = sizeof(tusb_desc_device_t), 47 | .bDescriptorType = TUSB_DESC_DEVICE, 48 | .bcdUSB = USB_BCD, 49 | 50 | // Use Interface Association Descriptor (IAD) for CDC 51 | // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) 52 | .bDeviceClass = TUSB_CLASS_MISC, 53 | .bDeviceSubClass = MISC_SUBCLASS_COMMON, 54 | .bDeviceProtocol = MISC_PROTOCOL_IAD, 55 | 56 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 57 | 58 | .idVendor = USB_VID, 59 | .idProduct = USB_PID, 60 | .bcdDevice = 0x0100, 61 | 62 | .iManufacturer = 0x01, 63 | .iProduct = 0x02, 64 | .iSerialNumber = 0x03, 65 | 66 | .bNumConfigurations = 0x01 67 | }; 68 | 69 | // Invoked when received GET DEVICE DESCRIPTOR 70 | // Application return pointer to descriptor 71 | uint8_t const *tud_descriptor_device_cb(void) { 72 | return (uint8_t const *) &desc_device; 73 | } 74 | 75 | //--------------------------------------------------------------------+ 76 | // Configuration Descriptor 77 | //--------------------------------------------------------------------+ 78 | 79 | enum { 80 | ITF_NUM_CDC = 0, 81 | ITF_NUM_CDC_DATA, 82 | ITF_NUM_MSC, 83 | ITF_NUM_TOTAL 84 | }; 85 | 86 | #define EPNUM_CDC_NOTIF 0x81 87 | #define EPNUM_CDC_OUT 0x02 88 | #define EPNUM_CDC_IN 0x82 89 | #define EPNUM_MSC_OUT 0x03 90 | #define EPNUM_MSC_IN 0x83 91 | 92 | #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN) 93 | 94 | // full speed configuration 95 | uint8_t const desc_fs_configuration[] = { 96 | // Config number, interface count, string index, total length, attribute, power in mA 97 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), 98 | // Interface number, string index, EP notification address and size, EP data address (out, in) and size. 99 | TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), 100 | // Interface number, string index, EP Out & EP In address, EP size 101 | TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), 102 | }; 103 | 104 | #if TUD_OPT_HIGH_SPEED 105 | // Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration 106 | 107 | // high speed configuration 108 | uint8_t const desc_hs_configuration[] = { 109 | // Config number, interface count, string index, total length, attribute, power in mA 110 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), 111 | 112 | // Interface number, string index, EP notification address and size, EP data address (out, in) and size. 113 | TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512), 114 | 115 | // Interface number, string index, EP Out & EP In address, EP size 116 | TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512), 117 | }; 118 | 119 | // other speed configuration 120 | uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; 121 | 122 | // device qualifier is mostly similar to device descriptor since we don't change configuration based on speed 123 | tusb_desc_device_qualifier_t const desc_device_qualifier = { 124 | .bLength = sizeof(tusb_desc_device_qualifier_t), 125 | .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, 126 | .bcdUSB = USB_BCD, 127 | 128 | .bDeviceClass = TUSB_CLASS_MISC, 129 | .bDeviceSubClass = MISC_SUBCLASS_COMMON, 130 | .bDeviceProtocol = MISC_PROTOCOL_IAD, 131 | 132 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 133 | .bNumConfigurations = 0x01, 134 | .bReserved = 0x00 135 | }; 136 | 137 | // Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request 138 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. 139 | // device_qualifier descriptor describes information about a high-speed capable device that would 140 | // change if the device were operating at the other speed. If not highspeed capable stall this request. 141 | uint8_t const *tud_descriptor_device_qualifier_cb(void) { 142 | return (uint8_t const *) &desc_device_qualifier; 143 | } 144 | 145 | // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request 146 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 147 | // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa 148 | uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { 149 | (void) index; // for multiple configurations 150 | 151 | // if link speed is high return fullspeed config, and vice versa 152 | // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG 153 | memcpy(desc_other_speed_config, 154 | (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration, 155 | CONFIG_TOTAL_LEN); 156 | 157 | desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; 158 | 159 | return desc_other_speed_config; 160 | } 161 | 162 | #endif // highspeed 163 | 164 | 165 | // Invoked when received GET CONFIGURATION DESCRIPTOR 166 | // Application return pointer to descriptor 167 | // Descriptor contents must exist long enough for transfer to complete 168 | uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { 169 | (void) index; // for multiple configurations 170 | //char tmp[81]; sprintf(tmp, "tud_descriptor_configuration_cb(%d)", index); logMsg(tmp); 171 | #if TUD_OPT_HIGH_SPEED 172 | // Although we are highspeed, host may be fullspeed. 173 | return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; 174 | #else 175 | return desc_fs_configuration; 176 | #endif 177 | } 178 | 179 | //--------------------------------------------------------------------+ 180 | // String Descriptors 181 | //--------------------------------------------------------------------+ 182 | 183 | // String Descriptor Index 184 | enum { 185 | STRID_LANGID = 0, 186 | STRID_MANUFACTURER, 187 | STRID_PRODUCT, 188 | STRID_SERIAL, 189 | }; 190 | 191 | // array of pointer to string descriptors 192 | char const *string_desc_arr[] = { 193 | (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) 194 | "TinyUSB", // 1: Manufacturer 195 | "TinyUSB Device", // 2: Product 196 | NULL, // 3: Serials will use unique ID if possible 197 | "TinyUSB CDC", // 4: CDC Interface 198 | "TinyUSB MSC", // 5: MSC Interface 199 | }; 200 | 201 | static uint16_t _desc_str[32 + 1]; 202 | 203 | // Invoked when received GET STRING DESCRIPTOR request 204 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 205 | uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { 206 | (void) langid; 207 | size_t chr_count; 208 | //char tmp[81]; sprintf(tmp, "tud_descriptor_string_cb(%d, %d)", index, langid); logMsg(tmp); 209 | switch ( index ) { 210 | case STRID_LANGID: 211 | memcpy(&_desc_str[1], string_desc_arr[0], 2); 212 | chr_count = 1; 213 | break; 214 | 215 | case STRID_SERIAL: 216 | chr_count = board_usb_get_serial(_desc_str + 1, 32); 217 | break; 218 | 219 | default: 220 | // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. 221 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors 222 | 223 | if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL; 224 | 225 | const char *str = string_desc_arr[index]; 226 | 227 | // Cap at max char 228 | chr_count = strlen(str); 229 | size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type 230 | if ( chr_count > max_count ) chr_count = max_count; 231 | 232 | // Convert ASCII string into UTF-16 233 | for ( size_t i = 0; i < chr_count; i++ ) { 234 | _desc_str[1 + i] = str[i]; 235 | } 236 | break; 237 | } 238 | 239 | // first byte is length (including header), second byte is string type 240 | _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); 241 | 242 | return _desc_str; 243 | } 244 | -------------------------------------------------------------------------------- /drivers/usbfs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeLists.txt for usbfs - part of the PicoW C/C++ Boilerplate Project 2 | # 3 | # This library uses FatFS (http://elm-chan.org/fsw/ff/00index_e.html) to provide 4 | # a mountable USB filesystem to Pico C/C++ SDK based projects. 5 | # 6 | # Although developed for the PicoW C++ Boilerplate, this can be easily dropped 7 | # into your own projects - see the documentation for more details. 8 | # 9 | # Copyright (C) 2023 Pete Favelle 10 | # This file is released under the BSD 3-Clause License; see LICENSE for details. 11 | 12 | add_library(usbfs 13 | # First, the source for the FatFS library. 14 | ${CMAKE_CURRENT_LIST_DIR}/diskio.c 15 | ${CMAKE_CURRENT_LIST_DIR}/ff.c 16 | ${CMAKE_CURRENT_LIST_DIR}/ffunicode.c 17 | 18 | # Next, the interfaces for TinyUSB 19 | ${CMAKE_CURRENT_LIST_DIR}/storage.c 20 | ${CMAKE_CURRENT_LIST_DIR}/usb.c 21 | ${CMAKE_CURRENT_LIST_DIR}/usb_descriptors.c 22 | 23 | # And lastly, the user-facing routines 24 | ${CMAKE_CURRENT_LIST_DIR}/usbfs.c 25 | ) 26 | 27 | # Make our header file(s) accessible both within and external to the library. 28 | target_include_directories(usbfs PUBLIC ${CMAKE_CURRENT_LIST_DIR}) 29 | 30 | # Specify the Pico C/C++ SDK libraries we need 31 | target_link_libraries(usbfs 32 | pico_stdlib pico_unique_id 33 | hardware_flash tinyusb_device 34 | ) -------------------------------------------------------------------------------- /drivers/usbfs/README.md: -------------------------------------------------------------------------------- 1 | USBFS 2 | ===== 3 | 4 | This is a library for providing an easy-to-use USB mounted drive to a Pico 5 | based application. The 'drive' is accessible via `fopen`-like functions, as 6 | well as from the host machine. 7 | 8 | It is designed to provide a relatively easy way to provide configuration 9 | details to an application (such as WiFi details) that would otherwise require 10 | recompilation and redeployment. 11 | 12 | The library is written in pure C, so can be safely used with C or C++ based 13 | projects. 14 | 15 | 16 | Usage 17 | ----- 18 | 19 | The library is baked into the [Picow C/C++ Boilerplate Project](https://github.com/ahnlak/picow-boilerplate), 20 | however it's possible to simply copy the entire `usbfs` directory into your 21 | own project, and include it in your `CMakeList.txt` file, and then simply 22 | `#include "usbfs.h"`: 23 | 24 | ``` 25 | add_subdirectory(usbfs) 26 | target_link_libraries(${NAME} 27 | usbfs 28 | ) 29 | ``` 30 | 31 | (where `${NAME}` is the filename of your project). 32 | 33 | 34 | Functions 35 | --------- 36 | 37 | `usbfs_init()` should be called at your program's startup. It initialises the 38 | USB interface (via TinyUSB), as well as the filesystem. 39 | 40 | `usbfs_update()` should be called regularly, in your main loop. USB processing 41 | is polled rather than run in the background, so this ensures that any required 42 | work is performed in a timely manner. 43 | 44 | `usbfs_sleep_ms()` is a drop-in replacement for `sleep_ms()`, which will call 45 | `usbfs_update()` for you on a regular basis during your sleep. If you are using 46 | this to manage the timing of your main loop, there is no need to call `usbfs_update()` 47 | directly. 48 | 49 | `usbfs_open()`, `usbfs_close()`, `usbfs_read()`, `usbfs_write()`, `usbfs_gets()` 50 | and `usbfs_puts()` are essentially the equivalents of the standard C equivalents, 51 | to allow you to interract with files stored on this filesystem. 52 | 53 | `usbfs_timestamp()` returns the modification date/time of the named file, to make 54 | it easy to detect changes. 55 | 56 | 57 | Caveats 58 | ------- 59 | 60 | Due to the limitations of FAT (related to sector size and count), this library 61 | requires the use of the top 512kb of flash memory. This is probably worth 62 | keeping in mind if you have a large application. 63 | -------------------------------------------------------------------------------- /drivers/usbfs/diskio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usbfs/diskio.cpp - part of the PicoW C/C++ Boilerplate Project 3 | * 4 | * These functions provide callbacks for FatFS to talk to our storage layer. 5 | * 6 | * Copyright (C) 2023 Pete Favelle 7 | * This file is released under the BSD 3-Clause License; see LICENSE for details. 8 | */ 9 | 10 | /* System headers. */ 11 | 12 | #include 13 | #include 14 | 15 | #include "pico/stdlib.h" 16 | 17 | /* Local headers. */ 18 | 19 | #include "ff.h" 20 | #include "diskio.h" 21 | #include "usbfs.h" 22 | 23 | 24 | /* Functions.*/ 25 | 26 | 27 | /* 28 | * disk_initialize - called to initializes the storage device. 29 | */ 30 | 31 | DSTATUS disk_initialize( BYTE pdrv ) 32 | { 33 | /* Our storage is always initialized. */ 34 | return RES_OK; 35 | } 36 | 37 | 38 | /* 39 | * disk_status - called to inquire the current drive status. 40 | */ 41 | 42 | DSTATUS disk_status( BYTE pdrv ) 43 | { 44 | /* Our storage is always available. */ 45 | return RES_OK; 46 | } 47 | 48 | 49 | /* 50 | * disk_read - called to read data from the storage device. 51 | */ 52 | 53 | DRESULT disk_read( BYTE pdrv, BYTE *buff, LBA_t sector, UINT count ) 54 | { 55 | int32_t l_bytecount; 56 | 57 | /* Make sure that we have a fixed sector size. */ 58 | static_assert( FF_MIN_SS == FF_MAX_SS, "FatFS Sector Size not fixed!" ); 59 | 60 | /* Ask the storage layer to perform the read. */ 61 | l_bytecount = storage_read( sector, 0, buff, FF_MIN_SS*count ); 62 | 63 | /* Check that we wrote as much data as expected. */ 64 | if ( l_bytecount != FF_MIN_SS*count ) 65 | { 66 | return RES_ERROR; 67 | } 68 | 69 | /* All went well then. */ 70 | return RES_OK; 71 | } 72 | 73 | 74 | /* 75 | * disk_write - called to write data to the storage device. 76 | */ 77 | 78 | DRESULT disk_write( BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count ) 79 | { 80 | int32_t l_bytecount; 81 | 82 | /* Make sure that we have a fixed sector size. */ 83 | static_assert( FF_MIN_SS == FF_MAX_SS, "FatFS Sector Size not fixed!" ); 84 | 85 | /* Ask the storage layer to perform the write. */ 86 | l_bytecount = storage_write( sector, 0, buff, FF_MIN_SS*count ); 87 | 88 | /* Check that we wrote as much data as expected. */ 89 | if ( l_bytecount != FF_MIN_SS*count ) 90 | { 91 | return RES_ERROR; 92 | } 93 | 94 | /* All went well then. */ 95 | return RES_OK; 96 | } 97 | 98 | 99 | /* 100 | * disk_ioctl - called to control device specific features and miscellaneous 101 | * functions other than generic read/write. 102 | */ 103 | 104 | DRESULT disk_ioctl( BYTE pdrv, BYTE cmd, void* buff ) 105 | { 106 | uint16_t block_size; 107 | uint32_t num_blocks; 108 | 109 | /* Handle each command as required. */ 110 | switch(cmd) 111 | { 112 | case CTRL_SYNC: 113 | /* We have no cache, so we're always synced. */ 114 | return RES_OK; 115 | 116 | case GET_SECTOR_COUNT: 117 | /* Just ask the storage layer for this data. */ 118 | storage_get_size( &block_size, &num_blocks ); 119 | *(LBA_t *)buff = num_blocks; 120 | return RES_OK; 121 | 122 | case GET_BLOCK_SIZE: 123 | *(DWORD *)buff = 1; 124 | return RES_OK; 125 | } 126 | 127 | /* Any other command we receive is an error as we do not handle it. */ 128 | return RES_PARERR; 129 | } 130 | 131 | /* End of file usbfs/diskio.cpp */ 132 | -------------------------------------------------------------------------------- /drivers/usbfs/diskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface module include file (C)ChaN, 2019 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /* Status of Disk Functions */ 13 | typedef BYTE DSTATUS; 14 | 15 | /* Results of Disk Functions */ 16 | typedef enum { 17 | RES_OK = 0, /* 0: Successful */ 18 | RES_ERROR, /* 1: R/W Error */ 19 | RES_WRPRT, /* 2: Write Protected */ 20 | RES_NOTRDY, /* 3: Not Ready */ 21 | RES_PARERR /* 4: Invalid Parameter */ 22 | } DRESULT; 23 | 24 | 25 | /*---------------------------------------*/ 26 | /* Prototypes for disk control functions */ 27 | 28 | 29 | DSTATUS disk_initialize (BYTE pdrv); 30 | DSTATUS disk_status (BYTE pdrv); 31 | DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); 32 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count); 33 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); 34 | 35 | 36 | /* Disk Status Bits (DSTATUS) */ 37 | 38 | #define STA_NOINIT 0x01 /* Drive not initialized */ 39 | #define STA_NODISK 0x02 /* No medium in the drive */ 40 | #define STA_PROTECT 0x04 /* Write protected */ 41 | 42 | 43 | /* Command code for disk_ioctrl fucntion */ 44 | 45 | /* Generic command (Used by FatFs) */ 46 | #define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */ 47 | #define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */ 48 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */ 49 | #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */ 50 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ 51 | 52 | /* Generic command (Not used by FatFs) */ 53 | #define CTRL_POWER 5 /* Get/Set power status */ 54 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 55 | #define CTRL_EJECT 7 /* Eject media */ 56 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 57 | 58 | /* MMC/SDC specific ioctl command */ 59 | #define MMC_GET_TYPE 10 /* Get card type */ 60 | #define MMC_GET_CSD 11 /* Get CSD */ 61 | #define MMC_GET_CID 12 /* Get CID */ 62 | #define MMC_GET_OCR 13 /* Get OCR */ 63 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 64 | #define ISDIO_READ 55 /* Read data form SD iSDIO register */ 65 | #define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ 66 | #define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ 67 | 68 | /* ATA/CF specific ioctl command */ 69 | #define ATA_GET_REV 20 /* Get F/W revision */ 70 | #define ATA_GET_MODEL 21 /* Get model name */ 71 | #define ATA_GET_SN 22 /* Get serial number */ 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /drivers/usbfs/storage.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usbfs/storage.cpp - part of the PicoW C/C++ Boilerplate Project 3 | * 4 | * These functions provide a common storage backend, to be used both by TinyUSB 5 | * and FatFS. The storage is simply a small section of flash at the end of memory. 6 | * 7 | * We allocated 128 sectors (~512kb) of storage, which the minimum that FatFS 8 | * will allow us to use. This takes quite a chunk out of the Pico flash, but it 9 | * is what it is. 10 | * 11 | * Copyright (C) 2023 Pete Favelle 12 | * This file is released under the BSD 3-Clause License; see LICENSE for details. 13 | */ 14 | 15 | /* System headers. */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "hardware/flash.h" 22 | #include "hardware/sync.h" 23 | 24 | 25 | /* Local headers. */ 26 | 27 | #include "usbfs.h" 28 | 29 | 30 | /* Module variables. */ 31 | 32 | static const uint32_t m_storage_size = FLASH_SECTOR_SIZE * 128; 33 | static const uint32_t m_storage_offset = PICO_FLASH_SIZE_BYTES - m_storage_size; 34 | 35 | 36 | /* Functions.*/ 37 | 38 | /* 39 | * get_size - provides size information about storage. 40 | */ 41 | 42 | void storage_get_size( uint16_t *p_block_size, uint32_t *p_num_blocks ) 43 | { 44 | /* Very simple look up. */ 45 | *p_block_size = FLASH_SECTOR_SIZE; 46 | *p_num_blocks = m_storage_size / FLASH_SECTOR_SIZE; 47 | 48 | /* All done. */ 49 | return; 50 | } 51 | 52 | 53 | /* 54 | * read - fetches data from flash. 55 | */ 56 | 57 | int32_t storage_read( uint32_t p_sector, uint32_t p_offset, 58 | void *p_buffer, uint32_t p_size_bytes ) 59 | { 60 | /* Very simple copy out of flash then! */ 61 | memcpy( 62 | p_buffer, 63 | (uint8_t *)XIP_NOCACHE_NOALLOC_BASE + m_storage_offset + p_sector * FLASH_SECTOR_SIZE + p_offset, 64 | p_size_bytes 65 | ); 66 | 67 | /* And return how much we read. */ 68 | return p_size_bytes; 69 | } 70 | 71 | 72 | /* 73 | * write - stores data into flash, with appropriate guards. 74 | */ 75 | 76 | int32_t storage_write( uint32_t p_sector, uint32_t p_offset, 77 | const uint8_t *p_buffer, uint32_t p_size_bytes ) 78 | { 79 | uint32_t l_status; 80 | 81 | /* Don't want to be interrupted. */ 82 | l_status = save_and_disable_interrupts(); 83 | 84 | /* Erasing with an offset of 0? Seems odd, but... */ 85 | if ( p_offset == 0 ) 86 | { 87 | flash_range_erase( m_storage_offset + p_sector * FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE ); 88 | } 89 | 90 | /* And just write the data now. */ 91 | flash_range_program( 92 | m_storage_offset + p_sector * FLASH_SECTOR_SIZE + p_offset, 93 | p_buffer, p_size_bytes 94 | ); 95 | 96 | /* Lastly, restore our interrupts. */ 97 | restore_interrupts( l_status ); 98 | 99 | /* Before returning the amount of data written. */ 100 | return p_size_bytes; 101 | } 102 | 103 | 104 | /* End of file usbfs/storage.cpp */ 105 | -------------------------------------------------------------------------------- /drivers/usbfs/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //-------------------------------------------------------------------- 34 | // COMMON CONFIGURATION 35 | //-------------------------------------------------------------------- 36 | 37 | // defined by board.mk 38 | #ifndef CFG_TUSB_MCU 39 | #error CFG_TUSB_MCU must be defined 40 | #endif 41 | 42 | // RHPort number used for device can be defined by board.mk, default to port 0 43 | #ifndef BOARD_DEVICE_RHPORT_NUM 44 | #define BOARD_DEVICE_RHPORT_NUM 0 45 | #endif 46 | 47 | // RHPort max operational speed can defined by board.mk 48 | // Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed 49 | #ifndef BOARD_DEVICE_RHPORT_SPEED 50 | #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ 51 | CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56) 52 | #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED 53 | #else 54 | #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED 55 | #endif 56 | #endif 57 | 58 | // Device mode with rhport and speed defined by board.mk 59 | #if BOARD_DEVICE_RHPORT_NUM == 0 60 | #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) 61 | #elif BOARD_DEVICE_RHPORT_NUM == 1 62 | #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) 63 | #else 64 | #error "Incorrect RHPort configuration" 65 | #endif 66 | 67 | // This example doesn't use an RTOS 68 | #ifndef CFG_TUSB_OS 69 | #define CFG_TUSB_OS OPT_OS_NONE 70 | #endif 71 | 72 | // CFG_TUSB_DEBUG is defined by compiler in DEBUG build 73 | // #define CFG_TUSB_DEBUG 0 74 | 75 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 76 | * Tinyusb use follows macros to declare transferring memory so that they can be put 77 | * into those specific section. 78 | * e.g 79 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 80 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 81 | */ 82 | #ifndef CFG_TUSB_MEM_SECTION 83 | #define CFG_TUSB_MEM_SECTION 84 | #endif 85 | 86 | #ifndef CFG_TUSB_MEM_ALIGN 87 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 88 | #endif 89 | 90 | //-------------------------------------------------------------------- 91 | // DEVICE CONFIGURATION 92 | //-------------------------------------------------------------------- 93 | 94 | #ifndef CFG_TUD_ENDPOINT0_SIZE 95 | #define CFG_TUD_ENDPOINT0_SIZE 64 96 | #endif 97 | 98 | //------------- CLASS -------------// 99 | #define CFG_TUD_CDC 1 100 | #define CFG_TUD_MSC 1 101 | #define CFG_TUD_HID 0 102 | #define CFG_TUD_MIDI 0 103 | #define CFG_TUD_VENDOR 0 104 | 105 | // CDC FIFO size of TX and RX 106 | #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 107 | #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 108 | 109 | // CDC Endpoint transfer buffer size, more is faster 110 | #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 111 | 112 | // MSC Buffer size of Device Mass storage 113 | #define CFG_TUD_MSC_EP_BUFSIZE 512 114 | 115 | #ifdef __cplusplus 116 | } 117 | #endif 118 | 119 | #endif /* _TUSB_CONFIG_H_ */ 120 | -------------------------------------------------------------------------------- /drivers/usbfs/usb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usbfs/usb.cpp - part of the PicoW C/C++ Boilerplate Project 3 | * 4 | * These functions are mostly the callbacks required by TinyUSB; it is largely 5 | * culled from DaftFreak's awesome work on the PicoSystem. 6 | * 7 | * Copyright (C) 2023 Pete Favelle 8 | * This file is released under the BSD 3-Clause License; see LICENSE for details. 9 | */ 10 | 11 | 12 | /* System headers. */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | /* Local headers. */ 21 | 22 | #include "tusb.h" 23 | #include "usbfs.h" 24 | 25 | 26 | /* Module variables. */ 27 | 28 | static bool m_mounted = true; 29 | static bool m_fs_changed = false; 30 | 31 | 32 | /* Functions.*/ 33 | 34 | /* 35 | * usb_set_fs_changed - sets the flag to indicate that a file has been locally 36 | * changed; in turn, this ensures that the host is told 37 | * that a re-scan is required. 38 | */ 39 | 40 | void usb_set_fs_changed( void ) 41 | { 42 | /* Very simple flag set. */ 43 | m_fs_changed = true; 44 | return; 45 | } 46 | 47 | 48 | /* 49 | * tud_mount_cb - Invoked when device is mounted (configured) 50 | */ 51 | 52 | void tud_mount_cb( void ) 53 | { 54 | /* All we really do is remember that we're mounted. */ 55 | m_mounted = true; 56 | return; 57 | } 58 | 59 | 60 | /* 61 | * tud_umount_cb - Invoked when device is unmounted 62 | */ 63 | 64 | void tud_umount_cb( void ) 65 | { 66 | /* As before, just remember this. */ 67 | m_mounted = false; 68 | return; 69 | } 70 | 71 | 72 | /* Mass Storage (MSC) callbacks. */ 73 | 74 | /* 75 | * tud_msc_read10_cb - Invoked when received SCSI READ10 command 76 | * 77 | * Fill the buffer (up to bufsize) with address contents and return number of 78 | * read bytes. 79 | * 80 | * If read < bufsize, these bytes are transferred first and callback invoked 81 | * again for remaining data. 82 | * 83 | * If read == 0, it indicates we're not ready yet e.g disk I/O busy. Callback 84 | * will be invoked again with the same parameters later on. 85 | * 86 | * If read < 0, indicates an error e.g invalid address. This request will be 87 | * STALLed and return failed status in command status wrapper phase. 88 | */ 89 | 90 | int32_t tud_msc_read10_cb( uint8_t p_lun, uint32_t p_lba, 91 | uint32_t p_offset, void *p_buffer, 92 | uint32_t p_bufsize ) 93 | { 94 | /* We simply pass on this request to the storage layer. */ 95 | return storage_read( p_lba, p_offset, p_buffer, p_bufsize ); 96 | } 97 | 98 | 99 | /* 100 | * tud_msc_write10_cd - Invoked when received SCSI WRITE10 command 101 | * 102 | * Write data from buffer to address (up to bufsize) and return number of 103 | * written bytes. 104 | * 105 | * If write < bufsize, callback invoked again with remaining data later on. 106 | * 107 | * If write == 0, it indicates we're not ready yet e.g disk I/O busy. 108 | * Callback will be invoked again with the same parameters later on. 109 | * 110 | * If write < 0, it indicates an error e.g invalid address. This request will 111 | * be STALLed and return failed status in command status wrapper phase. 112 | */ 113 | 114 | int32_t tud_msc_write10_cb( uint8_t p_lun, uint32_t p_lba, 115 | uint32_t p_offset, uint8_t *p_buffer, 116 | uint32_t p_bufsize ) 117 | { 118 | /* We simply pass on this request to the storage layer. */ 119 | return storage_write( p_lba, p_offset, p_buffer, p_bufsize ); 120 | } 121 | 122 | 123 | /* 124 | * tud_msc_inquiry_cb - Invoked when received SCSI_CMD_INQUIRY 125 | * 126 | * Return the vendor, product and revision strings in the provider buffers. 127 | */ 128 | 129 | void tud_msc_inquiry_cb( uint8_t p_lun, uint8_t p_vendor_id[8], 130 | uint8_t p_product_id[16], uint8_t p_product_rev[4] ) 131 | { 132 | const char *l_vid = USB_VENDOR_STR; 133 | const char *l_pid = USB_PRODUCT_STR " Storage"; 134 | const char *l_rev = "1.0"; 135 | 136 | memcpy( p_vendor_id , l_vid, strlen(l_vid) ); 137 | memcpy( p_product_id , l_pid, strlen(l_pid) ); 138 | memcpy( p_product_rev, l_rev, strlen(l_rev) ); 139 | } 140 | 141 | 142 | /* 143 | * tud_msc_test_unit_ready_cb - Invoked when received Test Unit Ready command. 144 | * 145 | * If we have made local changes to files, we raise a SENSE_NOT_READY and return 146 | * false to indicate that the host needs to re-scan. Otherwise, true indicates 147 | * that all is well. 148 | */ 149 | 150 | bool tud_msc_test_unit_ready_cb( uint8_t p_lun ) 151 | { 152 | /* If files have changed locally, or if we're not mounted, we're not ready. */ 153 | if( m_fs_changed || !m_mounted ) 154 | { 155 | tud_msc_set_sense( p_lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00 ); 156 | m_fs_changed = false; 157 | return false; 158 | } 159 | 160 | /* Otherwise, we are. */ 161 | return true; 162 | } 163 | 164 | 165 | /* 166 | * tud_msc_capacity_cb - Invoked when received SCSI_CMD_READ_CAPACITY_10 and 167 | * SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size. 168 | * 169 | * Simply return information about our block size and counts. 170 | */ 171 | 172 | void tud_msc_capacity_cb( uint8_t p_lun, 173 | uint32_t *p_block_count, uint16_t *p_block_size ) 174 | { 175 | /* This is a question for the storage layer. */ 176 | storage_get_size( p_block_size, p_block_count ); 177 | return; 178 | } 179 | 180 | 181 | /* 182 | * tud_msc_scsi_cb - Invoked when receivind a SCSI command not handled by its 183 | * own callback. 184 | * 185 | * Returns the actual bytes processed, can be zero for no-data command. 186 | * A negative return indicates error e.g unsupported command, tinyusb will STALL 187 | * the corresponding endpoint and return failed status in command status wrapper phase. 188 | */ 189 | 190 | int32_t tud_msc_scsi_cb( uint8_t p_lun, uint8_t const p_scsi_cmd[16], 191 | void *p_buffer, uint16_t p_bufsize ) 192 | { 193 | int32_t l_retval = 0; 194 | 195 | /* Decide what we're doing based on the SCSI command. */ 196 | switch ( p_scsi_cmd[0] ) 197 | { 198 | /* This is fine. */ 199 | case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: 200 | l_retval = 0; 201 | break; 202 | 203 | /* By default, send an ILLEGAL_REQUEST back, and fail. */ 204 | default: 205 | tud_msc_set_sense( p_lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00 ); 206 | l_retval = -1; 207 | break; 208 | } 209 | 210 | /* Return the appopriate code. */ 211 | return l_retval; 212 | } 213 | 214 | 215 | /* 216 | * tud_msc_start_stop_cb - Invoked when received Start Stop Unit command 217 | * 218 | * start = 0 : stopped power mode, if load_eject = 1 : unload disk storage 219 | * start = 1 : active mode, if load_eject = 1 : load disk storage 220 | */ 221 | 222 | bool tud_msc_start_stop_cb( uint8_t p_lun, uint8_t p_power_condition, 223 | bool p_start, bool p_load_eject ) 224 | { 225 | /* If the load_eject flag isn't set, we don't need to do anything. */ 226 | if( p_load_eject ) 227 | { 228 | /* If not starting, we're unloading our drive - flag it as unmounted. */ 229 | if ( !p_start ) 230 | { 231 | m_mounted = false; 232 | } 233 | } 234 | 235 | /* All is well. */ 236 | return true; 237 | } 238 | 239 | 240 | /* 241 | * tud_msc_is_writable_cb - Invoked to check if device is writable 242 | */ 243 | 244 | bool tud_msc_is_writable_cb( uint8_t p_lun ) 245 | { 246 | /* We're always writable. */ 247 | return true; 248 | } 249 | 250 | 251 | /* Communications Device (CDC) callbacks. */ 252 | 253 | /* We don't implement any of these, leaving them to the Pico stdio handling. */ 254 | 255 | 256 | /* End of file usbfs/usb.cpp */ 257 | -------------------------------------------------------------------------------- /drivers/usbfs/usb_descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "tusb.h" 27 | #include "usbfs.h" 28 | #include "pico/unique_id.h" 29 | 30 | /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. 31 | * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. 32 | * 33 | * Auto ProductID layout's Bitmap: 34 | * [MSB] HID | MSC | CDC [LSB] 35 | */ 36 | #ifndef USB_PRODUCT_ID 37 | #define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) 38 | #define USB_PRODUCT_ID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ 39 | _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) 40 | #endif 41 | 42 | //--------------------------------------------------------------------+ 43 | // Device Descriptors 44 | //--------------------------------------------------------------------+ 45 | tusb_desc_device_t const desc_device = 46 | { 47 | .bLength = sizeof(tusb_desc_device_t), 48 | .bDescriptorType = TUSB_DESC_DEVICE, 49 | .bcdUSB = 0x0200, 50 | 51 | // Use Interface Association Descriptor (IAD) for CDC 52 | // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) 53 | .bDeviceClass = TUSB_CLASS_MISC, 54 | .bDeviceSubClass = MISC_SUBCLASS_COMMON, 55 | .bDeviceProtocol = MISC_PROTOCOL_IAD, 56 | 57 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 58 | 59 | .idVendor = USB_VENDOR_ID, 60 | .idProduct = USB_PRODUCT_ID, 61 | .bcdDevice = 0x0100, 62 | 63 | .iManufacturer = 0x01, 64 | .iProduct = 0x02, 65 | .iSerialNumber = 0x03, 66 | 67 | .bNumConfigurations = 0x01 68 | }; 69 | 70 | // Invoked when received GET DEVICE DESCRIPTOR 71 | // Application return pointer to descriptor 72 | uint8_t const * tud_descriptor_device_cb(void) 73 | { 74 | return (uint8_t const *) &desc_device; 75 | } 76 | 77 | //--------------------------------------------------------------------+ 78 | // Configuration Descriptor 79 | //--------------------------------------------------------------------+ 80 | 81 | enum 82 | { 83 | ITF_NUM_CDC = 0, 84 | ITF_NUM_CDC_DATA, 85 | ITF_NUM_MSC, 86 | ITF_NUM_TOTAL 87 | }; 88 | 89 | #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN) 90 | 91 | #define EPNUM_CDC_NOTIF 0x81 92 | #define EPNUM_CDC_OUT 0x02 93 | #define EPNUM_CDC_IN 0x82 94 | 95 | #define EPNUM_MSC_OUT 0x03 96 | #define EPNUM_MSC_IN 0x83 97 | 98 | uint8_t const desc_fs_configuration[] = 99 | { 100 | // Config number, interface count, string index, total length, attribute, power in mA 101 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), 102 | 103 | // Interface number, string index, EP notification address and size, EP data address (out, in) and size. 104 | TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), 105 | 106 | // Interface number, string index, EP Out & EP In address, EP size 107 | TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), 108 | }; 109 | 110 | 111 | // Invoked when received GET CONFIGURATION DESCRIPTOR 112 | // Application return pointer to descriptor 113 | // Descriptor contents must exist long enough for transfer to complete 114 | uint8_t const * tud_descriptor_configuration_cb(uint8_t index) 115 | { 116 | (void) index; // for multiple configurations 117 | 118 | return desc_fs_configuration; 119 | } 120 | 121 | //--------------------------------------------------------------------+ 122 | // String Descriptors 123 | //--------------------------------------------------------------------+ 124 | 125 | // array of pointer to string descriptors 126 | char const* string_desc_arr [] = 127 | { 128 | (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) 129 | USB_VENDOR_STR, // 1: Manufacturer 130 | USB_PRODUCT_STR, // 2: Product 131 | NULL, // 3: Serials, should use chip ID 132 | USB_PRODUCT_STR" CDC", // 4: CDC Interface 133 | USB_PRODUCT_STR" MSC", // 5: MSC Interface 134 | }; 135 | 136 | static uint16_t _desc_str[32]; 137 | 138 | // Invoked when received GET STRING DESCRIPTOR request 139 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 140 | uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) 141 | { 142 | (void) langid; 143 | 144 | uint8_t chr_count; 145 | 146 | if ( index == 0) 147 | { 148 | memcpy(&_desc_str[1], string_desc_arr[0], 2); 149 | chr_count = 1; 150 | } 151 | else 152 | { 153 | // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. 154 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors 155 | 156 | if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; 157 | 158 | const char* str = string_desc_arr[index]; 159 | 160 | // get qnique id/serial 161 | char serial[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1]; 162 | if(!str) { 163 | pico_get_unique_board_id_string(serial, sizeof(serial)); 164 | str = serial; 165 | } 166 | 167 | // Cap at max char 168 | chr_count = strlen(str); 169 | if ( chr_count > 31 ) chr_count = 31; 170 | 171 | // Convert ASCII string into UTF-16 172 | for(uint8_t i=0; i 9 | * This file is released under the BSD 3-Clause License; see LICENSE for details. 10 | */ 11 | 12 | 13 | /* System headers. */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | /* Local headers. */ 22 | 23 | #include "tusb.h" 24 | #include "ff.h" 25 | #include "diskio.h" 26 | #include "usbfs.h" 27 | 28 | 29 | /* Module variables. */ 30 | 31 | static FATFS m_fatfs; 32 | 33 | 34 | /* Functions.*/ 35 | 36 | /* 37 | * init - initialised the TinyUSB library, and also the FatFS handling. 38 | */ 39 | 40 | void usbfs_init( void ) 41 | { 42 | FRESULT l_result; 43 | MKFS_PARM l_options; 44 | 45 | /* First order of the day, is TinyUSB. */ 46 | tusb_init(); 47 | 48 | /* And also mount our FatFS partition. */ 49 | l_result = f_mount( &m_fatfs, "", 1 ); 50 | 51 | /* If there was no filesystem, make one. */ 52 | if ( l_result == FR_NO_FILESYSTEM ) 53 | { 54 | /* Set up the options, and format. */ 55 | l_options.fmt = FM_ANY | FM_SFD; 56 | l_result = f_mkfs( "", &l_options, m_fatfs.win, FF_MAX_SS ); 57 | if ( l_result != FR_OK ) 58 | { 59 | return; 60 | } 61 | 62 | /* Set the label on the volume to something sensible. */ 63 | f_setlabel( UFS_LABEL ); 64 | 65 | /* And re-mount. */ 66 | l_result = f_mount( &m_fatfs, "", 1 ); 67 | } 68 | 69 | /* All done. */ 70 | return; 71 | } 72 | 73 | 74 | /* 75 | * update - run any updates required on TinyUSB or the filesystem. 76 | */ 77 | 78 | void usbfs_update( void ) 79 | { 80 | /* Ask TinyUSB to run any outstanding tasks. */ 81 | tud_task(); 82 | 83 | /* All done. */ 84 | return; 85 | } 86 | 87 | 88 | /* 89 | * sleep_ms - a replacement for the standard sleep_ms function; this will 90 | * run update in a busy loop until we reach the requested time, 91 | * to ensure that TinyUSB doesn't run into trouble. 92 | */ 93 | 94 | void usbfs_sleep_ms( uint32_t p_milliseconds ) 95 | { 96 | absolute_time_t l_target_time; 97 | 98 | /* Work out when we want to 'sleep' until. */ 99 | l_target_time = make_timeout_time_ms( p_milliseconds ); 100 | 101 | /* Now enter a busy(ish) loop until that time. */ 102 | while( !time_reached( l_target_time ) ) 103 | { 104 | /* Run any updates. */ 105 | tud_task(); 106 | } 107 | 108 | /* All done. */ 109 | return; 110 | } 111 | 112 | 113 | /* 114 | * open - opens a file in the FatFS filesystem. Takes the same filename and mode 115 | * strings as fopen() 116 | */ 117 | 118 | usbfs_file_t *usbfs_open( const char *p_pathname, const char *p_mode ) 119 | { 120 | BYTE l_mode; 121 | usbfs_file_t *l_fptr; 122 | FRESULT l_result; 123 | 124 | /* We need to translate the fopen-style mode into FatFS style bits. */ 125 | if ( strcmp( p_mode, "r" ) == 0 ) 126 | { 127 | l_mode = FA_READ; 128 | } 129 | else if ( strcmp( p_mode, "r+" ) == 0 ) 130 | { 131 | l_mode = FA_READ|FA_WRITE; 132 | } 133 | else if ( strcmp( p_mode, "w" ) == 0 ) 134 | { 135 | l_mode = FA_CREATE_ALWAYS|FA_WRITE; 136 | } 137 | else if ( strcmp( p_mode, "w+" ) == 0 ) 138 | { 139 | l_mode = FA_CREATE_ALWAYS|FA_WRITE|FA_READ; 140 | } 141 | else if ( strcmp( p_mode, "a" ) == 0 ) 142 | { 143 | l_mode = FA_OPEN_APPEND|FA_WRITE; 144 | } 145 | else if ( strcmp( p_mode, "a+" ) == 0 ) 146 | { 147 | l_mode = FA_OPEN_APPEND|FA_WRITE|FA_READ; 148 | } 149 | else 150 | { 151 | /* If it's not a mode we support, fail. */ 152 | return NULL; 153 | } 154 | 155 | /* We'll need a new file structure so save this all in. */ 156 | l_fptr = (usbfs_file_t *)malloc( sizeof( usbfs_file_t ) ); 157 | if ( l_fptr == NULL ) 158 | { 159 | return NULL; 160 | } 161 | memset( l_fptr, 0, sizeof( usbfs_file_t ) ); 162 | 163 | /* Good, we know the mode so we can just open the file regularly. */ 164 | l_result = f_open( &l_fptr->fatfs_fptr, p_pathname, l_mode ); 165 | if ( l_result != FR_OK ) 166 | { 167 | free( l_fptr ); 168 | return NULL; 169 | } 170 | 171 | /* Make sure our status flags are set right, and return our filepointer. */ 172 | l_fptr->modified = false; 173 | return l_fptr; 174 | } 175 | 176 | 177 | /* 178 | * close - closes a file in the FatFS filesystem; if the file has been modified, 179 | * it also informs the USB host that this is so. 180 | */ 181 | 182 | bool usbfs_close( usbfs_file_t *p_fileptr ) 183 | { 184 | /* Sanity check the pointer. */ 185 | if ( p_fileptr == NULL ) 186 | { 187 | return false; 188 | } 189 | 190 | /* Then simply close the file. */ 191 | f_close( &p_fileptr->fatfs_fptr ); 192 | 193 | /* If the file was flagged as modified, let the host know to re-load data. */ 194 | if ( p_fileptr->modified ) 195 | { 196 | usb_set_fs_changed(); 197 | } 198 | 199 | /* And lastly, free up the memory allocated for our filepointer. */ 200 | free( p_fileptr ); 201 | 202 | /* All done. */ 203 | return true; 204 | } 205 | 206 | 207 | /* 208 | * read - reads data from an open file, taking similar arguments and providing 209 | * the same returns as the standard 'fread()' function; the exception is 210 | * that only 'size' is required, no 'nmemb' parameter (because let's face 211 | * it, in 95% of cases one or other of those is set to 1 anyway). 212 | */ 213 | 214 | size_t usbfs_read( void *p_buffer, size_t p_size, usbfs_file_t *p_fileptr ) 215 | { 216 | UINT l_bytecount; 217 | FRESULT l_result; 218 | 219 | /* Sanity check our parameters. */ 220 | if ( ( p_buffer == NULL ) || ( p_fileptr == NULL ) ) 221 | { 222 | /* If we don't have valid pointers, we can't read data. */ 223 | return 0; 224 | } 225 | 226 | /* Then we just send it to FatFS. */ 227 | l_result = f_read( &p_fileptr->fatfs_fptr, p_buffer, p_size, &l_bytecount ); 228 | if ( l_result != FR_OK ) 229 | { 230 | /* The write has failed. */ 231 | return 0; 232 | } 233 | 234 | /* Simply return the number of bytes read then. */ 235 | return l_bytecount; 236 | } 237 | 238 | 239 | /* 240 | * write - writes data from an open file, taking similar arguments and providing 241 | * the same returns as the standard 'fwrite()' function; the exception is 242 | * that only 'size' is required, no 'nmemb' parameter (because let's face 243 | * it, in 95% of cases one or other of those is set to 1 anyway). 244 | */ 245 | 246 | size_t usbfs_write( const void *p_buffer, size_t p_size, usbfs_file_t *p_fileptr ) 247 | { 248 | UINT l_bytecount; 249 | FRESULT l_result; 250 | 251 | /* Sanity check our parameters. */ 252 | if ( ( p_buffer == NULL ) || ( p_fileptr == NULL ) ) 253 | { 254 | /* If we don't have valid pointers, we can't write data. */ 255 | return 0; 256 | } 257 | 258 | /* Then we just send it to FatFS. */ 259 | l_result = f_write( &p_fileptr->fatfs_fptr, p_buffer, p_size, &l_bytecount ); 260 | if ( l_result != FR_OK ) 261 | { 262 | /* The write has failed. */ 263 | return 0; 264 | } 265 | 266 | /* Flag that we've written data to this file. */ 267 | p_fileptr->modified = true; 268 | 269 | /* Simply return the number of bytes written then. */ 270 | return l_bytecount; 271 | } 272 | 273 | 274 | /* 275 | * gets - reads a line of text from the file; takes the same arguments and 276 | * returns the same as the standard 'fgets()' function. 277 | */ 278 | 279 | char *usbfs_gets( char *p_buffer, size_t p_size, usbfs_file_t *p_fileptr ) 280 | { 281 | /* Sanity check our parameters. */ 282 | if ( ( p_buffer == NULL ) || ( p_fileptr == NULL ) ) 283 | { 284 | /* If we don't have valid pointers, we can't read data. */ 285 | return NULL; 286 | } 287 | 288 | /* Excellent; so, ask FatFS to do the work. */ 289 | return f_gets( p_buffer, p_size, &p_fileptr->fatfs_fptr ); 290 | } 291 | 292 | 293 | /* 294 | * puts - writes a line of text from the file; takes the same arguments and 295 | * returns the same as the standard 'fputs()' function. 296 | */ 297 | 298 | size_t usbfs_puts( const char *p_buffer, usbfs_file_t *p_fileptr ) 299 | { 300 | int l_bytecount; 301 | 302 | /* Sanity check our parameters. */ 303 | if ( ( p_buffer == NULL ) || ( p_fileptr == NULL ) ) 304 | { 305 | /* If we don't have valid pointers, we can't write data. */ 306 | return -1; 307 | } 308 | 309 | /* Excellent; so, ask FatFS to do the work. */ 310 | l_bytecount = f_puts( p_buffer, &p_fileptr->fatfs_fptr ); 311 | 312 | /* 313 | * If a negative value was returned, this indicates an error - otherwise, 314 | * flag that the file has been modified. 315 | */ 316 | if ( l_bytecount >= 0 ) 317 | { 318 | p_fileptr->modified = true; 319 | } 320 | return l_bytecount; 321 | } 322 | 323 | 324 | /* 325 | * timestamp - fetches the timestamp of the named file; a zero is returned 326 | * if the file does not exist. This is encoded as per FatFS, but 327 | * can be used without decoding to compare to previous stamps. 328 | */ 329 | 330 | uint32_t usbfs_timestamp( const char *p_pathname ) 331 | { 332 | FILINFO l_fileinfo; 333 | FRESULT l_result; 334 | 335 | /* Force a re-mount to make sure timestamps are sync'd */ 336 | f_mount( &m_fatfs, "", 1 ); 337 | 338 | /* Ask for information about the file. */ 339 | l_result = f_stat( p_pathname, &l_fileinfo ); 340 | if ( l_result != FR_OK ) 341 | { 342 | /* If we encountered an error, return a zero timestamp. */ 343 | return 0; 344 | } 345 | 346 | /* The date and time are both encoded in WORDS; stick them together. */ 347 | return ( l_fileinfo.fdate << 16 ) | l_fileinfo.ftime; 348 | } 349 | 350 | 351 | /* End of file usbfs/usbfs.cpp */ 352 | -------------------------------------------------------------------------------- /drivers/usbfs/usbfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbfs/usbfs.h - part of the PicoW C/C++ Boilerplate Project 3 | * 4 | * usbfs is the library that handles presenting a filesystem to the host 5 | * over USB; the main aim is to make it easy to present configuration files 6 | * to remove the need to recompile (for example, WiFi settings) 7 | * 8 | * Copyright (C) 2023 Pete Favelle 9 | * This file is released under the BSD 3-Clause License; see LICENSE for details. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include "ff.h" 15 | 16 | 17 | /* Constants. */ 18 | 19 | #define USB_VENDOR_ID 0xCafe 20 | #define USB_VENDOR_STR "USBFS" 21 | #define USB_PRODUCT_STR "PicoW" 22 | 23 | #define UFS_LABEL "PicoW" 24 | 25 | 26 | /* Structures */ 27 | 28 | typedef struct 29 | { 30 | FIL fatfs_fptr; 31 | bool modified; 32 | } usbfs_file_t; 33 | 34 | 35 | /* Function prototypes. */ 36 | 37 | /* Internal functions. */ 38 | 39 | void storage_get_size( uint16_t *, uint32_t * ); 40 | int32_t storage_read( uint32_t, uint32_t, void *, uint32_t ); 41 | int32_t storage_write( uint32_t, uint32_t, const uint8_t *, uint32_t ); 42 | 43 | void usb_set_fs_changed( void ); 44 | 45 | /* Public functions. */ 46 | 47 | #ifdef __cplusplus 48 | extern "C" { 49 | #endif 50 | 51 | void usbfs_init( void ); 52 | void usbfs_update( void ); 53 | void usbfs_sleep_ms( uint32_t ); 54 | 55 | usbfs_file_t *usbfs_open( const char *, const char * ); 56 | bool usbfs_close( usbfs_file_t * ); 57 | size_t usbfs_read( void *, size_t, usbfs_file_t * ); 58 | size_t usbfs_write( const void *, size_t, usbfs_file_t * ); 59 | char *usbfs_gets( char *, size_t, usbfs_file_t * ); 60 | size_t usbfs_puts( const char *, usbfs_file_t * ); 61 | uint32_t usbfs_timestamp( const char * ); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | 68 | /* End of file usbfs/usbfs.h */ 69 | -------------------------------------------------------------------------------- /drivers/vga-nextgen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(vga-nextgen INTERFACE) 2 | 3 | target_sources(vga-nextgen INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/vga.c 5 | ) 6 | 7 | target_link_libraries(vga-nextgen INTERFACE hardware_pio hardware_dma) 8 | 9 | target_include_directories(vga-nextgen INTERFACE 10 | ${CMAKE_CURRENT_LIST_DIR} 11 | ) 12 | -------------------------------------------------------------------------------- /drivers/vga-nextgen/vga.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdbool.h" 3 | 4 | #define PIO_VGA (pio0) 5 | #ifndef VGA_BASE_PIN 6 | #define VGA_BASE_PIN (6) 7 | #endif 8 | #define VGA_DMA_IRQ (DMA_IRQ_0) 9 | 10 | #define TEXTMODE_COLS 80 11 | #define TEXTMODE_ROWS 30 12 | 13 | #define RGB888(r, g, b) ((r<<16) | (g << 8 ) | b ) 14 | -------------------------------------------------------------------------------- /drivers/ws2812/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ws2812 INTERFACE) 2 | 3 | target_sources(ws2812 INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/ws2812.c 5 | ${CMAKE_CURRENT_LIST_DIR}/ws2812.h 6 | ) 7 | 8 | target_link_libraries(ws2812 INTERFACE hardware_pio hardware_clocks) 9 | 10 | target_include_directories(ws2812 INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR} 12 | ) 13 | 14 | pico_generate_pio_header(ws2812 15 | ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio 16 | ) 17 | -------------------------------------------------------------------------------- /drivers/ws2812/ws2812.c: -------------------------------------------------------------------------------- 1 | #include "ws2812.h" 2 | #include "ws2812.pio.h" 3 | 4 | 5 | void ws2812_set_rgb(uint8_t red, uint8_t green, uint8_t blue) { 6 | pio_sm_restart(WS2812_PIO, WS2812_SM); 7 | 8 | // write the rgb 9 | uint32_t mask = (green << 16) | (red << 8) | (blue << 0); 10 | pio_sm_put_blocking(WS2812_PIO, WS2812_SM, mask << 8u); 11 | } 12 | 13 | void ws2812_init() { 14 | ws2812_program_init(WS2812_PIO, WS2812_SM, WS2812_PIN, true); 15 | } 16 | 17 | void ws2812_reset() { 18 | // turn it off 19 | ws2812_set_rgb(0, 0, 0); 20 | pio_sm_set_enabled(WS2812_PIO, WS2812_SM, false); 21 | pio_sm_restart(WS2812_PIO, WS2812_SM); 22 | } 23 | -------------------------------------------------------------------------------- /drivers/ws2812/ws2812.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "hardware/pio.h" 3 | 4 | #define WS2812_SM 1 5 | #define WS2812_PIO ((pio_hw_t *)PIO1_BASE) 6 | #define WS2812_PIN 23 7 | 8 | void ws2812_set_rgb(uint8_t red, uint8_t green, uint8_t blue); 9 | void ws2812_init(); 10 | void ws2812_reset(); 11 | -------------------------------------------------------------------------------- /drivers/ws2812/ws2812.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | .program ws2812 8 | .side_set 1 9 | 10 | .define public T1 2 11 | .define public T2 5 12 | .define public T3 3 13 | 14 | .lang_opt python sideset_init = pico.PIO.OUT_HIGH 15 | .lang_opt python out_init = pico.PIO.OUT_HIGH 16 | .lang_opt python out_shiftdir = 1 17 | 18 | .wrap_target 19 | bitloop: 20 | out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls 21 | jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse 22 | do_one: 23 | jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse 24 | do_zero: 25 | nop side 0 [T2 - 1] ; Or drive low, for a short pulse 26 | .wrap 27 | 28 | % c-sdk { 29 | #include "hardware/clocks.h" 30 | 31 | static inline void ws2812_program_init(PIO pio, uint sm, uint pin, bool rgbw) { 32 | pio_gpio_init(pio, pin); 33 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); 34 | 35 | uint offset = pio_add_program(WS2812_PIO, &ws2812_program); 36 | pio_sm_config c = ws2812_program_get_default_config(offset); 37 | sm_config_set_sideset_pins(&c, pin); 38 | sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); 39 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 40 | 41 | const int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; 42 | float div = clock_get_hz(clk_sys) / (800000 * cycles_per_bit); 43 | sm_config_set_clkdiv(&c, div); 44 | 45 | pio_sm_init(pio, sm, offset, &c); 46 | pio_sm_set_enabled(pio, sm, true); 47 | } 48 | %} 49 | -------------------------------------------------------------------------------- /ext/minigb_apu/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Alex Baines 2 | Copyright (c) 2019 Mahyar Koshkouei 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /ext/minigb_apu/minigb_apu.h: -------------------------------------------------------------------------------- 1 | /** 2 | * minigb_apu is released under the terms listed within the LICENSE file. 3 | * 4 | * minigb_apu emulates the audio processing unit (APU) of the Game Boy. This 5 | * project is based on MiniGBS by Alex Baines: https://github.com/baines/MiniGBS 6 | */ 7 | 8 | #pragma once 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #include 14 | 15 | #define AUDIO_SAMPLE_RATE 44100 16 | 17 | #define DMG_CLOCK_FREQ 4194304.0 18 | #define SCREEN_REFRESH_CYCLES 70224.0 19 | #define VERTICAL_SYNC (DMG_CLOCK_FREQ/SCREEN_REFRESH_CYCLES) 20 | 21 | #define AUDIO_SAMPLES ((unsigned)(AUDIO_SAMPLE_RATE / VERTICAL_SYNC)) 22 | #define AUDIO_BUFFER_SIZE_BYTES (AUDIO_SAMPLES*4) 23 | 24 | /** 25 | * Fill allocated buffer "data" with "len" number of 32-bit floating point 26 | * samples (native endian order) in stereo interleaved format. 27 | */ 28 | void audio_callback(void *ptr, int16_t *data, size_t len); 29 | 30 | /** 31 | * Read audio register at given address "addr". 32 | */ 33 | uint8_t audio_read(const uint16_t addr); 34 | 35 | /** 36 | * Write "val" to audio register at given address "addr". 37 | */ 38 | void audio_write(const uint16_t addr, const uint8_t val); 39 | 40 | /** 41 | * Initialise audio driver. 42 | */ 43 | void audio_init(void); 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | -------------------------------------------------------------------------------- /memmap.ld: -------------------------------------------------------------------------------- 1 | /* Based on GCC ARM embedded samples. 2 | Defines the following symbols for use by code: 3 | __exidx_start 4 | __exidx_end 5 | __etext 6 | __data_start__ 7 | __preinit_array_start 8 | __preinit_array_end 9 | __init_array_start 10 | __init_array_end 11 | __fini_array_start 12 | __fini_array_end 13 | __data_end__ 14 | __bss_start__ 15 | __bss_end__ 16 | __end__ 17 | end 18 | __HeapLimit 19 | __StackLimit 20 | __StackTop 21 | __stack (== StackTop) 22 | */ 23 | 24 | MEMORY 25 | { 26 | /* 64k = 10000 27 | FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 4096k 28 | */ 29 | FLASH(rx) : ORIGIN = 0x10010000, LENGTH = (4096k-64k) 30 | RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k 31 | SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k 32 | SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k 33 | } 34 | 35 | ENTRY(_entry_point) 36 | 37 | SECTIONS 38 | { 39 | .flash_begin : { 40 | __flash_binary_start = .; 41 | } > FLASH 42 | 43 | /* The bootrom will enter the image at the point indicated in your 44 | IMAGE_DEF, which is usually the reset handler of your vector table. 45 | 46 | The debugger will use the ELF entry point, which is the _entry_point 47 | symbol, and in our case is *different from the bootrom's entry point.* 48 | This is used to go back through the bootrom on debugger launches only, 49 | to perform the same initial flash setup that would be performed on a 50 | cold boot. 51 | */ 52 | 53 | .text : { 54 | __logical_binary_start = .; 55 | KEEP (*(.vectors)) 56 | KEEP (*(.binary_info_header)) 57 | __binary_info_header_end = .; 58 | KEEP (*(.embedded_block)) 59 | __embedded_block_end = .; 60 | KEEP (*(.reset)) 61 | /* TODO revisit this now memset/memcpy/float in ROM */ 62 | /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from 63 | * FLASH ... we will include any thing excluded here in .data below by default */ 64 | *(.init) 65 | *libgcc.a:cmse_nonsecure_call.o 66 | *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) 67 | *(.fini) 68 | /* Pull all c'tors into .text */ 69 | *crtbegin.o(.ctors) 70 | *crtbegin?.o(.ctors) 71 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) 72 | *(SORT(.ctors.*)) 73 | *(.ctors) 74 | /* Followed by destructors */ 75 | *crtbegin.o(.dtors) 76 | *crtbegin?.o(.dtors) 77 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) 78 | *(SORT(.dtors.*)) 79 | *(.dtors) 80 | 81 | . = ALIGN(4); 82 | /* preinit data */ 83 | PROVIDE_HIDDEN (__preinit_array_start = .); 84 | KEEP(*(SORT(.preinit_array.*))) 85 | KEEP(*(.preinit_array)) 86 | PROVIDE_HIDDEN (__preinit_array_end = .); 87 | 88 | . = ALIGN(4); 89 | /* init data */ 90 | PROVIDE_HIDDEN (__init_array_start = .); 91 | KEEP(*(SORT(.init_array.*))) 92 | KEEP(*(.init_array)) 93 | PROVIDE_HIDDEN (__init_array_end = .); 94 | 95 | . = ALIGN(4); 96 | /* finit data */ 97 | PROVIDE_HIDDEN (__fini_array_start = .); 98 | *(SORT(.fini_array.*)) 99 | *(.fini_array) 100 | PROVIDE_HIDDEN (__fini_array_end = .); 101 | 102 | *(.eh_frame*) 103 | . = ALIGN(4); 104 | } > FLASH 105 | 106 | /* Note the boot2 section is optional, and should be discarded if there is 107 | no reference to it *inside* the binary, as it is not called by the 108 | bootrom. (The bootrom performs a simple best-effort XIP setup and 109 | leaves it to the binary to do anything more sophisticated.) However 110 | there is still a size limit of 256 bytes, to ensure the boot2 can be 111 | stored in boot RAM. 112 | 113 | Really this is a "XIP setup function" -- the name boot2 is historic and 114 | refers to its dual-purpose on RP2040, where it also handled vectoring 115 | from the bootrom into the user image. 116 | */ 117 | 118 | .boot2 : { 119 | __boot2_start__ = .; 120 | *(.boot2) 121 | __boot2_end__ = .; 122 | } > FLASH 123 | 124 | ASSERT(__boot2_end__ - __boot2_start__ <= 256, 125 | "ERROR: Pico second stage bootloader must be no more than 256 bytes in size") 126 | 127 | .rodata : { 128 | *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) 129 | *(.srodata*) 130 | . = ALIGN(4); 131 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) 132 | . = ALIGN(4); 133 | } > FLASH 134 | 135 | .ARM.extab : 136 | { 137 | *(.ARM.extab* .gnu.linkonce.armextab.*) 138 | } > FLASH 139 | 140 | __exidx_start = .; 141 | .ARM.exidx : 142 | { 143 | *(.ARM.exidx* .gnu.linkonce.armexidx.*) 144 | } > FLASH 145 | __exidx_end = .; 146 | 147 | /* Machine inspectable binary information */ 148 | . = ALIGN(4); 149 | __binary_info_start = .; 150 | .binary_info : 151 | { 152 | KEEP(*(.binary_info.keep.*)) 153 | *(.binary_info.*) 154 | } > FLASH 155 | __binary_info_end = .; 156 | . = ALIGN(4); 157 | 158 | .ram_vector_table (NOLOAD): { 159 | *(.ram_vector_table) 160 | } > RAM 161 | 162 | .uninitialized_data (NOLOAD): { 163 | . = ALIGN(4); 164 | *(.uninitialized_data*) 165 | } > RAM 166 | 167 | .data : { 168 | __data_start__ = .; 169 | *(vtable) 170 | 171 | *(.time_critical*) 172 | 173 | /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ 174 | *(.text*) 175 | . = ALIGN(4); 176 | *(.rodata*) 177 | . = ALIGN(4); 178 | 179 | *(.data*) 180 | *(.sdata*) 181 | 182 | . = ALIGN(4); 183 | *(.after_data.*) 184 | . = ALIGN(4); 185 | /* preinit data */ 186 | PROVIDE_HIDDEN (__mutex_array_start = .); 187 | KEEP(*(SORT(.mutex_array.*))) 188 | KEEP(*(.mutex_array)) 189 | PROVIDE_HIDDEN (__mutex_array_end = .); 190 | 191 | *(.jcr) 192 | . = ALIGN(4); 193 | } > RAM AT> FLASH 194 | 195 | .tdata : { 196 | . = ALIGN(4); 197 | *(.tdata .tdata.* .gnu.linkonce.td.*) 198 | /* All data end */ 199 | __tdata_end = .; 200 | } > RAM AT> FLASH 201 | PROVIDE(__data_end__ = .); 202 | 203 | /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ 204 | __etext = LOADADDR(.data); 205 | 206 | .tbss (NOLOAD) : { 207 | . = ALIGN(4); 208 | __bss_start__ = .; 209 | __tls_base = .; 210 | *(.tbss .tbss.* .gnu.linkonce.tb.*) 211 | *(.tcommon) 212 | 213 | __tls_end = .; 214 | } > RAM 215 | 216 | .bss (NOLOAD) : { 217 | . = ALIGN(4); 218 | __tbss_end = .; 219 | 220 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) 221 | *(COMMON) 222 | PROVIDE(__global_pointer$ = . + 2K); 223 | *(.sbss*) 224 | . = ALIGN(4); 225 | __bss_end__ = .; 226 | } > RAM 227 | 228 | .heap (NOLOAD): 229 | { 230 | __end__ = .; 231 | end = __end__; 232 | KEEP(*(.heap*)) 233 | } > RAM 234 | /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however 235 | to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ 236 | __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); 237 | 238 | /* Start and end symbols must be word-aligned */ 239 | .scratch_x : { 240 | __scratch_x_start__ = .; 241 | *(.scratch_x.*) 242 | . = ALIGN(4); 243 | __scratch_x_end__ = .; 244 | } > SCRATCH_X AT > FLASH 245 | __scratch_x_source__ = LOADADDR(.scratch_x); 246 | 247 | .scratch_y : { 248 | __scratch_y_start__ = .; 249 | *(.scratch_y.*) 250 | . = ALIGN(4); 251 | __scratch_y_end__ = .; 252 | } > SCRATCH_Y AT > FLASH 253 | __scratch_y_source__ = LOADADDR(.scratch_y); 254 | 255 | /* .stack*_dummy section doesn't contains any symbols. It is only 256 | * used for linker to calculate size of stack sections, and assign 257 | * values to stack symbols later 258 | * 259 | * stack1 section may be empty/missing if platform_launch_core1 is not used */ 260 | 261 | /* by default we put core 0 stack at the end of scratch Y, so that if core 1 262 | * stack is not used then all of SCRATCH_X is free. 263 | */ 264 | .stack1_dummy (NOLOAD): 265 | { 266 | *(.stack1*) 267 | } > SCRATCH_X 268 | .stack_dummy (NOLOAD): 269 | { 270 | KEEP(*(.stack*)) 271 | } > SCRATCH_Y 272 | 273 | .flash_end : { 274 | KEEP(*(.embedded_end_block*)) 275 | PROVIDE(__flash_binary_end = .); 276 | } > FLASH =0xaa 277 | 278 | /* stack limit is poorly named, but historically is maximum heap ptr */ 279 | __StackLimit = ORIGIN(RAM) + LENGTH(RAM); 280 | __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); 281 | __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); 282 | __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); 283 | __StackBottom = __StackTop - SIZEOF(.stack_dummy); 284 | PROVIDE(__stack = __StackTop); 285 | 286 | /* picolibc and LLVM */ 287 | PROVIDE (__heap_start = __end__); 288 | PROVIDE (__heap_end = __HeapLimit); 289 | PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); 290 | PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); 291 | PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); 292 | 293 | /* llvm-libc */ 294 | PROVIDE (_end = __end__); 295 | PROVIDE (__llvm_libc_heap_limit = __HeapLimit); 296 | 297 | /* Check if data + heap + stack exceeds RAM limit */ 298 | ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") 299 | 300 | ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary") 301 | ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") 302 | 303 | /* todo assert on extra code */ 304 | } 305 | 306 | -------------------------------------------------------------------------------- /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 | # Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. 7 | # 8 | # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 9 | # following conditions are met: 10 | # 11 | # 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 12 | # disclaimer. 13 | # 14 | # 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 15 | # disclaimer in the documentation and/or other materials provided with the distribution. 16 | # 17 | # 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 18 | # derived from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 21 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 29 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 30 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 31 | endif () 32 | 33 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 34 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 35 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 36 | endif () 37 | 38 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 39 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 40 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 41 | endif () 42 | 43 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) 44 | set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) 45 | message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") 46 | endif () 47 | 48 | if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) 49 | set(PICO_SDK_FETCH_FROM_GIT_TAG "master") 50 | message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") 51 | endif() 52 | 53 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 54 | 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") 55 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 56 | set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") 57 | 58 | if (NOT PICO_SDK_PATH) 59 | if (PICO_SDK_FETCH_FROM_GIT) 60 | include(FetchContent) 61 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 62 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 63 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 64 | endif () 65 | FetchContent_Declare( 66 | pico_sdk 67 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 68 | GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} 69 | ) 70 | 71 | if (NOT pico_sdk) 72 | message("Downloading Raspberry Pi Pico SDK") 73 | # GIT_SUBMODULES_RECURSE was added in 3.17 74 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 75 | FetchContent_Populate( 76 | pico_sdk 77 | QUIET 78 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 79 | GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} 80 | GIT_SUBMODULES_RECURSE FALSE 81 | 82 | SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src 83 | BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build 84 | SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild 85 | ) 86 | else () 87 | FetchContent_Populate( 88 | pico_sdk 89 | QUIET 90 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 91 | GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} 92 | 93 | SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src 94 | BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build 95 | SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild 96 | ) 97 | endif () 98 | 99 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 100 | endif () 101 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 102 | else () 103 | message(FATAL_ERROR 104 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 105 | ) 106 | endif () 107 | endif () 108 | 109 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 110 | if (NOT EXISTS ${PICO_SDK_PATH}) 111 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 112 | endif () 113 | 114 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 115 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 116 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 117 | endif () 118 | 119 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 120 | 121 | include(${PICO_SDK_INIT_CMAKE_FILE}) 122 | -------------------------------------------------------------------------------- /src/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //--------------------------------------------------------------------+ 34 | // Board Specific Configuration 35 | //--------------------------------------------------------------------+ 36 | 37 | #if CFG_TUSB_MCU == OPT_MCU_RP2040 38 | // change to 1 if using pico-pio-usb as host controller for raspberry rp2040 39 | #define CFG_TUH_RPI_PIO_USB 0 40 | #define BOARD_TUH_RHPORT CFG_TUH_RPI_PIO_USB 41 | #endif 42 | 43 | // RHPort number used for host can be defined by board.mk, default to port 0 44 | #ifndef BOARD_TUH_RHPORT 45 | #define BOARD_TUH_RHPORT 0 46 | #endif 47 | 48 | // RHPort max operational speed can defined by board.mk 49 | #ifndef BOARD_TUH_MAX_SPEED 50 | #define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED 51 | #endif 52 | 53 | //-------------------------------------------------------------------- 54 | // COMMON CONFIGURATION 55 | //-------------------------------------------------------------------- 56 | 57 | // defined by compiler flags for flexibility 58 | #ifndef CFG_TUSB_MCU 59 | #error CFG_TUSB_MCU must be defined 60 | #endif 61 | 62 | #ifndef CFG_TUSB_OS 63 | #define CFG_TUSB_OS OPT_OS_NONE 64 | #endif 65 | 66 | #ifndef CFG_TUSB_DEBUG 67 | #define CFG_TUSB_DEBUG 0 68 | #endif 69 | 70 | // Enable Host stack 71 | #define CFG_TUH_ENABLED 1 72 | 73 | // Default is max speed that hardware controller could support with on-chip PHY 74 | #define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED 75 | 76 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 77 | * Tinyusb use follows macros to declare transferring memory so that they can be put 78 | * into those specific section. 79 | * e.g 80 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 81 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 82 | */ 83 | #ifndef CFG_TUSB_MEM_SECTION 84 | #define CFG_TUSB_MEM_SECTION 85 | #endif 86 | 87 | #ifndef CFG_TUSB_MEM_ALIGN 88 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 89 | #endif 90 | 91 | //-------------------------------------------------------------------- 92 | // CONFIGURATION 93 | //-------------------------------------------------------------------- 94 | 95 | // Size of buffer to hold descriptors and other data used for enumeration 96 | #define CFG_TUH_ENUMERATION_BUFSIZE 1024 97 | 98 | #define CFG_TUH_XINPUT 1 // 99 | #define CFG_TUH_HUB 1 // number of supported hubs 100 | #define CFG_TUH_CDC 0 101 | #define CFG_TUH_HID 4 // typical keyboard + mouse device can have 3-4 HID interfaces 102 | #define CFG_TUH_MSC 0 103 | #define CFG_TUH_VENDOR 0 104 | 105 | // max device support (excluding hub device) 106 | #define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports 107 | 108 | //------------- HID -------------// 109 | #define CFG_TUH_HID_EPIN_BUFSIZE 64 110 | #define CFG_TUH_HID_EPOUT_BUFSIZE 64 111 | 112 | //------------- CDC -------------// 113 | 114 | // Set Line Control state on enumeration/mounted: 115 | // DTR ( bit 0), RTS (bit 1) 116 | #define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0x03 117 | 118 | // Set Line Coding on enumeration/mounted, value for cdc_line_coding_t 119 | // bit rate = 115200, 1 stop bit, no parity, 8 bit data width 120 | #define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } 121 | 122 | 123 | #ifdef __cplusplus 124 | } 125 | #endif 126 | 127 | #endif /* _TUSB_CONFIG_H_ */ 128 | --------------------------------------------------------------------------------