├── .github └── workflows │ └── BuildAndRelease.yml ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── boot2 └── exit_from_boot2.S ├── 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 ├── 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 ├── memmap.ld.in ├── pico_sdk_import.cmake ├── src └── main.cpp └── uf2_vis.py /.github/workflows/BuildAndRelease.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: dendy 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 | sudo apt install -y ninja-build 34 | 35 | - name: Check out this repository 36 | uses: actions/checkout@v3 37 | 38 | - name: Print Working directory 39 | run: echo $HOME && pwd && ls -la 40 | 41 | - name: Update line containing pico_set_program_version() in CMakelists.txt with tag name. 42 | run: | 43 | # Extract the tag name that triggered the event and remove the 'refs/tags/' prefix 44 | input_string=${{ github.ref }} 45 | prefix="refs/tags/" 46 | tag="No versioninfo found" 47 | if [[ $input_string == $prefix* ]]; then 48 | echo "The string starts with 'refs/tags/'." 49 | tag="${input_string#$prefix}" 50 | echo "Tag is ${tag}" 51 | sed -i "s/^[[:space:]]*pico_set_program_version(.*/pico_set_program_version(\$\{PROJECT_NAME\} \"$tag\")/" CMakeLists.txt 52 | else 53 | echo "The string does not start with 'refs/tags/'." 54 | fi 55 | grep "pico_set_program_version" CMakeLists.txt 56 | 57 | - name: Install Pico SDk 58 | run: | 59 | cd $HOME && \ 60 | git clone https://github.com/raspberrypi/pico-sdk.git --branch master && \ 61 | cd pico-sdk/ && \ 62 | git submodule update --init && \ 63 | cd lib/tinyusb && 64 | git checkout 08f9ed67c92421cbd0bc09270d2f363886681866 65 | 66 | - name: Build the project 67 | run: | 68 | export PICO_SDK_PATH=$HOME/pico-sdk && \ 69 | mkdir build && cd build && echo 2048 &&\ 70 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=OFF -DTFT=OFF .. && cmake --build . &&\ 71 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=ON -DTFT=OFF .. && cmake --build . &&\ 72 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=OFF -DTFT=ON .. && cmake --build . &&\ 73 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=OFF -DTFT=ON -DILI9341=ON .. && cmake --build . &&\ 74 | cd .. && rm -rf build && mkdir build && cd build && echo 4096 &&\ 75 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=OFF -DTFT=OFF -DFLASH_SIZE=4096 .. && cmake --build . &&\ 76 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=ON -DTFT=OFF -DFLASH_SIZE=4096 .. && cmake --build . &&\ 77 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=OFF -DTFT=ON -DFLASH_SIZE=4096 .. && cmake --build . &&\ 78 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=OFF -DTFT=ON -DFLASH_SIZE=4096 -DILI9341=ON .. && cmake --build . &&\ 79 | cd .. && rm -rf build && mkdir build && cd build && echo 16384 &&\ 80 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=OFF -DTFT=OFF -DFLASH_SIZE=16384 .. && cmake --build . &&\ 81 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=ON -DTFT=OFF -DFLASH_SIZE=16384 .. && cmake --build . &&\ 82 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=OFF -DTFT=ON -DFLASH_SIZE=16384 .. && cmake --build . &&\ 83 | cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -DVGA=ON -DHDMI=OFF -DTFT=ON -DFLASH_SIZE=16384 -DILI9341=ON .. && cmake --build . &&\ 84 | 85 | echo done 86 | 87 | - name: Create release 88 | uses: softprops/action-gh-release@v1 89 | if: startsWith(github.ref, 'refs/tags/') 90 | with: 91 | files: | 92 | bin/MinSizeRel/**.uf2 93 | 94 | body_path: CHANGELOG.md 95 | 96 | 97 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /bin/ 3 | build 4 | uf2 5 | cmake-*/ 6 | fh.log 7 | /pimoroni-pico/ 8 | build 9 | # Prerequisites 10 | *.d 11 | 12 | # Compiled Object files 13 | *.slo 14 | *.lo 15 | *.o 16 | *.obj 17 | 18 | # Precompiled Headers 19 | *.gch 20 | *.pch 21 | 22 | # Compiled Dynamic libraries 23 | *.so 24 | *.dylib 25 | *.dll 26 | 27 | # Fortran module files 28 | *.mod 29 | *.smod 30 | 31 | # Compiled Static libraries 32 | *.lai 33 | *.la 34 | *.a 35 | *.lib 36 | 37 | # Executables 38 | *.exe 39 | *.out 40 | *.app 41 | 42 | **/build 43 | .vscode 44 | 45 | # Apple filesystem cruft 46 | .DS_Store 47 | venv 48 | # Visual Studio stuff 49 | ## Ignore Visual Studio temporary files, build results, and 50 | ## files generated by popular Visual Studio add-ons. 51 | ## 52 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 53 | 54 | # User-specific files 55 | *.rsuser 56 | *.suo 57 | *.user 58 | *.userosscache 59 | *.sln.docstates 60 | 61 | # User-specific files (MonoDevelop/Xamarin Studio) 62 | *.userprefs 63 | 64 | # Mono auto generated files 65 | mono_crash.* 66 | 67 | # Build results 68 | [Dd]ebug/ 69 | [Dd]ebugPublic/ 70 | [Rr]elease/ 71 | [Rr]eleases/ 72 | x64/ 73 | x86/ 74 | [Aa][Rr][Mm]/ 75 | [Aa][Rr][Mm]64/ 76 | bld/ 77 | [Bb]in/ 78 | [Oo]bj/ 79 | [Ll]og/ 80 | [Ll]ogs/ 81 | [Bb]uild/ 82 | 83 | # Visual Studio 2015/2017 cache/options directory 84 | .vs/ 85 | # Uncomment if you have tasks that create the project's static files in wwwroot 86 | #wwwroot/ 87 | 88 | # Visual Studio 2017 auto generated files 89 | Generated\ Files/ 90 | 91 | # MSTest test Results 92 | [Tt]est[Rr]esult*/ 93 | [Bb]uild[Ll]og.* 94 | 95 | # NUnit 96 | *.VisualState.xml 97 | TestResult.xml 98 | nunit-*.xml 99 | 100 | # Build Results of an ATL Project 101 | [Dd]ebugPS/ 102 | [Rr]eleasePS/ 103 | dlldata.c 104 | 105 | # Benchmark Results 106 | BenchmarkDotNet.Artifacts/ 107 | 108 | # .NET Core 109 | project.lock.json 110 | project.fragment.lock.json 111 | artifacts/ 112 | 113 | # StyleCop 114 | StyleCopReport.xml 115 | 116 | # Files built by Visual Studio 117 | *_i.c 118 | *_p.c 119 | *_h.h 120 | *.ilk 121 | *.meta 122 | *.obj 123 | *.iobj 124 | *.pch 125 | *.pdb 126 | *.ipdb 127 | *.pgc 128 | *.pgd 129 | *.rsp 130 | *.sbr 131 | *.tlb 132 | *.tli 133 | *.tlh 134 | *.tmp 135 | *.tmp_proj 136 | *_wpftmp.csproj 137 | *.log 138 | *.vspscc 139 | *.vssscc 140 | .builds 141 | *.pidb 142 | *.svclog 143 | *.scc 144 | 145 | # Chutzpah Test files 146 | _Chutzpah* 147 | 148 | # Visual C++ cache files 149 | ipch/ 150 | *.aps 151 | *.ncb 152 | *.opendb 153 | *.opensdf 154 | *.sdf 155 | *.cachefile 156 | *.VC.db 157 | *.VC.VC.opendb 158 | 159 | # Visual Studio profiler 160 | *.psess 161 | *.vsp 162 | *.vspx 163 | *.sap 164 | 165 | # Visual Studio Trace Files 166 | *.e2e 167 | 168 | # TFS 2012 Local Workspace 169 | $tf/ 170 | 171 | # Guidance Automation Toolkit 172 | *.gpState 173 | 174 | # ReSharper is a .NET coding add-in 175 | _ReSharper*/ 176 | *.[Rr]e[Ss]harper 177 | *.DotSettings.user 178 | 179 | # TeamCity is a build add-in 180 | _TeamCity* 181 | 182 | # DotCover is a Code Coverage Tool 183 | *.dotCover 184 | 185 | # AxoCover is a Code Coverage Tool 186 | .axoCover/* 187 | !.axoCover/settings.json 188 | 189 | # Visual Studio code coverage results 190 | *.coverage 191 | *.coveragexml 192 | 193 | # NCrunch 194 | _NCrunch_* 195 | .*crunch*.local.xml 196 | nCrunchTemp_* 197 | 198 | # MightyMoose 199 | *.mm.* 200 | AutoTest.Net/ 201 | 202 | # Web workbench (sass) 203 | .sass-cache/ 204 | 205 | # Installshield output folder 206 | [Ee]xpress/ 207 | 208 | # DocProject is a documentation generator add-in 209 | DocProject/buildhelp/ 210 | DocProject/Help/*.HxT 211 | DocProject/Help/*.HxC 212 | DocProject/Help/*.hhc 213 | DocProject/Help/*.hhk 214 | DocProject/Help/*.hhp 215 | DocProject/Help/Html2 216 | DocProject/Help/html 217 | 218 | # Click-Once directory 219 | publish/ 220 | 221 | # Publish Web Output 222 | *.[Pp]ublish.xml 223 | *.azurePubxml 224 | # Note: Comment the next line if you want to checkin your web deploy settings, 225 | # but database connection strings (with potential passwords) will be unencrypted 226 | *.pubxml 227 | *.publishproj 228 | 229 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 230 | # checkin your Azure Web App publish settings, but sensitive information contained 231 | # in these scripts will be unencrypted 232 | PublishScripts/ 233 | 234 | # NuGet Packages 235 | *.nupkg 236 | # NuGet Symbol Packages 237 | *.snupkg 238 | # The packages folder can be ignored because of Package Restore 239 | **/[Pp]ackages/* 240 | # except build/, which is used as an MSBuild target. 241 | !**/[Pp]ackages/build/ 242 | # Uncomment if necessary however generally it will be regenerated when needed 243 | #!**/[Pp]ackages/repositories.config 244 | # NuGet v3's project.json files produces more ignorable files 245 | *.nuget.props 246 | *.nuget.targets 247 | 248 | # Microsoft Azure Build Output 249 | csx/ 250 | *.build.csdef 251 | 252 | # Microsoft Azure Emulator 253 | ecf/ 254 | rcf/ 255 | 256 | # Windows Store app package directories and files 257 | AppPackages/ 258 | BundleArtifacts/ 259 | Package.StoreAssociation.xml 260 | _pkginfo.txt 261 | *.appx 262 | *.appxbundle 263 | *.appxupload 264 | 265 | # Visual Studio cache files 266 | # files ending in .cache can be ignored 267 | *.[Cc]ache 268 | # but keep track of directories ending in .cache 269 | !?*.[Cc]ache/ 270 | 271 | # Others 272 | ClientBin/ 273 | ~$* 274 | *~ 275 | *.dbmdl 276 | *.dbproj.schemaview 277 | *.jfm 278 | *.pfx 279 | *.publishsettings 280 | orleans.codegen.cs 281 | 282 | # Including strong name files can present a security risk 283 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 284 | #*.snk 285 | 286 | # Since there are multiple workflows, uncomment next line to ignore bower_components 287 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 288 | #bower_components/ 289 | 290 | # RIA/Silverlight projects 291 | Generated_Code/ 292 | 293 | # Backup & report files from converting an old project file 294 | # to a newer Visual Studio version. Backup files are not needed, 295 | # because we have git ;-) 296 | _UpgradeReport_Files/ 297 | Backup*/ 298 | UpgradeLog*.XML 299 | UpgradeLog*.htm 300 | ServiceFabricBackup/ 301 | *.rptproj.bak 302 | 303 | # SQL Server files 304 | *.mdf 305 | *.ldf 306 | *.ndf 307 | 308 | # Business Intelligence projects 309 | *.rdl.data 310 | *.bim.layout 311 | *.bim_*.settings 312 | *.rptproj.rsuser 313 | *- [Bb]ackup.rdl 314 | *- [Bb]ackup ([0-9]).rdl 315 | *- [Bb]ackup ([0-9][0-9]).rdl 316 | 317 | # Microsoft Fakes 318 | FakesAssemblies/ 319 | 320 | # GhostDoc plugin setting file 321 | *.GhostDoc.xml 322 | 323 | # Node.js Tools for Visual Studio 324 | .ntvs_analysis.dat 325 | node_modules/ 326 | 327 | # Visual Studio 6 build log 328 | *.plg 329 | 330 | # Visual Studio 6 workspace options file 331 | *.opt 332 | 333 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 334 | *.vbw 335 | 336 | # Visual Studio LightSwitch build output 337 | **/*.HTMLClient/GeneratedArtifacts 338 | **/*.DesktopClient/GeneratedArtifacts 339 | **/*.DesktopClient/ModelManifest.xml 340 | **/*.Server/GeneratedArtifacts 341 | **/*.Server/ModelManifest.xml 342 | _Pvt_Extensions 343 | 344 | # Paket dependency manager 345 | .paket/paket.exe 346 | paket-files/ 347 | 348 | # FAKE - F# Make 349 | .fake/ 350 | 351 | # CodeRush personal settings 352 | .cr/personal 353 | 354 | # Python Tools for Visual Studio (PTVS) 355 | __pycache__/ 356 | *.pyc 357 | 358 | # Cake - Uncomment if you are using it 359 | # tools/** 360 | # !tools/packages.config 361 | 362 | # Tabs Studio 363 | *.tss 364 | 365 | # Telerik's JustMock configuration file 366 | *.jmconfig 367 | 368 | # BizTalk build output 369 | *.btp.cs 370 | *.btm.cs 371 | *.odx.cs 372 | *.xsd.cs 373 | 374 | # OpenCover UI analysis results 375 | OpenCover/ 376 | 377 | # Azure Stream Analytics local run output 378 | ASALocalRun/ 379 | 380 | # MSBuild Binary and Structured Log 381 | *.binlog 382 | 383 | # NVidia Nsight GPU debugger configuration file 384 | *.nvuser 385 | 386 | # MFractors (Xamarin productivity tool) working folder 387 | .mfractor/ 388 | 389 | # Local History for Visual Studio 390 | .localhistory/ 391 | 392 | # BeatPulse healthcheck temp database 393 | healthchecksdb 394 | 395 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 396 | MigrationBackup/ 397 | 398 | # Ionide (cross platform F# VS Code tools) working folder 399 | .ionide/ 400 | .git -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.1.0 2 | 3 | First release -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | include(pico_sdk_import.cmake) 3 | 4 | project(launcher C CXX ASM) 5 | pico_sdk_init() 6 | 7 | 8 | option(VGA "Enable VGA" OFF) 9 | option(TFT "Enable TFT display" OFF) 10 | option(ILI9341 "Enable TFT ILI9341 display" OFF) 11 | option(HDMI "Enable HDMI display" OFF) 12 | option(TV "Enable TV composite output" OFF) 13 | option(SOFTTV "Enable TV soft composite output" OFF) 14 | 15 | # 16384 16 | # 4096 17 | option(FLASH_SIZE "Target Flash Size" 2048) 18 | 19 | if(NOT FLASH_SIZE) 20 | set(FLASH_SIZE 2048) 21 | endif () 22 | 23 | if(NOT LAUNCHER_VERSION) 24 | set(LAUNCHER_VERSION 2) 25 | endif () 26 | 27 | SET(BUILD_NAME "${PROJECT_NAME}-${FLASH_SIZE}K-${LAUNCHER_VERSION}") 28 | 29 | set(CMAKE_C_STANDARD 11) 30 | set(CMAKE_CXX_STANDARD 17) 31 | 32 | set(OUTPUT_DIR "${CMAKE_SOURCE_DIR}/bin/${CMAKE_BUILD_TYPE}") 33 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${OUTPUT_DIR}") 34 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIR}") 35 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_DIR}") 36 | 37 | set(FAMILY rp2040) 38 | set(BOARD pico_sdk) 39 | 40 | add_subdirectory(drivers/ps2) 41 | add_subdirectory(drivers/fatfs) 42 | add_subdirectory(drivers/sdcard) 43 | add_subdirectory(drivers/nespad) 44 | 45 | add_subdirectory(drivers/vga-nextgen) 46 | add_subdirectory(drivers/st7789) 47 | add_subdirectory(drivers/hdmi) 48 | add_subdirectory(drivers/tv) 49 | add_subdirectory(drivers/tv-software) 50 | add_subdirectory(drivers/graphics) 51 | 52 | # INCLUDE FILES THAT SHOULD BE COMPILED: 53 | file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c" "drivers/usb/*.c") 54 | 55 | message(STATUS "Add source files:") 56 | foreach (SRC_FILE IN LISTS SRC) 57 | message(STATUS "${SRC_FILE}") 58 | endforeach () 59 | message(STATUS "") 60 | 61 | add_executable(${PROJECT_NAME} ${SRC}) 62 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) 63 | 64 | configure_file(memmap.ld.in memmap.ld @ONLY) 65 | pico_set_linker_script(${PROJECT_NAME} ${CMAKE_CURRENT_BINARY_DIR}/memmap.ld) 66 | 67 | pico_set_program_name(${PROJECT_NAME} "RetroPico Launcher") 68 | pico_set_program_version(${PROJECT_NAME} "test") 69 | 70 | target_link_libraries(${PROJECT_NAME} PRIVATE 71 | ps2 72 | sdcard 73 | fatfs 74 | nespad 75 | 76 | graphics 77 | 78 | pico_runtime 79 | pico_stdlib 80 | hardware_pio 81 | 82 | pico_multicore 83 | hardware_flash 84 | 85 | tinyusb_board 86 | tinyusb_device 87 | ) 88 | 89 | #family_configure_device_example(${PROJECT_NAME} noos) 90 | target_link_options(${PROJECT_NAME} PRIVATE -Xlinker --print-memory-usage) 91 | target_include_directories(${PROJECT_NAME} PRIVATE 92 | ${CMAKE_CURRENT_SOURCE_DIR}/src 93 | ${CMAKE_CURRENT_SOURCE_DIR}/drivers/usb 94 | ) 95 | 96 | if (CMAKE_C_COMPILER_ID STREQUAL "GNU") 97 | target_compile_options(${PROJECT_NAME} PUBLIC 98 | -Wno-error=suggest-attribute=format 99 | -Wno-error=cast-qual 100 | -Wno-error=unused-parameter 101 | -Wno-error=conversion 102 | -Wno-error=format= 103 | -Wno-error=sign-compare 104 | -Wno-error=missing-field-initializers 105 | -Wno-error=switch 106 | -Wno-error=implicit-fallthrough= 107 | -Wno-error=stringop-truncation 108 | -Wno-error=restrict 109 | -w 110 | ) 111 | endif () 112 | 113 | target_compile_definitions(${PROJECT_NAME} PRIVATE 114 | PICO_FLASH_SIZE_BYTES=16777216 115 | 116 | # VGA 8 pins starts from pin: 117 | VGA_BASE_PIN=6 118 | 119 | # HDMI 8 pins starts from pin: 120 | HDMI_BASE_PIN=6 121 | 122 | # TFT 123 | TFT_CS_PIN=6 124 | TFT_RST_PIN=8 125 | TFT_LED_PIN=9 126 | TFT_DC_PIN=10 127 | TFT_DATA_PIN=12 128 | TFT_CLK_PIN=13 129 | 130 | # SDCARD 131 | SDCARD_PIN_SPI0_SCK=2 132 | SDCARD_PIN_SPI0_MOSI=3 133 | SDCARD_PIN_SPI0_MISO=4 134 | SDCARD_PIN_SPI0_CS=5 135 | 136 | # PS2 keyboard 137 | KBD_CLOCK_PIN=0 138 | KBD_DATA_PIN=1 139 | 140 | # NES Gamepad 141 | NES_GPIO_CLK=14 142 | NES_GPIO_DATA=16 143 | NES_GPIO_LAT=15 144 | ) 145 | 146 | 147 | pico_define_boot_stage2(slower_boot2 ${PICO_DEFAULT_BOOT_STAGE2_FILE}) 148 | math(EXPR CUSTOM_XIP_BASE "1024 * (${FLASH_SIZE} - 68) + 0x10000000") 149 | message(STATUS "${CUSTOM_XIP_BASE}") 150 | target_compile_definitions(slower_boot2 PRIVATE CUSTOM_XIP_BASE=${CUSTOM_XIP_BASE}) 151 | target_compile_definitions(slower_boot2 PRIVATE PICO_FLASH_SPI_CLKDIV=4) 152 | pico_set_boot_stage2(${PROJECT_NAME} slower_boot2) 153 | 154 | 155 | target_compile_definitions(${PROJECT_NAME} PRIVATE FLASH_SIZE=${FLASH_SIZE}) 156 | 157 | IF (TFT) 158 | target_link_libraries(${PROJECT_NAME} PRIVATE st7789) 159 | target_compile_definitions(${PROJECT_NAME} PRIVATE TFT) 160 | SET(BUILD_NAME "${BUILD_NAME}-TFT") 161 | IF (ILI9341) 162 | SET(BUILD_NAME "${BUILD_NAME}-ILI9341") 163 | target_compile_definitions(${PROJECT_NAME} PRIVATE ILI9341) 164 | ELSE () 165 | SET(BUILD_NAME "${BUILD_NAME}-ST7789") 166 | ENDIF () 167 | ELSEIF (HDMI) 168 | target_link_libraries(${PROJECT_NAME} PRIVATE hdmi) 169 | target_compile_definitions(${PROJECT_NAME} PRIVATE HDMI) 170 | SET(BUILD_NAME "${BUILD_NAME}-HDMI") 171 | ELSEIF (TV) 172 | target_compile_definitions(${PROJECT_NAME} PRIVATE TV) 173 | target_link_libraries(${PROJECT_NAME} PRIVATE tv) 174 | SET(BUILD_NAME "${BUILD_NAME}-TV") 175 | ELSEIF(TV) 176 | target_compile_definitions(${PROJECT_NAME} PRIVATE TV) 177 | target_link_libraries(${PROJECT_NAME} PRIVATE tv) 178 | SET(BUILD_NAME "${BUILD_NAME}-TV") 179 | ELSEIF(SOFTTV) 180 | target_compile_definitions(${PROJECT_NAME} PRIVATE SOFTTV) 181 | target_link_libraries(${PROJECT_NAME} PRIVATE tv-software) 182 | SET(BUILD_NAME "${BUILD_NAME}-TV-SOFT") 183 | ELSE() 184 | target_compile_definitions(${PROJECT_NAME} PRIVATE VGA) 185 | target_link_libraries(${PROJECT_NAME} PRIVATE vga-nextgen) 186 | SET(BUILD_NAME "${BUILD_NAME}-VGA") 187 | ENDIF() 188 | 189 | set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "${BUILD_NAME}") 190 | 191 | pico_enable_stdio_uart(${PROJECT_NAME} 0) 192 | pico_enable_stdio_usb(${PROJECT_NAME} 0) 193 | 194 | #pico_set_binary_type(${PROJECT_NAME} no_flash) 195 | #pico_set_binary_type(${PROJECT_NAME} copy_to_ram) 196 | 197 | pico_add_extra_outputs(${PROJECT_NAME}) 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pico-launcher - Bootloader Firmware Flasher / Launcher for MURMULATOR Devboard 2 | Bootloader firmware flasher / launcher for MURMULATOR devboard 3 | 4 | # How to Use 5 | 1) Copy all your Pico firmwares to the SD card. 6 | 2) Reboot PICO by holding down SELECT on the Gamepad or F11 on the keyboard and select default firmware. 7 | 8 | Additionally, you can: 9 | - Hold F12 or START button while booting to enter firmware update mode. 10 | - Use F10 or the A button in the launcher to enter SD card-reader mode. 11 | 12 | # Compiling 13 | To compile it yourself: 14 | 15 | 1. Copy the file ``boot2\exit_from_boot2.S`` from the repository to your Pico SDK at ``pico-sdk\src\rp2_common\boot_stage2\asminclude\boot2_helpers\exit_from_boot2.S`` 16 | 2. Build using the "MinSizeRel" build type. 17 | -------------------------------------------------------------------------------- /boot2/exit_from_boot2.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _BOOT2_HELPER_EXIT_FROM_BOOT2 8 | #define _BOOT2_HELPER_EXIT_FROM_BOOT2 9 | 10 | #ifndef CUSTOM_XIP_BASE 11 | #define CUSTOM_XIP_BASE (XIP_BASE + 0x100) 12 | #endif 13 | 14 | #include "hardware/regs/m0plus.h" 15 | 16 | // If entered from the bootrom, lr (which we earlier pushed) will be 0, 17 | // and we vector through the table at the start of the main flash image. 18 | // Any regular function call will have a nonzero value for lr. 19 | check_return: 20 | pop {r0} 21 | cmp r0, #0 22 | beq vector_into_flash 23 | bx r0 24 | vector_into_flash: 25 | ldr r0, =(CUSTOM_XIP_BASE) 26 | ldr r1, =(PPB_BASE + M0PLUS_VTOR_OFFSET) 27 | str r0, [r1] 28 | ldmia r0, {r0, r1} 29 | msr msp, r0 30 | bx r1 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /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 hardware_pwm) 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 = 26, 43 | .clock_pin_base = 27, 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 | 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 | -------------------------------------------------------------------------------- /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/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; 251 | lcd_set_window(graphics_buffer_shift_x, graphics_buffer_shift_y, graphics_buffer_width, 252 | graphics_buffer_height); 253 | uint32_t i = graphics_buffer_width * graphics_buffer_height; 254 | start_pixels(); 255 | // st7789_dma_pixels(graphics_buffer, i); 256 | while (--i) { 257 | st7789_lcd_put_pixel(pio, sm, palette[*bitmap++]); 258 | } 259 | 260 | stop_pixels(); 261 | } 262 | } 263 | 264 | // st7789_lcd_wait_idle(pio, sm); 265 | } 266 | 267 | 268 | void graphics_set_palette(const uint8_t i, const uint32_t color) { 269 | palette[i] = (uint16_t)color; 270 | } 271 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /memmap.ld.in: -------------------------------------------------------------------------------- 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 | BOOT(rx) : ORIGIN = 0x10000000, LENGTH = 4k 27 | /* @FLASH_SIZE@Kb */ 28 | FLASH(rx) : ORIGIN = 0x10000000+(@FLASH_SIZE@k-68k), LENGTH = 68k 29 | RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k 30 | SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k 31 | SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k 32 | } 33 | 34 | ENTRY(_entry_point) 35 | 36 | SECTIONS 37 | { 38 | /* Second stage bootloader is prepended to the image. It must be 256 bytes big 39 | and checksummed. It is usually built by the boot_stage2 target 40 | in the Raspberry Pi Pico SDK 41 | */ 42 | .flash_begin : { 43 | __flash_binary_start = .; 44 | } > FLASH 45 | 46 | .boot2 : { 47 | __boot2_start__ = .; 48 | KEEP (*(.boot2)) 49 | __boot2_end__ = .; 50 | } > BOOT 51 | 52 | ASSERT(__boot2_end__ - __boot2_start__ == 256, 53 | "ERROR: Pico second stage bootloader must be 256 bytes in size") 54 | 55 | 56 | 57 | 58 | 59 | 60 | /* The second stage will always enter the image at the start of .text. 61 | The debugger will use the ELF entry point, which is the _entry_point 62 | symbol if present, otherwise defaults to start of .text. 63 | This can be used to transfer control back to the bootrom on debugger 64 | launches only, to perform proper flash setup. 65 | */ 66 | 67 | .text : { 68 | __logical_binary_start = .; 69 | KEEP (*(.vectors)) 70 | KEEP (*(.binary_info_header)) 71 | __binary_info_header_end = .; 72 | KEEP (*(.reset)) 73 | /* TODO revisit this now memset/memcpy/float in ROM */ 74 | /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from 75 | * FLASH ... we will include any thing excluded here in .data below by default */ 76 | *(.init) 77 | *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) 78 | *(.fini) 79 | /* Pull all c'tors into .text */ 80 | *crtbegin.o(.ctors) 81 | *crtbegin?.o(.ctors) 82 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) 83 | *(SORT(.ctors.*)) 84 | *(.ctors) 85 | /* Followed by destructors */ 86 | *crtbegin.o(.dtors) 87 | *crtbegin?.o(.dtors) 88 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) 89 | *(SORT(.dtors.*)) 90 | *(.dtors) 91 | 92 | *(.eh_frame*) 93 | . = ALIGN(4); 94 | } > FLASH 95 | 96 | .rodata : { 97 | *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) 98 | . = ALIGN(4); 99 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) 100 | . = ALIGN(4); 101 | } > FLASH 102 | 103 | .ARM.extab : 104 | { 105 | *(.ARM.extab* .gnu.linkonce.armextab.*) 106 | } > FLASH 107 | 108 | __exidx_start = .; 109 | .ARM.exidx : 110 | { 111 | *(.ARM.exidx* .gnu.linkonce.armexidx.*) 112 | } > FLASH 113 | __exidx_end = .; 114 | 115 | /* Machine inspectable binary information */ 116 | . = ALIGN(4); 117 | __binary_info_start = .; 118 | .binary_info : 119 | { 120 | KEEP(*(.binary_info.keep.*)) 121 | *(.binary_info.*) 122 | } > FLASH 123 | __binary_info_end = .; 124 | . = ALIGN(4); 125 | 126 | .ram_vector_table (NOLOAD): { 127 | *(.ram_vector_table) 128 | } > RAM 129 | 130 | .data : { 131 | __data_start__ = .; 132 | *(vtable) 133 | 134 | *(.time_critical*) 135 | 136 | /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ 137 | *(.text*) 138 | . = ALIGN(4); 139 | *(.rodata*) 140 | . = ALIGN(4); 141 | 142 | *(.data*) 143 | 144 | . = ALIGN(4); 145 | *(.after_data.*) 146 | . = ALIGN(4); 147 | /* preinit data */ 148 | PROVIDE_HIDDEN (__mutex_array_start = .); 149 | KEEP(*(SORT(.mutex_array.*))) 150 | KEEP(*(.mutex_array)) 151 | PROVIDE_HIDDEN (__mutex_array_end = .); 152 | 153 | . = ALIGN(4); 154 | /* preinit data */ 155 | PROVIDE_HIDDEN (__preinit_array_start = .); 156 | KEEP(*(SORT(.preinit_array.*))) 157 | KEEP(*(.preinit_array)) 158 | PROVIDE_HIDDEN (__preinit_array_end = .); 159 | 160 | . = ALIGN(4); 161 | /* init data */ 162 | PROVIDE_HIDDEN (__init_array_start = .); 163 | KEEP(*(SORT(.init_array.*))) 164 | KEEP(*(.init_array)) 165 | PROVIDE_HIDDEN (__init_array_end = .); 166 | 167 | . = ALIGN(4); 168 | /* finit data */ 169 | PROVIDE_HIDDEN (__fini_array_start = .); 170 | *(SORT(.fini_array.*)) 171 | *(.fini_array) 172 | PROVIDE_HIDDEN (__fini_array_end = .); 173 | 174 | *(.jcr) 175 | . = ALIGN(4); 176 | /* All data end */ 177 | __data_end__ = .; 178 | } > RAM AT> FLASH 179 | /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ 180 | __etext = LOADADDR(.data); 181 | 182 | .uninitialized_data (NOLOAD): { 183 | . = ALIGN(4); 184 | *(.uninitialized_data*) 185 | } > RAM 186 | 187 | /* Start and end symbols must be word-aligned */ 188 | .scratch_x : { 189 | __scratch_x_start__ = .; 190 | *(.scratch_x.*) 191 | . = ALIGN(4); 192 | __scratch_x_end__ = .; 193 | } > SCRATCH_X AT > FLASH 194 | __scratch_x_source__ = LOADADDR(.scratch_x); 195 | 196 | .scratch_y : { 197 | __scratch_y_start__ = .; 198 | *(.scratch_y.*) 199 | . = ALIGN(4); 200 | __scratch_y_end__ = .; 201 | } > SCRATCH_Y AT > FLASH 202 | __scratch_y_source__ = LOADADDR(.scratch_y); 203 | 204 | .bss : { 205 | . = ALIGN(4); 206 | __bss_start__ = .; 207 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) 208 | *(COMMON) 209 | . = ALIGN(4); 210 | __bss_end__ = .; 211 | } > RAM 212 | 213 | .heap (NOLOAD): 214 | { 215 | __end__ = .; 216 | end = __end__; 217 | KEEP(*(.heap*)) 218 | __HeapLimit = .; 219 | } > RAM 220 | 221 | /* .stack*_dummy section doesn't contains any symbols. It is only 222 | * used for linker to calculate size of stack sections, and assign 223 | * values to stack symbols later 224 | * 225 | * stack1 section may be empty/missing if platform_launch_core1 is not used */ 226 | 227 | /* by default we put core 0 stack at the end of scratch Y, so that if core 1 228 | * stack is not used then all of SCRATCH_X is free. 229 | */ 230 | .stack1_dummy (NOLOAD): 231 | { 232 | *(.stack1*) 233 | } > SCRATCH_X 234 | .stack_dummy (NOLOAD): 235 | { 236 | KEEP(*(.stack*)) 237 | } > SCRATCH_Y 238 | 239 | .flash_end : { 240 | PROVIDE(__flash_binary_end = .); 241 | } > FLASH 242 | 243 | /* stack limit is poorly named, but historically is maximum heap ptr */ 244 | __StackLimit = ORIGIN(RAM) + LENGTH(RAM); 245 | __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); 246 | __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); 247 | __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); 248 | __StackBottom = __StackTop - SIZEOF(.stack_dummy); 249 | PROVIDE(__stack = __StackTop); 250 | 251 | /* Check if data + heap + stack exceeds RAM limit */ 252 | ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") 253 | 254 | ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") 255 | /* todo assert on extra code */ 256 | } 257 | 258 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /uf2_vis.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | f=open("/home/alex/Рабочий стол/APPLICATION128.uf2","rb") 4 | 5 | 6 | fdata=f.read() 7 | 8 | f.close() 9 | 10 | inx=1 11 | for i in range(len(fdata)): 12 | if i%512==0: 13 | print("\n inx="+str(inx)) 14 | inx+=1 15 | bsize=int.from_bytes(fdata[i+16:i+20],"little") 16 | binx=int.from_bytes(fdata[i+20:i+24],"little") 17 | bN=int.from_bytes(fdata[i+24:i+28],"little") 18 | baddr=int.from_bytes(fdata[i+12:i+16],"little") 19 | bId=int.from_bytes(fdata[i+28:i+32],"little") 20 | print("size="+str(bsize)) 21 | print("block= "+str(binx)+" ,total= "+str(bN)) 22 | print("addr= "+hex(baddr)) 23 | print("Id= "+hex(bId)) 24 | 25 | print(hex(int.from_bytes(fdata[i:i+4],"little"))) 26 | 27 | print("size= "+str(bN*256)) 28 | 29 | --------------------------------------------------------------------------------