commit 8b9d2d93a1c09a59ac31479ef3709e07ec929116 Author: Kevin Thomas Date: Fri Oct 6 14:27:20 2023 -0400 Initial commit with strict idiomatic rust enforcement diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..30fc821 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +*.c linguist-detectable=true +*.cmake linguist-detectable=false +*.py linguist-detectable=false +*.txt linguist-detectable=false +*.json linguist-detectable=false +*.ps1 linguist-detectable=false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1430494 --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb +*.rep +*.gpr + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# Build output +build/ +target/ + +# Node.js and PDF generation script +node_modules/ +package-lock.json +package.json +svg-to-pdf.ps1 diff --git a/0x0001_hello-world/.gitignore b/0x0001_hello-world/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0001_hello-world/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0001_hello-world/.vscode/c_cpp_properties.json b/0x0001_hello-world/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0001_hello-world/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0001_hello-world/.vscode/cmake-kits.json b/0x0001_hello-world/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0001_hello-world/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0001_hello-world/.vscode/extensions.json b/0x0001_hello-world/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0001_hello-world/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0001_hello-world/.vscode/launch.json b/0x0001_hello-world/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0001_hello-world/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0001_hello-world/.vscode/settings.json b/0x0001_hello-world/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/0x0001_hello-world/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/0x0001_hello-world/.vscode/tasks.json b/0x0001_hello-world/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0001_hello-world/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0001_hello-world/0x0001_hello-world.c b/0x0001_hello-world/0x0001_hello-world.c new file mode 100644 index 0000000..7f7b062 --- /dev/null +++ b/0x0001_hello-world/0x0001_hello-world.c @@ -0,0 +1,46 @@ +/** + * @file 0x0001_hello-world.c + * @brief Hello World: print a greeting over UART in an infinite loop + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the minimal "hello, world" program on the Raspberry Pi Pico 2. + * Initializes stdio over UART and prints a greeting in an infinite loop. + * + * Wiring: + * No external wiring required (USB serial). + */ + +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + while (true) { + printf("hello, world\r\n"); + } +} diff --git a/0x0001_hello-world/CMakeLists.txt b/0x0001_hello-world/CMakeLists.txt new file mode 100644 index 0000000..31d8fc5 --- /dev/null +++ b/0x0001_hello-world/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0001_hello-world C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0001_hello-world 0x0001_hello-world.c ) + +pico_set_program_name(0x0001_hello-world "0x0001_hello-world") +pico_set_program_version(0x0001_hello-world "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0001_hello-world 1) +pico_enable_stdio_usb(0x0001_hello-world 0) + +# Add the standard library to the build +target_link_libraries(0x0001_hello-world + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0001_hello-world PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0001_hello-world) + diff --git a/0x0001_hello-world/pico_sdk_import.cmake b/0x0001_hello-world/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0001_hello-world/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0005_intro-to-variables/.gitignore b/0x0005_intro-to-variables/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0005_intro-to-variables/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0005_intro-to-variables/.vscode/c_cpp_properties.json b/0x0005_intro-to-variables/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0005_intro-to-variables/.vscode/cmake-kits.json b/0x0005_intro-to-variables/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0005_intro-to-variables/.vscode/extensions.json b/0x0005_intro-to-variables/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0005_intro-to-variables/.vscode/launch.json b/0x0005_intro-to-variables/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0005_intro-to-variables/.vscode/settings.json b/0x0005_intro-to-variables/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0005_intro-to-variables/.vscode/tasks.json b/0x0005_intro-to-variables/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0005_intro-to-variables/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0005_intro-to-variables/0x0005_intro-to-variables.c b/0x0005_intro-to-variables/0x0005_intro-to-variables.c new file mode 100644 index 0000000..29f6b2d --- /dev/null +++ b/0x0005_intro-to-variables/0x0005_intro-to-variables.c @@ -0,0 +1,48 @@ +/** + * @file 0x0005_intro-to-variables.c + * @brief Introduction to variables: declare, assign, and print a uint8_t + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates declaring, initializing, and reassigning a uint8_t variable. + * Prints the variable value over UART in an infinite loop. + * + * Wiring: + * No external wiring required (USB serial). + */ + +#include +#include "pico/stdlib.h" + +int main(void) { + uint8_t age = 42; + age = 43; + stdio_init_all(); + while (true) { + printf("age: %d\r\n", age); + } +} diff --git a/0x0005_intro-to-variables/CMakeLists.txt b/0x0005_intro-to-variables/CMakeLists.txt new file mode 100644 index 0000000..673ff61 --- /dev/null +++ b/0x0005_intro-to-variables/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0005_intro-to-variables C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0005_intro-to-variables 0x0005_intro-to-variables.c ) + +pico_set_program_name(0x0005_intro-to-variables "0x0005_intro-to-variables") +pico_set_program_version(0x0005_intro-to-variables "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0005_intro-to-variables 1) +pico_enable_stdio_usb(0x0005_intro-to-variables 0) + +# Add the standard library to the build +target_link_libraries(0x0005_intro-to-variables + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0005_intro-to-variables PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0005_intro-to-variables) + diff --git a/0x0005_intro-to-variables/pico_sdk_import.cmake b/0x0005_intro-to-variables/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0005_intro-to-variables/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables-a/.gitignore b/0x0008_uninitialized-variables-a/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables-a/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables-a/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables-a/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..27d83bd --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/c_cpp_properties.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables-a/.vscode/cmake-kits.json b/0x0008_uninitialized-variables-a/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables-a/.vscode/extensions.json b/0x0008_uninitialized-variables-a/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables-a/.vscode/launch.json b/0x0008_uninitialized-variables-a/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables-a/.vscode/settings.json b/0x0008_uninitialized-variables-a/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0008_uninitialized-variables-a/.vscode/tasks.json b/0x0008_uninitialized-variables-a/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables-a/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables-a/0x0008_uninitialized-variables-a.c b/0x0008_uninitialized-variables-a/0x0008_uninitialized-variables-a.c new file mode 100644 index 0000000..9d2ddb3 --- /dev/null +++ b/0x0008_uninitialized-variables-a/0x0008_uninitialized-variables-a.c @@ -0,0 +1,53 @@ +/** + * @file 0x0008_uninitialized-variables-a.c + * @brief Blink LED using SDK gpio functions (no UART) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Blinks an LED on GPIO16 using the standard Pico SDK gpio_init / gpio_put + * functions. No UART output; pure GPIO blink demonstration. + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the LED */ +#define LED_PIN 16 + +int main(void) { + gpio_init(LED_PIN); + gpio_set_dir(LED_PIN, GPIO_OUT); + while (true) { + gpio_put(LED_PIN, 1); + sleep_ms(500); + gpio_put(LED_PIN, 0); + sleep_ms(500); + } +} diff --git a/0x0008_uninitialized-variables-a/CMakeLists.txt b/0x0008_uninitialized-variables-a/CMakeLists.txt new file mode 100644 index 0000000..347375d --- /dev/null +++ b/0x0008_uninitialized-variables-a/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables-a C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables-a 0x0008_uninitialized-variables-a.c ) + +pico_set_program_name(0x0008_uninitialized-variables-a "0x0008_uninitialized-variables-a") +pico_set_program_version(0x0008_uninitialized-variables-a "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables-a 0) +pico_enable_stdio_usb(0x0008_uninitialized-variables-a 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables-a + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables-a PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables-a) + diff --git a/0x0008_uninitialized-variables-a/pico_sdk_import.cmake b/0x0008_uninitialized-variables-a/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables-a/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables-b/.gitignore b/0x0008_uninitialized-variables-b/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables-b/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables-b/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables-b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables-b/.vscode/cmake-kits.json b/0x0008_uninitialized-variables-b/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables-b/.vscode/extensions.json b/0x0008_uninitialized-variables-b/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables-b/.vscode/launch.json b/0x0008_uninitialized-variables-b/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables-b/.vscode/settings.json b/0x0008_uninitialized-variables-b/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0008_uninitialized-variables-b/.vscode/tasks.json b/0x0008_uninitialized-variables-b/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables-b/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables-b/0x0008_uninitialized-variables-b.c b/0x0008_uninitialized-variables-b/0x0008_uninitialized-variables-b.c new file mode 100644 index 0000000..b42c544 --- /dev/null +++ b/0x0008_uninitialized-variables-b/0x0008_uninitialized-variables-b.c @@ -0,0 +1,80 @@ +/** + * @file 0x0008_uninitialized-variables-b.c + * @brief Blink LED using gpioc coprocessor bit-level functions + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Blinks an LED on GPIO16 using the RP2350 gpioc coprocessor bit-level + * functions (gpioc_bit_oe_put / gpioc_bit_out_put) instead of the standard + * gpio_init / gpio_put SDK calls. + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the LED */ +#define LED_PIN 16 + +/** + * @brief Initialize GPIO16 for output using coprocessor bit functions + * + * @details Configures the pad for SIO, sets direction to input first, + * clears the output, selects SIO function, then enables output. + * + * @retval None + */ +static void gpio_coprocessor_init(void) { + gpio_set_dir(LED_PIN, GPIO_IN); + gpio_put(LED_PIN, 0); + gpio_set_function(LED_PIN, GPIO_FUNC_SIO); + gpioc_bit_oe_put(LED_PIN, GPIO_OUT); +} + +/** + * @brief Blink LED using coprocessor bit output functions + * + * @details Toggles the LED on and off with 500ms delays using + * gpioc_bit_out_put and sleep_us. + * + * @retval None + */ +static void blink_cycle(void) { + gpioc_bit_out_put(LED_PIN, 1); + sleep_us(500 * 1000ull); + gpioc_bit_out_put(LED_PIN, 0); + sleep_us(500 * 1000ull); +} + +int main(void) { + gpio_coprocessor_init(); + while (true) { + blink_cycle(); + } +} diff --git a/0x0008_uninitialized-variables-b/CMakeLists.txt b/0x0008_uninitialized-variables-b/CMakeLists.txt new file mode 100644 index 0000000..24d559f --- /dev/null +++ b/0x0008_uninitialized-variables-b/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables-b C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables-b 0x0008_uninitialized-variables-b.c ) + +pico_set_program_name(0x0008_uninitialized-variables-b "0x0008_uninitialized-variables-b") +pico_set_program_version(0x0008_uninitialized-variables-b "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables-b 0) +pico_enable_stdio_usb(0x0008_uninitialized-variables-b 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables-b + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables-b PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables-b) + diff --git a/0x0008_uninitialized-variables-b/pico_sdk_import.cmake b/0x0008_uninitialized-variables-b/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables-b/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables-c/.gitignore b/0x0008_uninitialized-variables-c/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables-c/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables-c/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables-c/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables-c/.vscode/cmake-kits.json b/0x0008_uninitialized-variables-c/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables-c/.vscode/extensions.json b/0x0008_uninitialized-variables-c/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables-c/.vscode/launch.json b/0x0008_uninitialized-variables-c/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables-c/.vscode/settings.json b/0x0008_uninitialized-variables-c/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0008_uninitialized-variables-c/.vscode/tasks.json b/0x0008_uninitialized-variables-c/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables-c/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables-c/0x0008_uninitialized-variables-c.c b/0x0008_uninitialized-variables-c/0x0008_uninitialized-variables-c.c new file mode 100644 index 0000000..40771de --- /dev/null +++ b/0x0008_uninitialized-variables-c/0x0008_uninitialized-variables-c.c @@ -0,0 +1,85 @@ +/** + * @file 0x0008_uninitialized-variables-c.c + * @brief Blink LED using direct register-level pad and IO bank configuration + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Blinks an LED on GPIO16 using direct register writes to PADS_BANK0 and + * IO_BANK0 for pad configuration, plus gpioc coprocessor bit-level functions + * for output enable and data. Demonstrates the register-level equivalent of + * gpio_init / gpio_set_function. + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the LED */ +#define LED_PIN 16 + +/** + * @brief Configure pad and IO bank registers for GPIO16 as SIO output + * + * @details Sets PADS_BANK0 IE/OD bits, assigns FUNCSEL to SIO in IO_BANK0, + * clears the ISO bit, and enables output via the coprocessor. + * + * @retval None + */ +static void configure_pad_and_iobank(void) { + gpioc_bit_oe_put(LED_PIN, GPIO_OUT); + gpioc_bit_out_put(LED_PIN, 0); + hw_write_masked(&pads_bank0_hw->io[LED_PIN], + PADS_BANK0_GPIO0_IE_BITS, + PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS); + io_bank0_hw->io[LED_PIN].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; + hw_clear_bits(&pads_bank0_hw->io[LED_PIN], PADS_BANK0_GPIO0_ISO_BITS); + gpioc_bit_oe_put(LED_PIN, GPIO_OUT); +} + +/** + * @brief Blink LED using coprocessor bit output functions + * + * @details Toggles the LED on and off with 500ms delays using + * gpioc_bit_out_put and sleep_us. + * + * @retval None + */ +static void blink_cycle(void) { + gpioc_bit_out_put(LED_PIN, 1); + sleep_us(500 * 1000ull); + gpioc_bit_out_put(LED_PIN, 0); + sleep_us(500 * 1000ull); +} + +int main(void) { + configure_pad_and_iobank(); + while (true) { + blink_cycle(); + } +} diff --git a/0x0008_uninitialized-variables-c/CMakeLists.txt b/0x0008_uninitialized-variables-c/CMakeLists.txt new file mode 100644 index 0000000..b7e1991 --- /dev/null +++ b/0x0008_uninitialized-variables-c/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables-c C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables-c 0x0008_uninitialized-variables-c.c ) + +pico_set_program_name(0x0008_uninitialized-variables-c "0x0008_uninitialized-variables-c") +pico_set_program_version(0x0008_uninitialized-variables-c "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables-c 0) +pico_enable_stdio_usb(0x0008_uninitialized-variables-c 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables-c + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables-c PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables-c) + diff --git a/0x0008_uninitialized-variables-c/pico_sdk_import.cmake b/0x0008_uninitialized-variables-c/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables-c/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables-d/.gitignore b/0x0008_uninitialized-variables-d/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables-d/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables-d/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables-d/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables-d/.vscode/cmake-kits.json b/0x0008_uninitialized-variables-d/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables-d/.vscode/extensions.json b/0x0008_uninitialized-variables-d/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables-d/.vscode/launch.json b/0x0008_uninitialized-variables-d/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables-d/.vscode/settings.json b/0x0008_uninitialized-variables-d/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0008_uninitialized-variables-d/.vscode/tasks.json b/0x0008_uninitialized-variables-d/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables-d/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables-d/0x0008_uninitialized-variables-d.c b/0x0008_uninitialized-variables-d/0x0008_uninitialized-variables-d.c new file mode 100644 index 0000000..b834115 --- /dev/null +++ b/0x0008_uninitialized-variables-d/0x0008_uninitialized-variables-d.c @@ -0,0 +1,144 @@ +/** + * @file 0x0008_uninitialized-variables-d.c + * @brief Blink LED using pico_default_asm_volatile coprocessor instructions + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Blinks an LED on GPIO16 using pico_default_asm_volatile to emit mcrr + * coprocessor instructions for GPIO output enable and data, plus inline + * assembly for pad configuration (hw_write_masked, IO_BANK0 FUNCSEL, + * hw_clear_bits equivalents). + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the LED */ +#define LED_PIN 16 + +/** + * @brief Enable output and set initial coprocessor OE/OUT via mcrr + * + * @details Issues two mcrr p0 instructions: one to enable output direction, + * one to set the initial output state. + * + * @retval None + */ +static void asm_set_oe_and_out(void) { + pico_default_asm_volatile ("mcrr p0, #4, %0, %1, c4" : : "r" (LED_PIN), "r" (GPIO_OUT)); + pico_default_asm_volatile ("mcrr p0, #4, %0, %1, c4" : : "r" (LED_PIN), "r" (GPIO_OUT)); +} + +/** + * @brief Configure PADS_BANK0 IE/OD bits via inline assembly hw_xor_bits + * + * @details Performs a read-modify-write on the pad register to set IE + * and clear OD, replicating hw_write_masked behavior. + * + * @retval None + */ +static void asm_configure_pad(void) { + pico_default_asm_volatile ( + "ldr r2, [%0]\n" // load current pad register + "eor r2, r2, %1\n" // xor with IE bit + "and r2, r2, %2\n" // mask with (IE|OD) + "eor r2, r2, %1\n" // recombine (hw_xor_bits logic) + "str r2, [%0]\n" // write back + : + : "r" (&pads_bank0_hw->io[LED_PIN]), + "r" (PADS_BANK0_GPIO0_IE_BITS), + "r" (PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS) + : "r2", "memory" + ); +} + +/** + * @brief Set IO_BANK0 FUNCSEL to SIO via inline assembly store + * + * @details Writes the SIO function select value directly to the + * IO_BANK0 GPIO control register. + * + * @retval None + */ +static void asm_set_funcsel(void) { + pico_default_asm_volatile ( + "str %1, [%0]\n" + : + : "r" (&io_bank0_hw->io[LED_PIN].ctrl), + "r" (GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB) + : "memory" + ); +} + +/** + * @brief Clear ISO bits in PADS_BANK0 via inline assembly + * + * @details Performs a read-modify-write to clear the pad isolation + * bit, un-isolating the pad for normal operation. + * + * @retval None + */ +static void asm_clear_iso(void) { + pico_default_asm_volatile ( + "ldr r2, [%0]\n" // load current register value + "bic r2, r2, %1\n" // clear the ISO bits (bit clear) + "str r2, [%0]\n" // write back + : + : "r" (&pads_bank0_hw->io[LED_PIN]), + "r" (PADS_BANK0_GPIO0_ISO_BITS) + : "r2", "memory" + ); +} + +/** + * @brief Blink LED using coprocessor mcrr output instructions + * + * @details Toggles the LED on and off with 500ms delays using + * pico_default_asm_volatile mcrr instructions. + * + * @retval None + */ +static void asm_blink_cycle(void) { + pico_default_asm_volatile ("mcrr p0, #4, %0, %1, c0" : : "r" (LED_PIN), "r" (1)); + sleep_us(500 * 1000ull); + pico_default_asm_volatile ("mcrr p0, #4, %0, %1, c0" : : "r" (LED_PIN), "r" (0)); + sleep_us(500 * 1000ull); +} + +int main(void) { + asm_set_oe_and_out(); + asm_configure_pad(); + asm_set_funcsel(); + asm_clear_iso(); + pico_default_asm_volatile ("mcrr p0, #4, %0, %1, c4" : : "r" (LED_PIN), "r" (GPIO_OUT)); + while (true) { + asm_blink_cycle(); + } +} diff --git a/0x0008_uninitialized-variables-d/CMakeLists.txt b/0x0008_uninitialized-variables-d/CMakeLists.txt new file mode 100644 index 0000000..9e88754 --- /dev/null +++ b/0x0008_uninitialized-variables-d/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables-d C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables-d 0x0008_uninitialized-variables-d.c ) + +pico_set_program_name(0x0008_uninitialized-variables-d "0x0008_uninitialized-variables-d") +pico_set_program_version(0x0008_uninitialized-variables-d "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables-d 0) +pico_enable_stdio_usb(0x0008_uninitialized-variables-d 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables-d + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables-d PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables-d) + diff --git a/0x0008_uninitialized-variables-d/pico_sdk_import.cmake b/0x0008_uninitialized-variables-d/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables-d/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables-e/.gitignore b/0x0008_uninitialized-variables-e/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables-e/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables-e/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables-e/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables-e/.vscode/cmake-kits.json b/0x0008_uninitialized-variables-e/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables-e/.vscode/extensions.json b/0x0008_uninitialized-variables-e/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables-e/.vscode/launch.json b/0x0008_uninitialized-variables-e/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables-e/.vscode/settings.json b/0x0008_uninitialized-variables-e/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0008_uninitialized-variables-e/.vscode/tasks.json b/0x0008_uninitialized-variables-e/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables-e/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables-e/0x0008_uninitialized-variables-e.c b/0x0008_uninitialized-variables-e/0x0008_uninitialized-variables-e.c new file mode 100644 index 0000000..ac572c8 --- /dev/null +++ b/0x0008_uninitialized-variables-e/0x0008_uninitialized-variables-e.c @@ -0,0 +1,107 @@ +/** + * @file 0x0008_uninitialized-variables-e.c + * @brief Blink LED using pure inline ARM assembly with direct register addresses + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Blinks an LED on GPIO16 using pure inline ARM assembly. No SDK calls, no + * includes. Configures PADS_BANK0, IO_BANK0, and GPIO coprocessor via direct + * register addresses and mcrr instructions, with a busy-wait delay loop. + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +int main(void) { + __asm__ volatile ( + // gpio_init(LED_PIN); + /// gpio_set_dir(LED_PIN, GPIO_IN); + //// gpioc_bit_oe_put(LED_PIN, GPIO_OUT); + "movs r4, #0x10\n" // GPIO16 + "movs r5, #0x01\n" // bit 1; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c4\n" // gpioc_bit_oe_put(16, 1); p102 + + // gpio_set_function(LED_PIN, GPIO_FUNC_SIO); + /// hw_write_masked(&pads_bank0_hw->io[LED_PIN], + /// PADS_BANK0_GPIO0_IE_BITS, + /// PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS + /// ); + //// hw_xor_bits(addr, (*addr ^ values) & write_mask); + "ldr r3, =0x40038044\n" // &pads_bank0_hw->io[16]; p785, p796 + "ldr r2, [r3]\n" // load current config + "bic r2, r2, #0x80\n" // clear OD; output disable + "orr r2, r2, #0x40\n" // set IE; enable input buffer + "str r2, [r3]\n" // store updated config + /// io_bank0_hw->io[LED_PIN].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; + "ldr r3, =0x40028084\n" // &io_bank0_hw->io[16].ctrl; p603, p637 + "ldr r2, [r3]\n" // load current config + "bic r2, r2, #0x1f\n" // clear FUNCSEL bits [4:0] + "orr r2, r2, #5\n" // set FUNCSEL = 5 (SIO) + "str r2, [r3]\n" // store updated config + /// hw_clear_bits(&pads_bank0_hw->io[gpio], PADS_BANK0_GPIO0_ISO_BITS); + "ldr r3, =0x40038044\n" // &pads_bank0_hw->io[16]; p785, p796 + "ldr r2, [r3]\n" // load current config + "bic r2, r2, #0x100\n" // clear ISO bit (bit 8) un‑isolate pad + "str r2, [r3]\n" // store updated config + + // gpio_set_dir(LED_PIN, GPIO_OUT); + /// gpioc_bit_oe_put(LED_PIN, GPIO_OUT); + "movs r4, #0x10\n" // GPIO16 + "movs r5, #0x01\n" // bit 1; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c4\n" // gpioc_bit_oe_put(16, 1); p102 + + // while (true) + "1:\n" // loop start + + // gpio_put(LED_PIN, 1); + /// gpioc_bit_out_put(LED_PIN, 1); + "movs r4, #0x10\n" // GPIO16 + "movs r5, #0x01\n" // bit 1; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c0\n" // gpioc_bit_out_put(16, 1) + // sleep_ms(500); + /// sleep_us(500 * 1000ull); + "ldr r2, =0x17D7840\n" // r2 = ~8.4M cycles + "2:\n" // delay loop + "subs r2, r2, #1\n" // decrement counter + "bne 2b\n" // repeat until zero + + // gpio_put(LED_PIN, 1); + /// gpioc_bit_out_put(LED_PIN, 1); + "movs r4, #0x10\n" // GPIO16 + "movs r5, #0x00\n" // bit 0; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c0\n" // gpioc_bit_out_put(16, 0) + // sleep_ms(500); + /// sleep_us(500 * 1000ull); + "ldr r2, =0x17D7840\n" // r2 = ~8.4M cycles + "3:\n" // delay loop + "subs r2, r2, #1\n" // decrement counter + "bne 3b\n" // repeat until zero + + // jmp + "b 1b\n" // repeat forever + ); +} diff --git a/0x0008_uninitialized-variables-e/CMakeLists.txt b/0x0008_uninitialized-variables-e/CMakeLists.txt new file mode 100644 index 0000000..bfc1c28 --- /dev/null +++ b/0x0008_uninitialized-variables-e/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables-e C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables-e 0x0008_uninitialized-variables-e.c ) + +pico_set_program_name(0x0008_uninitialized-variables-e "0x0008_uninitialized-variables-e") +pico_set_program_version(0x0008_uninitialized-variables-e "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables-e 0) +pico_enable_stdio_usb(0x0008_uninitialized-variables-e 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables-e + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables-e PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables-e) + diff --git a/0x0008_uninitialized-variables-e/pico_sdk_import.cmake b/0x0008_uninitialized-variables-e/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables-e/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0008_uninitialized-variables/.gitignore b/0x0008_uninitialized-variables/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0008_uninitialized-variables/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0008_uninitialized-variables/.vscode/c_cpp_properties.json b/0x0008_uninitialized-variables/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0008_uninitialized-variables/.vscode/cmake-kits.json b/0x0008_uninitialized-variables/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0008_uninitialized-variables/.vscode/extensions.json b/0x0008_uninitialized-variables/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0008_uninitialized-variables/.vscode/launch.json b/0x0008_uninitialized-variables/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0008_uninitialized-variables/.vscode/settings.json b/0x0008_uninitialized-variables/.vscode/settings.json new file mode 100644 index 0000000..4e5d7b6 --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/settings.json @@ -0,0 +1,45 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "addressmap.h": "c", + "resets.h": "c", + "pads_bank0.h": "c" + } +} diff --git a/0x0008_uninitialized-variables/.vscode/tasks.json b/0x0008_uninitialized-variables/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x0008_uninitialized-variables/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0008_uninitialized-variables/0x0008_uninitialized-variables.c b/0x0008_uninitialized-variables/0x0008_uninitialized-variables.c new file mode 100644 index 0000000..8e9dc42 --- /dev/null +++ b/0x0008_uninitialized-variables/0x0008_uninitialized-variables.c @@ -0,0 +1,68 @@ +/** + * @file 0x0008_uninitialized-variables.c + * @brief Uninitialized variables: demonstrate undefined behavior with printf + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the danger of uninitialized variables. Prints the value of an + * uninitialized uint8_t over UART while blinking an LED on GPIO16. + * + * Wiring: + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the LED */ +#define LED_PIN 16 + +/** + * @brief Toggle LED and print uninitialized variable value + * + * @details Blinks the LED on and off with a 500ms delay between each + * transition and prints the age variable each cycle. + * + * @param age value to print (uninitialized in this demo) + */ +static void blink_and_print(uint8_t age) { + printf("age: %d\r\n", age); + gpio_put(LED_PIN, 1); + sleep_ms(500); + gpio_put(LED_PIN, 0); + sleep_ms(500); +} + +int main(void) { + uint8_t age; + stdio_init_all(); + gpio_init(LED_PIN); + gpio_set_dir(LED_PIN, GPIO_OUT); + while (true) { + blink_and_print(age); + } +} diff --git a/0x0008_uninitialized-variables/CMakeLists.txt b/0x0008_uninitialized-variables/CMakeLists.txt new file mode 100644 index 0000000..c2ac591 --- /dev/null +++ b/0x0008_uninitialized-variables/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0008_uninitialized-variables C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0008_uninitialized-variables 0x0008_uninitialized-variables.c ) + +pico_set_program_name(0x0008_uninitialized-variables "0x0008_uninitialized-variables") +pico_set_program_version(0x0008_uninitialized-variables "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0008_uninitialized-variables 1) +pico_enable_stdio_usb(0x0008_uninitialized-variables 0) + +# Add the standard library to the build +target_link_libraries(0x0008_uninitialized-variables + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0008_uninitialized-variables PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0008_uninitialized-variables) + diff --git a/0x0008_uninitialized-variables/pico_sdk_import.cmake b/0x0008_uninitialized-variables/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0008_uninitialized-variables/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x000b_integer-data-type/.gitignore b/0x000b_integer-data-type/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x000b_integer-data-type/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x000b_integer-data-type/.vscode/c_cpp_properties.json b/0x000b_integer-data-type/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x000b_integer-data-type/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x000b_integer-data-type/.vscode/cmake-kits.json b/0x000b_integer-data-type/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x000b_integer-data-type/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x000b_integer-data-type/.vscode/extensions.json b/0x000b_integer-data-type/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x000b_integer-data-type/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x000b_integer-data-type/.vscode/launch.json b/0x000b_integer-data-type/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x000b_integer-data-type/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x000b_integer-data-type/.vscode/settings.json b/0x000b_integer-data-type/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x000b_integer-data-type/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x000b_integer-data-type/.vscode/tasks.json b/0x000b_integer-data-type/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x000b_integer-data-type/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x000b_integer-data-type/0x000b_integer-data-type.c b/0x000b_integer-data-type/0x000b_integer-data-type.c new file mode 100644 index 0000000..c923449 --- /dev/null +++ b/0x000b_integer-data-type/0x000b_integer-data-type.c @@ -0,0 +1,132 @@ +/** + * @file 0x000b_integer-data-type.c + * @brief Integer data types: cycle LEDs on GPIO16-18 with asm, print int values + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates uint8_t and int8_t integer data types. Initializes GPIO16-18 + * via inline assembly (PADS_BANK0, IO_BANK0, coprocessor OE), then cycles + * LEDs using coprocessor mcrr instructions while printing integer values. + * + * Wiring: + * GPIO16 -> LED1 anode (with current-limiting resistor to GND) + * GPIO17 -> LED2 anode (with current-limiting resistor to GND) + * GPIO18 -> LED3 anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** + * @brief Initialize GPIO16-18 as SIO outputs via inline assembly loop + * + * @details Configures PADS_BANK0 (clear OD+ISO, set IE), IO_BANK0 + * (FUNCSEL=5 SIO), and coprocessor OE for pins 16-18. + * + * @retval None + */ +static void asm_init_gpio_range(void) { + __asm volatile ( + "ldr r3, =0x40038000\n" // address of PADS_BANK0_BASE + "ldr r2, =0x40028004\n" // address of IO_BANK0 GPIO0.ctrl + "movs r0, #16\n" // GPIO16 (start pin) + "init_loop:\n" // loop start + "lsls r1, r0, #2\n" // pin * 4 (pad offset) + "adds r4, r3, r1\n" // PADS base + offset + "ldr r5, [r4]\n" // load current config + "bic r5, r5, #0x180\n" // clear OD+ISO + "orr r5, r5, #0x40\n" // set IE + "str r5, [r4]\n" // store updated config + "lsls r1, r0, #3\n" // pin * 8 (ctrl offset) + "adds r4, r2, r1\n" // IO_BANK0 base + offset + "ldr r5, [r4]\n" // load current config + "bic r5, r5, #0x1f\n" // clear FUNCSEL bits [4:0] + "orr r5, r5, #5\n" // set FUNCSEL = 5 (SIO) + "str r5, [r4]\n" // store updated config + "mov r4, r0\n" // pin + "movs r5, #1\n" // bit 1; used for OUT/OE writes + "mcrr p0, #4, r4, r5, c4\n" // gpioc_bit_oe_put(pin,1) + "adds r0, r0, #1\n" // increment pin + "cmp r0, #20\n" // stop after pin 18 + "blt init_loop\n" // loop until r0 == 20 + ); +} + +/** + * @brief Blink a single pin using coprocessor mcrr instructions + * + * @details Turns the specified pin on for 500ms, then off for 500ms + * using inline asm mcrr coprocessor output instructions. + * + * @param pin GPIO pin number to blink + * @retval None + */ +static void asm_blink_pin(uint8_t pin) { + __asm volatile ( + "mov r4, %0\n" + "movs r5, #0x01\n" + "mcrr p0, #4, r4, r5, c0\n" + : : "r"(pin) : "r4", "r5" + ); + sleep_ms(500); + __asm volatile ( + "mov r4, %0\n" + "movs r5, #0\n" + "mcrr p0, #4, r4, r5, c0\n" + : : "r"(pin) : "r4", "r5" + ); + sleep_ms(500); +} + +/** + * @brief Blink LED, advance pin, and print age/range + * + * @details Blinks the current pin, wraps pin 16-18, + * and prints both integer variables. + * + * @param pin pointer to the current GPIO pin number + * @param age unsigned 8-bit age value + * @param range signed 8-bit range value + * @retval None + */ +static void blink_and_print(uint8_t *pin, uint8_t age, int8_t range) { + asm_blink_pin(*pin); + *pin = (*pin > 18) ? 16 : *pin + 1; + printf("age: %d\r\n", age); + printf("range: %d\r\n", range); +} + +int main(void) { + uint8_t age = 43; + int8_t range = -42; + stdio_init_all(); + asm_init_gpio_range(); + uint8_t pin = 16; + while (1) { + blink_and_print(&pin, age, range); + } +} diff --git a/0x000b_integer-data-type/CMakeLists.txt b/0x000b_integer-data-type/CMakeLists.txt new file mode 100644 index 0000000..6178a2a --- /dev/null +++ b/0x000b_integer-data-type/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x000b_integer-data-type C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x000b_integer-data-type 0x000b_integer-data-type.c ) + +pico_set_program_name(0x000b_integer-data-type "0x000b_integer-data-type") +pico_set_program_version(0x000b_integer-data-type "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x000b_integer-data-type 1) +pico_enable_stdio_usb(0x000b_integer-data-type 0) + +# Add the standard library to the build +target_link_libraries(0x000b_integer-data-type + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x000b_integer-data-type PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x000b_integer-data-type) + diff --git a/0x000b_integer-data-type/pico_sdk_import.cmake b/0x000b_integer-data-type/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x000b_integer-data-type/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x000e_floating-point-data-type/.gitignore b/0x000e_floating-point-data-type/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x000e_floating-point-data-type/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x000e_floating-point-data-type/.vscode/c_cpp_properties.json b/0x000e_floating-point-data-type/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x000e_floating-point-data-type/.vscode/cmake-kits.json b/0x000e_floating-point-data-type/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x000e_floating-point-data-type/.vscode/extensions.json b/0x000e_floating-point-data-type/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x000e_floating-point-data-type/.vscode/launch.json b/0x000e_floating-point-data-type/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x000e_floating-point-data-type/.vscode/settings.json b/0x000e_floating-point-data-type/.vscode/settings.json new file mode 100644 index 0000000..cdb8e61 --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x000e_floating-point-data-type/.vscode/tasks.json b/0x000e_floating-point-data-type/.vscode/tasks.json new file mode 100644 index 0000000..f427bf0 --- /dev/null +++ b/0x000e_floating-point-data-type/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x000e_floating-point-data-type/0x000e_floating-point-data-type.c b/0x000e_floating-point-data-type/0x000e_floating-point-data-type.c new file mode 100644 index 0000000..f0a3bac --- /dev/null +++ b/0x000e_floating-point-data-type/0x000e_floating-point-data-type.c @@ -0,0 +1,47 @@ +/** + * @file 0x000e_floating-point-data-type.c + * @brief Floating-point data type: print a float value over UART + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the float data type on the Raspberry Pi Pico 2. Initializes + * a float variable and prints its value over UART in an infinite loop. + * + * Wiring: + * No external wiring required (USB serial). + */ + +#include +#include "pico/stdlib.h" + +int main(void) { + float fav_num = 42.5; + stdio_init_all(); + while (true) { + printf("fav_num: %f\r\n", fav_num); + } +} diff --git a/0x000e_floating-point-data-type/CMakeLists.txt b/0x000e_floating-point-data-type/CMakeLists.txt new file mode 100644 index 0000000..72041a0 --- /dev/null +++ b/0x000e_floating-point-data-type/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x000e_floating-point-data-type C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x000e_floating-point-data-type 0x000e_floating-point-data-type.c ) + +pico_set_program_name(0x000e_floating-point-data-type "0x000e_floating-point-data-type") +pico_set_program_version(0x000e_floating-point-data-type "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x000e_floating-point-data-type 1) +pico_enable_stdio_usb(0x000e_floating-point-data-type 0) + +# Add the standard library to the build +target_link_libraries(0x000e_floating-point-data-type + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x000e_floating-point-data-type PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x000e_floating-point-data-type) + diff --git a/0x000e_floating-point-data-type/pico_sdk_import.cmake b/0x000e_floating-point-data-type/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x000e_floating-point-data-type/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0011_double-floating-point-data-type/.gitignore b/0x0011_double-floating-point-data-type/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0011_double-floating-point-data-type/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0011_double-floating-point-data-type/.vscode/c_cpp_properties.json b/0x0011_double-floating-point-data-type/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0011_double-floating-point-data-type/.vscode/cmake-kits.json b/0x0011_double-floating-point-data-type/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0011_double-floating-point-data-type/.vscode/extensions.json b/0x0011_double-floating-point-data-type/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0011_double-floating-point-data-type/.vscode/launch.json b/0x0011_double-floating-point-data-type/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0011_double-floating-point-data-type/.vscode/settings.json b/0x0011_double-floating-point-data-type/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0011_double-floating-point-data-type/.vscode/tasks.json b/0x0011_double-floating-point-data-type/.vscode/tasks.json new file mode 100644 index 0000000..2359f7f --- /dev/null +++ b/0x0011_double-floating-point-data-type/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0011_double-floating-point-data-type/0x0011_double-floating-point-data-type.c b/0x0011_double-floating-point-data-type/0x0011_double-floating-point-data-type.c new file mode 100644 index 0000000..753f51d --- /dev/null +++ b/0x0011_double-floating-point-data-type/0x0011_double-floating-point-data-type.c @@ -0,0 +1,47 @@ +/** + * @file 0x0011_double-floating-point-data-type.c + * @brief Double floating-point data type: print a double value over UART + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the double data type on the Raspberry Pi Pico 2. Initializes + * a double variable and prints its value over UART in an infinite loop. + * + * Wiring: + * No external wiring required (USB serial). + */ + +#include +#include "pico/stdlib.h" + +int main(void) { + double fav_num = 42.52525; + stdio_init_all(); + while (true) { + printf("fav_num: %lf\r\n", fav_num); + } +} diff --git a/0x0011_double-floating-point-data-type/CMakeLists.txt b/0x0011_double-floating-point-data-type/CMakeLists.txt new file mode 100644 index 0000000..035b92e --- /dev/null +++ b/0x0011_double-floating-point-data-type/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0011_double-floating-point-data-type C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0011_double-floating-point-data-type 0x0011_double-floating-point-data-type.c ) + +pico_set_program_name(0x0011_double-floating-point-data-type "0x0011_double-floating-point-data-type") +pico_set_program_version(0x0011_double-floating-point-data-type "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0011_double-floating-point-data-type 1) +pico_enable_stdio_usb(0x0011_double-floating-point-data-type 0) + +# Add the standard library to the build +target_link_libraries(0x0011_double-floating-point-data-type + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0011_double-floating-point-data-type PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0011_double-floating-point-data-type) + diff --git a/0x0011_double-floating-point-data-type/pico_sdk_import.cmake b/0x0011_double-floating-point-data-type/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0011_double-floating-point-data-type/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0014_static-variables/.gitignore b/0x0014_static-variables/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0014_static-variables/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0014_static-variables/.vscode/c_cpp_properties.json b/0x0014_static-variables/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0014_static-variables/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0014_static-variables/.vscode/cmake-kits.json b/0x0014_static-variables/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0014_static-variables/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0014_static-variables/.vscode/extensions.json b/0x0014_static-variables/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0014_static-variables/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0014_static-variables/.vscode/launch.json b/0x0014_static-variables/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0014_static-variables/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0014_static-variables/.vscode/settings.json b/0x0014_static-variables/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x0014_static-variables/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0014_static-variables/.vscode/tasks.json b/0x0014_static-variables/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x0014_static-variables/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0014_static-variables/0x0014_static-variables.c b/0x0014_static-variables/0x0014_static-variables.c new file mode 100644 index 0000000..ff5fd32 --- /dev/null +++ b/0x0014_static-variables/0x0014_static-variables.c @@ -0,0 +1,90 @@ +/** + * @file 0x0014_static-variables.c + * @brief Static variables: compare regular vs static local variable persistence + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the difference between regular and static local variables. + * A regular variable resets to 42 each iteration while a static variable + * persists and increments across loop iterations. Also reads a button on + * GPIO15 and mirrors its state to an LED on GPIO16. + * + * Wiring: + * GPIO15 -> Button (with pull-up, active low) + * GPIO16 -> LED anode (with current-limiting resistor to GND) + */ + +#include +#include "pico/stdlib.h" + +/** @brief GPIO pin number for the button input */ +#define BUTTON_GPIO 15 +/** @brief GPIO pin number for the LED output */ +#define LED_GPIO 16 + +/** + * @brief Initialize button and LED GPIO pins + * + * @details Configures the button pin as input with pull-up and the + * LED pin as output. + * + * @retval None + */ +static void init_gpio(void) { + gpio_init(BUTTON_GPIO); + gpio_set_dir(BUTTON_GPIO, GPIO_IN); + gpio_pull_up(BUTTON_GPIO); + gpio_init(LED_GPIO); + gpio_set_dir(LED_GPIO, GPIO_OUT); +} + +/** + * @brief Print and increment regular vs static variables, update LED + * + * @details Prints both variable values, increments them, reads the + * button state and drives the LED accordingly. + * + * @retval None + */ +static void demo_static_variable(void) { + uint8_t regular_fav_num = 42; + static uint8_t static_fav_num = 42; + printf("regular_fav_num: %d\r\n", regular_fav_num); + printf("static_fav_num: %d\r\n", static_fav_num); + regular_fav_num++; + static_fav_num++; + bool pressed = gpio_get(BUTTON_GPIO); + gpio_put(LED_GPIO, pressed ? 0 : 1); +} + +int main(void) { + stdio_init_all(); + init_gpio(); + while (true) { + demo_static_variable(); + } +} diff --git a/0x0014_static-variables/CMakeLists.txt b/0x0014_static-variables/CMakeLists.txt new file mode 100644 index 0000000..63d1f6b --- /dev/null +++ b/0x0014_static-variables/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0014_static-variables C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0014_static-variables 0x0014_static-variables.c ) + +pico_set_program_name(0x0014_static-variables "0x0014_static-variables") +pico_set_program_version(0x0014_static-variables "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0014_static-variables 1) +pico_enable_stdio_usb(0x0014_static-variables 0) + +# Add the standard library to the build +target_link_libraries(0x0014_static-variables + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0014_static-variables PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0014_static-variables) + diff --git a/0x0014_static-variables/pico_sdk_import.cmake b/0x0014_static-variables/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0014_static-variables/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0017_constants/.gitignore b/0x0017_constants/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0017_constants/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0017_constants/.vscode/c_cpp_properties.json b/0x0017_constants/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0017_constants/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0017_constants/.vscode/cmake-kits.json b/0x0017_constants/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0017_constants/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0017_constants/.vscode/extensions.json b/0x0017_constants/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0017_constants/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0017_constants/.vscode/launch.json b/0x0017_constants/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0017_constants/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0017_constants/.vscode/settings.json b/0x0017_constants/.vscode/settings.json new file mode 100644 index 0000000..8aecbe6 --- /dev/null +++ b/0x0017_constants/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/0x0017_constants/.vscode/tasks.json b/0x0017_constants/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x0017_constants/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0017_constants/0x0017_constants.c b/0x0017_constants/0x0017_constants.c new file mode 100644 index 0000000..f3b5182 --- /dev/null +++ b/0x0017_constants/0x0017_constants.c @@ -0,0 +1,100 @@ +/** + * @file 0x0017_constants.c + * @brief Constants: demonstrate #define and const with LCD1602 I2C display + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates #define macro constants and const-qualified variables. + * Initializes an LCD1602 display over I2C and prints constant values + * over UART in an infinite loop. + * + * Wiring: + * GPIO2 (SDA) -> LCD1602 PCF8574 SDA + * GPIO3 (SCL) -> LCD1602 PCF8574 SCL + * 3V3 -> LCD1602 VCC + * GND -> LCD1602 GND + */ + +#include +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "lcd_1602.h" + +/** @brief Macro constant for favorite number */ +#define FAV_NUM 42 +/** @brief I2C peripheral instance */ +#define I2C_PORT i2c1 +/** @brief GPIO pin for I2C SDA */ +#define I2C_SDA_PIN 2 +/** @brief GPIO pin for I2C SCL */ +#define I2C_SCL_PIN 3 + +/** @brief Const-qualified favorite number */ +const int OTHER_FAV_NUM = 1337; + +/** + * @brief Initialize I2C bus and LCD1602 display + * + * @details Configures I2C1 at 100kHz on GPIO2/3, initializes the + * LCD via PCF8574 at address 0x27, and writes two lines. + * + * @retval None + */ +static void init_i2c_and_lcd(void) { + i2c_init(I2C_PORT, 100000); + gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(I2C_SDA_PIN); + gpio_pull_up(I2C_SCL_PIN); + lcd_i2c_init(I2C_PORT, 0x27, 4, 0x08); +} + +/** + * @brief Write greeting text to the LCD display + * + * @details Sets cursor to line 0 and writes "Reverse", then sets + * cursor to line 1 and writes "Engineering". + * + * @retval None + */ +static void write_lcd_greeting(void) { + lcd_set_cursor(0, 0); + lcd_puts("Reverse"); + lcd_set_cursor(1, 0); + lcd_puts("Engineering"); +} + +int main(void) { + stdio_init_all(); + init_i2c_and_lcd(); + write_lcd_greeting(); + while (true) { + printf("FAV_NUM: %d\r\n", FAV_NUM); + printf("OTHER_FAV_NUM: %d\r\n", OTHER_FAV_NUM); + } +} diff --git a/0x0017_constants/CMakeLists.txt b/0x0017_constants/CMakeLists.txt new file mode 100644 index 0000000..4c75aa3 --- /dev/null +++ b/0x0017_constants/CMakeLists.txt @@ -0,0 +1,59 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0017_constants C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0017_constants 0x0017_constants.c lcd_1602.c) + +pico_set_program_name(0x0017_constants "0x0017_constants") +pico_set_program_version(0x0017_constants "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0017_constants 1) +pico_enable_stdio_usb(0x0017_constants 0) + +# Add the standard library to the build +target_link_libraries(0x0017_constants + pico_stdlib + hardware_i2c + hardware_gpio) + +# Add the standard include files to the build +target_include_directories(0x0017_constants PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0017_constants) + diff --git a/0x0017_constants/lcd_1602.c b/0x0017_constants/lcd_1602.c new file mode 100644 index 0000000..80054cc --- /dev/null +++ b/0x0017_constants/lcd_1602.c @@ -0,0 +1,160 @@ +/** + * @file lcd_1602.c + * @brief Implementation of PCF8574-backed HD44780 (16x2) LCD driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lcd_1602.h" +#include +#include + +/** @brief I2C instance pointer for the LCD */ +static i2c_inst_t *lcd_i2c = NULL; +/** @brief I2C address of the PCF8574 backpack */ +static uint8_t lcd_addr = 0x27; +/** @brief Bit shift for 4-bit nibble position */ +static int lcd_nibble_shift = 4; +/** @brief PCF8574 bit mask controlling the backlight */ +static uint8_t lcd_backlight_mask = 0x08; + +/** @brief PCF8574 bit mask for Register Select */ +#define PIN_RS 0x01 +/** @brief PCF8574 bit mask for Read/Write */ +#define PIN_RW 0x02 +/** @brief PCF8574 bit mask for Enable */ +#define PIN_EN 0x04 + +/** + * @brief Write one raw byte to the PCF8574 expander over I2C + * + * @param data Output byte to send to the expander + */ +static void pcf_write_byte(uint8_t data) { + if (!lcd_i2c) return; + i2c_write_blocking(lcd_i2c, lcd_addr, &data, 1, false); +} + +/** + * @brief Toggle EN to latch a nibble into the LCD controller + * + * @param data Current control/data bus byte (with RS and backlight already set) + */ +static void pcf_pulse_enable(uint8_t data) { + pcf_write_byte(data | PIN_EN); + sleep_us(1); + pcf_write_byte(data & ~PIN_EN); + sleep_us(50); +} + +/** + * @brief Write one 4-bit nibble to the LCD + * + * @param nibble Lower 4 bits to write + * @param mode 0 for command, non-zero for character data + */ +static void lcd_write4(uint8_t nibble, uint8_t mode) { + uint8_t data = (nibble & 0x0F) << lcd_nibble_shift; + data |= mode ? PIN_RS : 0; + data |= lcd_backlight_mask; + pcf_pulse_enable(data); +} + +/** + * @brief Send one full 8-bit command/data value as two nibbles + * + * @param value Byte to send to the LCD + * @param mode 0 for command, non-zero for character data + */ +static void lcd_send(uint8_t value, uint8_t mode) { + lcd_write4((value >> 4) & 0x0F, mode); + lcd_write4(value & 0x0F, mode); +} + +/** + * @brief Store LCD driver configuration in module-level state + * + * @param i2c Pointer to the I2C instance + * @param pcf_addr 7-bit PCF8574 address + * @param nibble_shift Bit shift for 4-bit nibbles + * @param backlight_mask Backlight control bit mask + */ +static void lcd_store_config(i2c_inst_t *i2c, uint8_t pcf_addr, + int nibble_shift, uint8_t backlight_mask) { + lcd_i2c = i2c; + lcd_addr = pcf_addr; + lcd_nibble_shift = nibble_shift; + lcd_backlight_mask = backlight_mask; +} + +/** + * @brief Execute the HD44780 4-bit mode power-on reset sequence + */ +static void lcd_hd44780_reset(void) { + lcd_write4(0x03, 0); + sleep_ms(5); + lcd_write4(0x03, 0); + sleep_us(150); + lcd_write4(0x03, 0); + sleep_us(150); + lcd_write4(0x02, 0); + sleep_us(150); +} + +/** + * @brief Send post-reset configuration commands to the HD44780 + * + * Sets 4-bit mode with 2 display lines, turns the display on with + * cursor hidden, clears the screen, and selects left-to-right entry mode. + */ +static void lcd_hd44780_configure(void) { + lcd_send(0x28, 0); + lcd_send(0x0C, 0); + lcd_send(0x01, 0); + sleep_ms(2); + lcd_send(0x06, 0); +} + +void lcd_i2c_init(i2c_inst_t *i2c, uint8_t pcf_addr, int nibble_shift, uint8_t backlight_mask) { + lcd_store_config(i2c, pcf_addr, nibble_shift, backlight_mask); + lcd_hd44780_reset(); + lcd_hd44780_configure(); +} + +void lcd_clear(void) { + lcd_send(0x01, 0); + sleep_ms(2); +} + +void lcd_set_cursor(int line, int position) { + const uint8_t row_offsets[] = {0x00, 0x40}; + if (line > 1) line = 1; + lcd_send(0x80 | (position + row_offsets[line]), 0); +} + +void lcd_puts(const char *s) { + while (*s) + lcd_send((uint8_t)*s++, 1); +} diff --git a/0x0017_constants/lcd_1602.h b/0x0017_constants/lcd_1602.h new file mode 100644 index 0000000..072ae13 --- /dev/null +++ b/0x0017_constants/lcd_1602.h @@ -0,0 +1,74 @@ +/** + * @file lcd_1602.h + * @brief Header for PCF8574-backed HD44780 (16x2) LCD driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LCD_1602_H +#define LCD_1602_H + +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" + +/** + * @brief Initialize the LCD driver over I2C + * + * Configures the internal driver state and performs the HD44780 initialization + * sequence. The driver does not configure I2C pins or call i2c_init; that + * must be done by the caller prior to calling this function. + * + * @param i2c Pointer to the I2C instance (e.g. i2c0 or i2c1) + * @param pcf_addr PCF8574 I2C address (commonly 0x27 or 0x3F) + * @param nibble_shift Bit shift applied to 4-bit nibbles (commonly 4 or 0) + * @param backlight_mask PCF8574 bit mask that controls the backlight + */ +void lcd_i2c_init(i2c_inst_t *i2c, uint8_t pcf_addr, int nibble_shift, uint8_t backlight_mask); + +/** + * @brief Clear the LCD display + * + * Clears the display and returns the cursor to the home position. This + * call blocks for the duration required by the HD44780 controller. + */ +void lcd_clear(void); + +/** + * @brief Set the cursor position + * + * @param line Line number (0 or 1) + * @param position Column (0..15) + */ +void lcd_set_cursor(int line, int position); + +/** + * @brief Write a null-terminated string to the display + * + * @param s The string to write (ASCII) + */ +void lcd_puts(const char *s); + +#endif // LCD_1602_H diff --git a/0x0017_constants/pico_sdk_import.cmake b/0x0017_constants/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0017_constants/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x001a_operators/.gitignore b/0x001a_operators/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x001a_operators/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x001a_operators/.vscode/c_cpp_properties.json b/0x001a_operators/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x001a_operators/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x001a_operators/.vscode/cmake-kits.json b/0x001a_operators/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x001a_operators/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x001a_operators/.vscode/extensions.json b/0x001a_operators/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x001a_operators/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x001a_operators/.vscode/launch.json b/0x001a_operators/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x001a_operators/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x001a_operators/.vscode/settings.json b/0x001a_operators/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x001a_operators/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x001a_operators/.vscode/tasks.json b/0x001a_operators/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x001a_operators/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x001a_operators/0x001a_operators.c b/0x001a_operators/0x001a_operators.c new file mode 100644 index 0000000..1eecc97 --- /dev/null +++ b/0x001a_operators/0x001a_operators.c @@ -0,0 +1,126 @@ +/** + * @file 0x001a_operators.c + * @brief Operators: demonstrate C operators with DHT11 temperature sensor + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates arithmetic, increment, relational, logical, bitwise, and + * assignment operators in C. Also reads humidity and temperature from a + * DHT11 sensor on GPIO4. + * + * Wiring: + * GPIO4 -> DHT11 data pin (with 10k pull-up to 3V3) + * 3V3 -> DHT11 VCC + * GND -> DHT11 GND + */ + +#include +#include "pico/stdlib.h" +#include "dht11.h" + +/** + * @brief Print pre-computed operator results + * + * @details Prints all six operator demonstration values over UART. + * + * @param arith arithmetic operator result + * @param inc increment operator result + * @param rel relational operator result + * @param logic logical operator result + * @param bitw bitwise operator result + * @param assign assignment operator result + * @retval None + */ +static void print_operator_results(int arith, int inc, bool rel, + bool logic, int bitw, int assign) { + printf("arithmetic_operator: %d\r\n", arith); + printf("increment_operator: %d\r\n", inc); + printf("relational_operator: %d\r\n", rel); + printf("logical_operator: %d\r\n", logic); + printf("bitwise_operator: %d\r\n", bitw); + printf("assignment_operator: %d\r\n", assign); +} + +/** + * @brief Compute arithmetic and increment operator results + * + * @param arith pointer to store multiplication result + * @param incr pointer to store post-increment result + * @param x left operand + * @param y right operand + * @retval None + */ +static void compute_arithmetic_ops(int *arith, int *incr, int x, int y) { + *arith = (x * y); + *incr = x++; +} + +/** + * @brief Compute all operator values and print results + * + * @details Performs arithmetic, relational, logical, bitwise, + * and assignment operations, then prints each result. + * + * @retval None + */ +static void compute_operators(void) { + int x = 5, y = 10; + int arith, incr; + compute_arithmetic_ops(&arith, &incr, x, y); + bool rel = (x > y); + bool logic = (x > y) && (y > x); + int bits = (x << 1); + int assign = (x += 5); + print_operator_results(arith, incr, rel, logic, bits, assign); +} + +/** + * @brief Read and print DHT11 humidity and temperature + * + * @details Attempts a DHT11 read; on success prints humidity and + * temperature, on failure prints an error message. + * + * @retval None + */ +static void print_dht11_reading(void) { + float hum, temp; + if (dht11_read(&hum, &temp)) { + printf("Humidity: %.1f%%, Temperature: %.1f°C\r\n", hum, temp); + } else { + printf("DHT11 read failed\r\n"); + } +} + +int main(void) { + stdio_init_all(); + dht11_init(4); + while (true) { + compute_operators(); + print_dht11_reading(); + sleep_ms(2000); + } +} \ No newline at end of file diff --git a/0x001a_operators/CMakeLists.txt b/0x001a_operators/CMakeLists.txt new file mode 100644 index 0000000..f6ef427 --- /dev/null +++ b/0x001a_operators/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x001a_operators C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x001a_operators 0x001a_operators.c dht11.c ) + +pico_set_program_name(0x001a_operators "0x001a_operators") +pico_set_program_version(0x001a_operators "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x001a_operators 1) +pico_enable_stdio_usb(0x001a_operators 0) + +# Add the standard library to the build +target_link_libraries(0x001a_operators + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x001a_operators PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x001a_operators) + diff --git a/0x001a_operators/dht11.c b/0x001a_operators/dht11.c new file mode 100644 index 0000000..28f147f --- /dev/null +++ b/0x001a_operators/dht11.c @@ -0,0 +1,140 @@ +/** + * @file dht11.c + * @brief Implementation of DHT11 temperature and humidity sensor driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "dht11.h" +#include "hardware/gpio.h" +#include "pico/time.h" + +/** @brief GPIO pin connected to the DHT11 sensor */ +static uint dht_pin; + +/** + * @brief Send the DHT11 start signal on the data pin + * + * Drives the pin LOW for 18 ms then HIGH for 40 us before switching + * the pin to input mode to listen for the sensor response. + */ +static void send_start_signal(void) { + gpio_set_dir(dht_pin, GPIO_OUT); + gpio_put(dht_pin, 0); + sleep_ms(18); + gpio_put(dht_pin, 1); + sleep_us(40); + gpio_set_dir(dht_pin, GPIO_IN); +} + +/** + * @brief Wait for the pin to leave a given logic level + * + * Spins until the pin no longer reads the specified level, returning + * false if a timeout of 10 000 iterations is exceeded. + * + * @param level Logic level to wait through (0 or 1) + * @return bool true once the level changed, false on timeout + */ +static bool wait_for_level(int level) { + uint32_t timeout = 10000; + while (gpio_get(dht_pin) == level) + if (--timeout == 0) return false; + return true; +} + +/** + * @brief Wait for the DHT11 response after the start signal + * + * The sensor pulls LOW then HIGH then LOW again; each transition + * is awaited with a timeout. + * + * @return bool true if the full response was received, false on timeout + */ +static bool wait_response(void) { + if (!wait_for_level(1)) return false; + if (!wait_for_level(0)) return false; + if (!wait_for_level(1)) return false; + return true; +} + +/** + * @brief Read a single bit from the DHT11 data stream + * + * Waits for the low-period to end, measures the high-period duration, + * and shifts the result into the appropriate byte of the data array. + * + * @param data 5-byte array accumulating the received bits + * @param i Bit index (0-39) + * @return bool true on success, false on timeout + */ +static bool read_bit(uint8_t *data, int i) { + if (!wait_for_level(0)) return false; + uint32_t start = time_us_32(); + if (!wait_for_level(1)) return false; + uint32_t duration = time_us_32() - start; + data[i / 8] <<= 1; + if (duration > 40) data[i / 8] |= 1; + return true; +} + +/** + * @brief Read all 40 data bits from the DHT11 + * + * @param data 5-byte array filled with the received data + * @return bool true if all 40 bits were read, false on timeout + */ +static bool read_40_bits(uint8_t *data) { + for (int i = 0; i < 40; i++) + if (!read_bit(data, i)) return false; + return true; +} + +/** + * @brief Verify the DHT11 checksum byte + * + * @param data 5-byte received data (bytes 0-3 plus checksum in byte 4) + * @return bool true if the checksum matches, false otherwise + */ +static bool validate_checksum(const uint8_t *data) { + return data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF); +} + +void dht11_init(uint8_t pin) { + dht_pin = pin; + gpio_init(pin); + gpio_pull_up(pin); +} + +bool dht11_read(float *humidity, float *temperature) { + uint8_t data[5] = {0}; + send_start_signal(); + if (!wait_response()) return false; + if (!read_40_bits(data)) return false; + if (!validate_checksum(data)) return false; + *humidity = data[0] + data[1] * 0.1f; + *temperature = data[2] + data[3] * 0.1f; + return true; +} diff --git a/0x001a_operators/dht11.h b/0x001a_operators/dht11.h new file mode 100644 index 0000000..30e298c --- /dev/null +++ b/0x001a_operators/dht11.h @@ -0,0 +1,57 @@ +/** + * @file dht11.h + * @brief Header for DHT11 temperature and humidity sensor driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef DHT11_H +#define DHT11_H + +#include +#include + +/** + * @brief Initialize the DHT11 driver + * + * Configures the GPIO pin for the DHT11 sensor. This must be called before + * using dht11_read(). + * + * @param pin GPIO pin number connected to DHT11 signal + */ +void dht11_init(uint8_t pin); + +/** + * @brief Read temperature and humidity from DHT11 sensor + * + * Performs the DHT11 communication protocol to read sensor data. + * + * @param humidity Pointer to store humidity value (0-100%) + * @param temperature Pointer to store temperature value in Celsius + * @return true if read successful, false on error or timeout + */ +bool dht11_read(float *humidity, float *temperature); + +#endif // DHT11_H diff --git a/0x001a_operators/hack-temp.py b/0x001a_operators/hack-temp.py new file mode 100644 index 0000000..8973073 --- /dev/null +++ b/0x001a_operators/hack-temp.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 + +""" +FILE: hack-temp.py + +DESCRIPTION: +Incremental firmware patcher with minimal, conservative steps to bias readings. + +BRIEF: +Provides three modes: +- pool_only: change only the literal pool constant (scaling effect; least invasive) +- temp_only_vadd: convert temperature path to vadd.f32 and set a small negative pool +- both_vadd: convert both paths to vadd.f32 and set a small negative pool + +Intended to let you test changes incrementally, keeping humidity stable while +you evaluate temperature bias first. + +AUTHOR: Kevin Thomas +CREATION DATE: November 1, 2025 +UPDATE DATE: November 1, 2025 +""" + +import sys +import os +import struct + +BIN_PATH = "build/0x001a_operators.bin" + +# Offsets +OFF_410 = 0x410 +OFF_414 = 0x414 +OFF_POOL = 0x42C + +# Original encodings (for visibility only) +ORIG_410 = bytes.fromhex("a6ee257a") # vfma.f32 s14, s12, s11 +ORIG_414 = bytes.fromhex("e6eea57a") # vfma.f32 s15, s13, s11 +ORIG_POOL = bytes.fromhex("cccccc3d") # 0.1f + +# Encodings for vadd.f32 s14, s14, s11 and vadd.f32 s15, s15, s11 +VADD_S14_S11 = struct.pack(" 1 else "pool_only").strip().lower() + if mode == "pool_only": + patch_pool_only(new_pool_float=-1.0) + elif mode == "temp_only_vadd": + patch_temp_only_vadd(new_pool_float=-2.0) + elif mode == "both_vadd": + patch_both_vadd(new_pool_float=-2.0) + else: + print(f"Unknown mode: {mode}") + print("Use: pool_only | temp_only_vadd | both_vadd") + sys.exit(2) + + +if __name__ == "__main__": + main() + print("Patch complete. Convert to UF2 and flash. Test each mode incrementally.") diff --git a/0x001a_operators/pico_sdk_import.cmake b/0x001a_operators/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x001a_operators/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x001d_static-conditionals/.gitignore b/0x001d_static-conditionals/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x001d_static-conditionals/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x001d_static-conditionals/.vscode/c_cpp_properties.json b/0x001d_static-conditionals/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x001d_static-conditionals/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x001d_static-conditionals/.vscode/cmake-kits.json b/0x001d_static-conditionals/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x001d_static-conditionals/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x001d_static-conditionals/.vscode/extensions.json b/0x001d_static-conditionals/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x001d_static-conditionals/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x001d_static-conditionals/.vscode/launch.json b/0x001d_static-conditionals/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x001d_static-conditionals/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x001d_static-conditionals/.vscode/settings.json b/0x001d_static-conditionals/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x001d_static-conditionals/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x001d_static-conditionals/.vscode/tasks.json b/0x001d_static-conditionals/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x001d_static-conditionals/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x001d_static-conditionals/0x001d_static-conditionals.c b/0x001d_static-conditionals/0x001d_static-conditionals.c new file mode 100644 index 0000000..6db9a5e --- /dev/null +++ b/0x001d_static-conditionals/0x001d_static-conditionals.c @@ -0,0 +1,106 @@ +/** + * @file 0x001d_static-conditionals.c + * @brief Static conditionals: if/else and switch with servo sweep + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates static (compile-time known) conditionals using if/else if/else + * and switch/case statements. Also sweeps a servo on GPIO6 between 0 and + * 180 degrees each iteration. + * + * Wiring: + * GPIO6 -> Servo signal wire (orange/white) + * 5V -> Servo VCC (red) + * GND -> Servo GND (brown/black) + */ + +#include +#include "pico/stdlib.h" +#include "servo.h" + +/** @brief GPIO pin number for the servo */ +#define SERVO_GPIO 6 + +/** + * @brief Print choice value using if/else if/else conditional + * + * @details Prints "1", "2", or "?" depending on the choice value. + * + * @param choice integer value to evaluate + * @retval None + */ +static void print_if_else(int choice) { + if (choice == 1) { + printf("1\r\n"); + } else if (choice == 2) { + printf("2\r\n"); + } else { + printf("?\r\n"); + } +} + +/** + * @brief Print choice value using switch/case conditional + * + * @details Prints "one", "two", or "??" depending on the choice value. + * + * @param choice integer value to evaluate + * @retval None + */ +static void print_switch(int choice) { + switch (choice) { + case 1: printf("one\r\n"); break; + case 2: printf("two\r\n"); break; + default: printf("??\r\n"); + } +} + +/** + * @brief Sweep servo between 0 and 180 degrees + * + * @details Sets the servo to 0 degrees, waits 500ms, then sets + * to 180 degrees and waits 500ms. + * + * @retval None + */ +static void sweep_servo(void) { + servo_set_angle(0.0f); + sleep_ms(500); + servo_set_angle(180.0f); + sleep_ms(500); +} + +int main(void) { + stdio_init_all(); + int choice = 1; + servo_init(SERVO_GPIO); + while (true) { + print_if_else(choice); + print_switch(choice); + sweep_servo(); + } +} \ No newline at end of file diff --git a/0x001d_static-conditionals/CMakeLists.txt b/0x001d_static-conditionals/CMakeLists.txt new file mode 100644 index 0000000..7b03b00 --- /dev/null +++ b/0x001d_static-conditionals/CMakeLists.txt @@ -0,0 +1,59 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x001d_static-conditionals C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x001d_static-conditionals 0x001d_static-conditionals.c servo.c) + +pico_set_program_name(0x001d_static-conditionals "0x001d_static-conditionals") +pico_set_program_version(0x001d_static-conditionals "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x001d_static-conditionals 1) +pico_enable_stdio_usb(0x001d_static-conditionals 0) + +# Add the standard library to the build +target_link_libraries(0x001d_static-conditionals + pico_stdlib + hardware_pwm + hardware_clocks) + +# Add the standard include files to the build +target_include_directories(0x001d_static-conditionals PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x001d_static-conditionals) + diff --git a/0x001d_static-conditionals/pico_sdk_import.cmake b/0x001d_static-conditionals/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x001d_static-conditionals/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x001d_static-conditionals/servo.c b/0x001d_static-conditionals/servo.c new file mode 100644 index 0000000..d0fefbd --- /dev/null +++ b/0x001d_static-conditionals/servo.c @@ -0,0 +1,107 @@ +/** + * @file servo.c + * @brief Implementation of a simple SG90 servo driver using PWM (50Hz) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "servo.h" +#include "pico/stdlib.h" +#include "hardware/pwm.h" +#include "hardware/clocks.h" + +/** @brief Default minimum pulse width in microseconds */ +static const uint16_t SERVO_DEFAULT_MIN_US = 1000; +/** @brief Default maximum pulse width in microseconds */ +static const uint16_t SERVO_DEFAULT_MAX_US = 2000; + +/** @brief GPIO pin assigned to the servo */ +static uint8_t servo_pin = 0; +/** @brief PWM hardware slice for the servo pin */ +static uint servo_slice = 0; +/** @brief PWM channel within the servo slice */ +static uint servo_chan = 0; +/** @brief PWM counter wrap value for 50 Hz servo */ +static uint32_t servo_wrap = 20000 - 1; +/** @brief Servo PWM frequency in Hz */ +static float servo_hz = 50.0f; +/** @brief Flag indicating servo has been initialized */ +static bool servo_initialized = false; + +/** + * @brief Convert a pulse width in microseconds to a PWM counter level + * + * Uses the configured PWM wrap and servo frequency to map pulse time + * into the channel compare value expected by the PWM hardware. + * + * @param pulse_us Pulse width in microseconds + * @return uint32_t PWM level suitable for pwm_set_chan_level() + */ +static uint32_t pulse_us_to_level(uint32_t pulse_us) { + const float period_us = 1000000.0f / servo_hz; + float counts_per_us = (servo_wrap + 1) / period_us; + return (uint32_t)(pulse_us * counts_per_us + 0.5f); +} + +/** + * @brief Build and apply the PWM slice configuration for 50 Hz servo + * + * Computes the clock divider from the system clock to achieve the + * target servo frequency with the chosen wrap value, then starts + * the PWM slice. + */ +static void apply_servo_config(void) { + pwm_config config = pwm_get_default_config(); + const uint32_t sys_clock_hz = clock_get_hz(clk_sys); + float clock_div = (float)sys_clock_hz / (servo_hz * (servo_wrap + 1)); + pwm_config_set_clkdiv(&config, clock_div); + pwm_config_set_wrap(&config, servo_wrap); + pwm_init(servo_slice, &config, true); +} + +void servo_init(uint8_t pin) { + servo_pin = pin; + gpio_set_function(servo_pin, GPIO_FUNC_PWM); + servo_slice = pwm_gpio_to_slice_num(servo_pin); + servo_chan = pwm_gpio_to_channel(servo_pin); + apply_servo_config(); + servo_initialized = true; +} + +void servo_set_pulse_us(uint16_t pulse_us) { + if (!servo_initialized) return; + if (pulse_us < SERVO_DEFAULT_MIN_US) pulse_us = SERVO_DEFAULT_MIN_US; + if (pulse_us > SERVO_DEFAULT_MAX_US) pulse_us = SERVO_DEFAULT_MAX_US; + uint32_t level = pulse_us_to_level(pulse_us); + pwm_set_chan_level(servo_slice, servo_chan, level); +} + +void servo_set_angle(float degrees) { + if (degrees < 0.0f) degrees = 0.0f; + if (degrees > 180.0f) degrees = 180.0f; + float ratio = degrees / 180.0f; + uint16_t pulse = (uint16_t)(SERVO_DEFAULT_MIN_US + ratio * (SERVO_DEFAULT_MAX_US - SERVO_DEFAULT_MIN_US) + 0.5f); + servo_set_pulse_us(pulse); +} \ No newline at end of file diff --git a/0x001d_static-conditionals/servo.h b/0x001d_static-conditionals/servo.h new file mode 100644 index 0000000..878d029 --- /dev/null +++ b/0x001d_static-conditionals/servo.h @@ -0,0 +1,62 @@ +/** + * @file servo.h + * @brief Header for SG90 servo driver (PWM) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SERVO_H +#define SERVO_H + +#include +#include + +/** + * @brief Initialize servo driver on a given GPIO pin + * + * Configures PWM for a 50 Hz servo (SG90). Call once before using other API. + * + * @param pin GPIO pin number to use for servo PWM + */ +void servo_init(uint8_t pin); + +/** + * @brief Set servo pulse width in microseconds (typical 1000-2000) + * + * @param pulse_us Pulse width in microseconds + */ +void servo_set_pulse_us(uint16_t pulse_us); + +/** + * @brief Set servo angle in degrees (0 to 180) + * + * Maps 0..180 degrees to the configured pulse range (default 1000..2000 us). + * Values outside [0,180] will be clamped. + * + * @param degrees Angle in degrees + */ +void servo_set_angle(float degrees); + +#endif // SERVO_H diff --git a/0x0020_dynamic-conditionals/.gitignore b/0x0020_dynamic-conditionals/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0020_dynamic-conditionals/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0020_dynamic-conditionals/.vscode/c_cpp_properties.json b/0x0020_dynamic-conditionals/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0020_dynamic-conditionals/.vscode/cmake-kits.json b/0x0020_dynamic-conditionals/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0020_dynamic-conditionals/.vscode/extensions.json b/0x0020_dynamic-conditionals/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0020_dynamic-conditionals/.vscode/launch.json b/0x0020_dynamic-conditionals/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0020_dynamic-conditionals/.vscode/settings.json b/0x0020_dynamic-conditionals/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0020_dynamic-conditionals/.vscode/tasks.json b/0x0020_dynamic-conditionals/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x0020_dynamic-conditionals/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0020_dynamic-conditionals/0x0020_dynamic-conditionals.c b/0x0020_dynamic-conditionals/0x0020_dynamic-conditionals.c new file mode 100644 index 0000000..028fcd4 --- /dev/null +++ b/0x0020_dynamic-conditionals/0x0020_dynamic-conditionals.c @@ -0,0 +1,111 @@ +/** + * @file 0x0020_dynamic-conditionals.c + * @brief Dynamic conditionals: UART input drives if/else and switch with servo + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates dynamic (runtime) conditionals using UART input via getchar(). + * Reads a character, evaluates it with if/else and switch/case, and controls + * a servo on GPIO6 based on the input ('1' or '2'). + * + * Wiring: + * GPIO6 -> Servo signal wire (orange/white) + * 5V -> Servo VCC (red) + * GND -> Servo GND (brown/black) + */ + +#include +#include "pico/stdlib.h" +#include "servo.h" + +/** @brief GPIO pin number for the servo */ +#define SERVO_GPIO 6 + +/** + * @brief Evaluate choice with if/else conditional and print result + * + * @details Compares against hex ASCII values 0x31 ('1') and 0x32 ('2'). + * + * @param choice character value received from UART + * @retval None + */ +static void eval_if_else(uint8_t choice) { + if (choice == 0x31) { + printf("1\r\n"); + } else if (choice == 0x32) { + printf("2\r\n"); + } else { + printf("??\r\n"); + } +} + +/** + * @brief Sweep servo from start angle to end angle with a pause + * + * @details Sets the servo to start, waits 500 ms, sets to end, + * waits 500 ms. + * + * @param label text to print before sweeping + * @param start starting angle in degrees + * @param end ending angle in degrees + * @retval None + */ +static void sweep_servo(const char *label, float start, float end) { + printf("%s\r\n", label); + servo_set_angle(start); + sleep_ms(500); + servo_set_angle(end); + sleep_ms(500); +} + +/** + * @brief Process choice with switch/case and drive servo accordingly + * + * @details On '1', sweeps servo 0->180; on '2', sweeps 180->0; + * otherwise prints unknown. + * + * @param choice character value received from UART + * @retval None + */ +static void process_servo_command(uint8_t choice) { + switch (choice) { + case '1': sweep_servo("one", 0.0f, 180.0f); break; + case '2': sweep_servo("two", 180.0f, 0.0f); break; + default: printf("??\r\n"); + } +} + +int main(void) { + stdio_init_all(); + uint8_t choice = 0; + servo_init(SERVO_GPIO); + while (true) { + choice = getchar(); + eval_if_else(choice); + process_servo_command(choice); + } +} diff --git a/0x0020_dynamic-conditionals/CMakeLists.txt b/0x0020_dynamic-conditionals/CMakeLists.txt new file mode 100644 index 0000000..4859d4e --- /dev/null +++ b/0x0020_dynamic-conditionals/CMakeLists.txt @@ -0,0 +1,59 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0020_dynamic-conditionals C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0020_dynamic-conditionals 0x0020_dynamic-conditionals.c servo.c) + +pico_set_program_name(0x0020_dynamic-conditionals "0x0020_dynamic-conditionals") +pico_set_program_version(0x0020_dynamic-conditionals "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0020_dynamic-conditionals 1) +pico_enable_stdio_usb(0x0020_dynamic-conditionals 0) + +# Add the standard library to the build +target_link_libraries(0x0020_dynamic-conditionals + pico_stdlib + hardware_pwm + hardware_clocks) + +# Add the standard include files to the build +target_include_directories(0x0020_dynamic-conditionals PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0020_dynamic-conditionals) + diff --git a/0x0020_dynamic-conditionals/pico_sdk_import.cmake b/0x0020_dynamic-conditionals/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0020_dynamic-conditionals/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0020_dynamic-conditionals/servo.c b/0x0020_dynamic-conditionals/servo.c new file mode 100644 index 0000000..d0fefbd --- /dev/null +++ b/0x0020_dynamic-conditionals/servo.c @@ -0,0 +1,107 @@ +/** + * @file servo.c + * @brief Implementation of a simple SG90 servo driver using PWM (50Hz) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "servo.h" +#include "pico/stdlib.h" +#include "hardware/pwm.h" +#include "hardware/clocks.h" + +/** @brief Default minimum pulse width in microseconds */ +static const uint16_t SERVO_DEFAULT_MIN_US = 1000; +/** @brief Default maximum pulse width in microseconds */ +static const uint16_t SERVO_DEFAULT_MAX_US = 2000; + +/** @brief GPIO pin assigned to the servo */ +static uint8_t servo_pin = 0; +/** @brief PWM hardware slice for the servo pin */ +static uint servo_slice = 0; +/** @brief PWM channel within the servo slice */ +static uint servo_chan = 0; +/** @brief PWM counter wrap value for 50 Hz servo */ +static uint32_t servo_wrap = 20000 - 1; +/** @brief Servo PWM frequency in Hz */ +static float servo_hz = 50.0f; +/** @brief Flag indicating servo has been initialized */ +static bool servo_initialized = false; + +/** + * @brief Convert a pulse width in microseconds to a PWM counter level + * + * Uses the configured PWM wrap and servo frequency to map pulse time + * into the channel compare value expected by the PWM hardware. + * + * @param pulse_us Pulse width in microseconds + * @return uint32_t PWM level suitable for pwm_set_chan_level() + */ +static uint32_t pulse_us_to_level(uint32_t pulse_us) { + const float period_us = 1000000.0f / servo_hz; + float counts_per_us = (servo_wrap + 1) / period_us; + return (uint32_t)(pulse_us * counts_per_us + 0.5f); +} + +/** + * @brief Build and apply the PWM slice configuration for 50 Hz servo + * + * Computes the clock divider from the system clock to achieve the + * target servo frequency with the chosen wrap value, then starts + * the PWM slice. + */ +static void apply_servo_config(void) { + pwm_config config = pwm_get_default_config(); + const uint32_t sys_clock_hz = clock_get_hz(clk_sys); + float clock_div = (float)sys_clock_hz / (servo_hz * (servo_wrap + 1)); + pwm_config_set_clkdiv(&config, clock_div); + pwm_config_set_wrap(&config, servo_wrap); + pwm_init(servo_slice, &config, true); +} + +void servo_init(uint8_t pin) { + servo_pin = pin; + gpio_set_function(servo_pin, GPIO_FUNC_PWM); + servo_slice = pwm_gpio_to_slice_num(servo_pin); + servo_chan = pwm_gpio_to_channel(servo_pin); + apply_servo_config(); + servo_initialized = true; +} + +void servo_set_pulse_us(uint16_t pulse_us) { + if (!servo_initialized) return; + if (pulse_us < SERVO_DEFAULT_MIN_US) pulse_us = SERVO_DEFAULT_MIN_US; + if (pulse_us > SERVO_DEFAULT_MAX_US) pulse_us = SERVO_DEFAULT_MAX_US; + uint32_t level = pulse_us_to_level(pulse_us); + pwm_set_chan_level(servo_slice, servo_chan, level); +} + +void servo_set_angle(float degrees) { + if (degrees < 0.0f) degrees = 0.0f; + if (degrees > 180.0f) degrees = 180.0f; + float ratio = degrees / 180.0f; + uint16_t pulse = (uint16_t)(SERVO_DEFAULT_MIN_US + ratio * (SERVO_DEFAULT_MAX_US - SERVO_DEFAULT_MIN_US) + 0.5f); + servo_set_pulse_us(pulse); +} \ No newline at end of file diff --git a/0x0020_dynamic-conditionals/servo.h b/0x0020_dynamic-conditionals/servo.h new file mode 100644 index 0000000..878d029 --- /dev/null +++ b/0x0020_dynamic-conditionals/servo.h @@ -0,0 +1,62 @@ +/** + * @file servo.h + * @brief Header for SG90 servo driver (PWM) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SERVO_H +#define SERVO_H + +#include +#include + +/** + * @brief Initialize servo driver on a given GPIO pin + * + * Configures PWM for a 50 Hz servo (SG90). Call once before using other API. + * + * @param pin GPIO pin number to use for servo PWM + */ +void servo_init(uint8_t pin); + +/** + * @brief Set servo pulse width in microseconds (typical 1000-2000) + * + * @param pulse_us Pulse width in microseconds + */ +void servo_set_pulse_us(uint16_t pulse_us); + +/** + * @brief Set servo angle in degrees (0 to 180) + * + * Maps 0..180 degrees to the configured pulse range (default 1000..2000 us). + * Values outside [0,180] will be clamped. + * + * @param degrees Angle in degrees + */ +void servo_set_angle(float degrees); + +#endif // SERVO_H diff --git a/0x0023_structures/.gitignore b/0x0023_structures/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0023_structures/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0023_structures/.vscode/c_cpp_properties.json b/0x0023_structures/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0023_structures/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0023_structures/.vscode/cmake-kits.json b/0x0023_structures/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0023_structures/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0023_structures/.vscode/extensions.json b/0x0023_structures/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0023_structures/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0023_structures/.vscode/launch.json b/0x0023_structures/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0023_structures/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0023_structures/.vscode/settings.json b/0x0023_structures/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x0023_structures/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0023_structures/.vscode/tasks.json b/0x0023_structures/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x0023_structures/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0023_structures/0x0023_structures.c b/0x0023_structures/0x0023_structures.c new file mode 100644 index 0000000..657dc5f --- /dev/null +++ b/0x0023_structures/0x0023_structures.c @@ -0,0 +1,144 @@ +/** + * @file 0x0023_structures.c + * @brief Structures: IR remote controls LEDs via a struct-based controller + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates C structures by defining a simple_led_ctrl_t to manage three + * LEDs. An IR receiver on GPIO5 decodes NEC commands and activates the + * corresponding LED (0x0C -> GPIO16, 0x18 -> GPIO17, 0x5E -> GPIO18). + * + * Wiring: + * GPIO5 -> IR receiver OUT + * GPIO16 -> LED1 anode (with current-limiting resistor to GND) + * GPIO17 -> LED2 anode (with current-limiting resistor to GND) + * GPIO18 -> LED3 anode (with current-limiting resistor to GND) + */ + +#include +#include +#include "pico/stdlib.h" +#include "ir.h" + +/** @brief GPIO pin number for the IR receiver */ +#define IR_PIN 5 + +typedef struct { + uint8_t led1_pin; + uint8_t led2_pin; + uint8_t led3_pin; + bool led1_state; + bool led2_state; + bool led3_state; +} simple_led_ctrl_t; + +/** + * @brief Initialize all three LED GPIO pins as outputs + * + * @details Configures led1_pin, led2_pin, and led3_pin from the + * structure as GPIO outputs. + * + * @param leds pointer to the LED controller structure + * @retval None + */ +static void init_led_gpios(simple_led_ctrl_t *leds) { + gpio_init(leds->led1_pin); + gpio_set_dir(leds->led1_pin, GPIO_OUT); + gpio_init(leds->led2_pin); + gpio_set_dir(leds->led2_pin, GPIO_OUT); + gpio_init(leds->led3_pin); + gpio_set_dir(leds->led3_pin, GPIO_OUT); +} + +/** + * @brief Process an NEC IR key and update LED states + * + * @details Maps NEC command codes to LEDs: 0x0C -> LED1, 0x18 -> LED2, + * 0x5E -> LED3. Turns off all LEDs first, then activates the match. + * + * @param leds pointer to the LED controller structure + * @param key NEC command code from IR receiver + * @retval None + */ +static void process_ir_key(simple_led_ctrl_t *leds, int key) { + printf("NEC command: 0x%02X\n", key); + leds->led1_state = (key == 0x0C); + leds->led2_state = (key == 0x18); + leds->led3_state = (key == 0x5E); + gpio_put(leds->led1_pin, leds->led1_state); + gpio_put(leds->led2_pin, leds->led2_state); + gpio_put(leds->led3_pin, leds->led3_state); + sleep_ms(10); +} + +/** + * @brief Poll IR receiver and dispatch key to handler + * + * @details Reads one IR key; if valid, processes it; otherwise + * yields with a short sleep. + * + * @param leds pointer to the LED controller structure + * @retval None + */ +static void poll_ir(simple_led_ctrl_t *leds) { + int key = ir_getkey(); + if (key >= 0) { + process_ir_key(leds, key); + } else { + sleep_ms(1); + } +} + +/** + * @brief Build the default LED controller structure + * + * @details Initializes pins 16-18 with all LEDs off. + * + * @return simple_led_ctrl_t initialized structure + */ +static simple_led_ctrl_t make_default_leds(void) { + simple_led_ctrl_t leds = { + .led1_pin = 16, + .led2_pin = 17, + .led3_pin = 18, + .led1_state = false, + .led2_state = false, + .led3_state = false + }; + return leds; +} + +int main(void) { + stdio_init_all(); + simple_led_ctrl_t leds = make_default_leds(); + init_led_gpios(&leds); + ir_init(IR_PIN); + printf("IR receiver on GPIO %d ready\n", IR_PIN); + while (true) { + poll_ir(&leds); + } +} diff --git a/0x0023_structures/CMakeLists.txt b/0x0023_structures/CMakeLists.txt new file mode 100644 index 0000000..d171bb7 --- /dev/null +++ b/0x0023_structures/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0023_structures C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0023_structures 0x0023_structures.c ir.c) + +pico_set_program_name(0x0023_structures "0x0023_structures") +pico_set_program_version(0x0023_structures "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0023_structures 1) +pico_enable_stdio_usb(0x0023_structures 0) + +# Add the standard library to the build +target_link_libraries(0x0023_structures + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0023_structures PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0023_structures) + diff --git a/0x0023_structures/ir.c b/0x0023_structures/ir.c new file mode 100644 index 0000000..9d5b42a --- /dev/null +++ b/0x0023_structures/ir.c @@ -0,0 +1,130 @@ +/** + * @file ir.c + * @brief Implementation of NEC IR receiver (decoder) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ir.h" +#include "pico/stdlib.h" +#include "pico/time.h" +#include "hardware/gpio.h" + +/** @brief GPIO pin connected to the IR receiver */ +static unsigned int ir_pin = 0; + +/** + * @brief Wait for a GPIO pin to reach a given logic level + * + * Spins until the pin matches the requested level or a microsecond timeout + * is exceeded. Returns the elapsed time in microseconds, or -1 on timeout. + * + * @param gpio GPIO pin to monitor + * @param level Desired logic level (true = HIGH, false = LOW) + * @param timeout_us Maximum wait in microseconds + * @return int64_t Elapsed microseconds, or -1 on timeout + */ +static int64_t wait_for_level(unsigned int gpio, bool level, uint32_t timeout_us) { + absolute_time_t start = get_absolute_time(); + while (gpio_get(gpio) != level) { + if (absolute_time_diff_us(start, get_absolute_time()) > (int64_t)timeout_us) + return -1; + } + return absolute_time_diff_us(start, get_absolute_time()); +} + +/** + * @brief Wait for the NEC 9 ms leader pulse and 4.5 ms space + * + * @return bool true if a valid leader was detected, false on timeout + */ +static bool wait_leader(void) { + if (wait_for_level(ir_pin, 0, 150000) < 0) return false; + int64_t t = wait_for_level(ir_pin, 1, 12000); + if (t < 8000 || t > 10000) return false; + t = wait_for_level(ir_pin, 0, 7000); + if (t < 3500 || t > 5000) return false; + return true; +} + +/** + * @brief Read a single NEC-encoded bit from the IR receiver + * + * Measures the mark/space timing and shifts the result into the + * appropriate byte of the data array. + * + * @param data 4-byte array accumulating received bits + * @param i Bit index (0-31) + * @return bool true on success, false on timeout or protocol error + */ +static bool read_nec_bit(uint8_t *data, int i) { + if (wait_for_level(ir_pin, 1, 1000) < 0) return false; + int64_t t = wait_for_level(ir_pin, 0, 2500); + if (t < 200) return false; + int byte_idx = i / 8; + int bit_idx = i % 8; + if (t > 1200) data[byte_idx] |= (1 << bit_idx); + return true; +} + +/** + * @brief Read all 32 data bits of an NEC frame + * + * @param data 4-byte array filled with the received address and command + * @return bool true if all 32 bits were read, false on timeout + */ +static bool read_32_bits(uint8_t *data) { + for (int i = 0; i < 32; ++i) + if (!read_nec_bit(data, i)) return false; + return true; +} + +/** + * @brief Validate an NEC frame and extract the command byte + * + * Checks that the address and command pairs are bitwise-inverted. + * + * @param data 4-byte NEC frame (addr, ~addr, cmd, ~cmd) + * @return int Command byte (0-255) on success, -1 on validation failure + */ +static int validate_nec_frame(const uint8_t *data) { + if ((uint8_t)(data[0] + data[1]) == 0xFF && (uint8_t)(data[2] + data[3]) == 0xFF) + return data[2]; + return -1; +} + +void ir_init(uint8_t pin) { + ir_pin = pin; + gpio_init(pin); + gpio_set_dir(pin, GPIO_IN); + gpio_pull_up(pin); +} + +int ir_getkey(void) { + if (!wait_leader()) return -1; + uint8_t data[4] = {0, 0, 0, 0}; + if (!read_32_bits(data)) return -1; + return validate_nec_frame(data); +} diff --git a/0x0023_structures/ir.h b/0x0023_structures/ir.h new file mode 100644 index 0000000..84aa67f --- /dev/null +++ b/0x0023_structures/ir.h @@ -0,0 +1,63 @@ +/** + * @file ir.h + * @brief Header for NEC IR receiver (decoder) API + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IR_H +#define IR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the IR receiver GPIO + * + * Configures the given `pin` as an input with pull-up for the NEC IR receiver. + * Call this once during board initialization before calling `ir_getkey()`. + * + * @param pin GPIO pin number connected to IR receiver output + */ +void ir_init(uint8_t pin); + +/** + * @brief Blocking NEC IR decoder + * + * Blocks while waiting for a complete NEC frame. On success returns the + * decoded command byte (0..255). On timeout or protocol error returns -1. + * + * @return decoded command byte (0..255) or -1 on failure + */ +int ir_getkey(void); + +#ifdef __cplusplus +} +#endif + +#endif // IR_H diff --git a/0x0023_structures/pico_sdk_import.cmake b/0x0023_structures/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0023_structures/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/0x0026_functions/.gitignore b/0x0026_functions/.gitignore new file mode 100644 index 0000000..2435c20 --- /dev/null +++ b/0x0026_functions/.gitignore @@ -0,0 +1,2 @@ +build +!.vscode/* diff --git a/0x0026_functions/.vscode/c_cpp_properties.json b/0x0026_functions/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c429d6d --- /dev/null +++ b/0x0026_functions/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/0x0026_functions/.vscode/cmake-kits.json b/0x0026_functions/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/0x0026_functions/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/0x0026_functions/.vscode/extensions.json b/0x0026_functions/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/0x0026_functions/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/0x0026_functions/.vscode/launch.json b/0x0026_functions/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/0x0026_functions/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/0x0026_functions/.vscode/settings.json b/0x0026_functions/.vscode/settings.json new file mode 100644 index 0000000..95be83b --- /dev/null +++ b/0x0026_functions/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/0x0026_functions/.vscode/tasks.json b/0x0026_functions/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/0x0026_functions/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/0x0026_functions/0x0026_functions.c b/0x0026_functions/0x0026_functions.c new file mode 100644 index 0000000..210c7cc --- /dev/null +++ b/0x0026_functions/0x0026_functions.c @@ -0,0 +1,237 @@ +/** + * @file 0x0026_functions.c + * @brief Functions: IR remote controls LEDs with helper function decomposition + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates function decomposition by breaking IR-controlled LED logic + * into small, focused helper functions. An IR receiver on GPIO5 decodes + * NEC commands and activates the corresponding LED with a blink effect. + * + * Wiring: + * GPIO5 -> IR receiver OUT + * GPIO16 -> LED1 anode (with current-limiting resistor to GND) + * GPIO17 -> LED2 anode (with current-limiting resistor to GND) + * GPIO18 -> LED3 anode (with current-limiting resistor to GND) + */ + +#include +#include +#include "pico/stdlib.h" +#include "ir.h" + +/** @brief GPIO pin number for the IR receiver */ +#define IR_PIN 5 + +typedef struct { + uint8_t led1_pin; + uint8_t led2_pin; + uint8_t led3_pin; + bool led1_state; + bool led2_state; + bool led3_state; +} simple_led_ctrl_t; + +/** + * @brief Map NEC IR command code to LED number + * + * @details Translates a received NEC IR command code to a logical + * LED number. Supports three button mappings. + * + * @param ir_command NEC command code from IR receiver + * @retval 1-3 for matched LED, 0 if no match + */ +static int ir_to_led_number(int ir_command) { + if (ir_command == 0x0C) return 1; + if (ir_command == 0x18) return 2; + if (ir_command == 0x5E) return 3; + return 0; +} + +/** + * @brief Get GPIO pin number for a given LED number + * + * @details Retrieves the GPIO pin associated with a logical LED + * number from the LED controller structure. + * + * @param leds pointer to LED controller structure + * @param led_num LED number (1-3) + * @retval GPIO pin number or 0 if invalid + */ +static uint8_t get_led_pin(simple_led_ctrl_t *leds, int led_num) { + if (led_num == 1) return leds->led1_pin; + if (led_num == 2) return leds->led2_pin; + if (led_num == 3) return leds->led3_pin; + return 0; +} + +/** + * @brief Turn off all LEDs in the controller + * + * @details Sets all three LED GPIO outputs to low. + * + * @param leds pointer to LED controller structure + * @retval None + */ +static void leds_all_off(simple_led_ctrl_t *leds) { + gpio_put(leds->led1_pin, false); + gpio_put(leds->led2_pin, false); + gpio_put(leds->led3_pin, false); +} + +/** + * @brief Blink an LED pin a specified number of times + * + * @details Toggles the specified GPIO pin on and off for the given + * count, with configurable delay between transitions. + * + * @param pin GPIO pin number to blink + * @param count number of blink cycles + * @param delay_ms delay in milliseconds for on/off periods + * @retval None + */ +static void blink_led(uint8_t pin, uint8_t count, uint32_t delay_ms) { + for (uint8_t i = 0; i < count; i++) { + gpio_put(pin, true); + sleep_ms(delay_ms); + gpio_put(pin, false); + sleep_ms(delay_ms); + } +} + +/** + * @brief Process IR command and activate corresponding LED + * + * @details Turns off all LEDs, maps the command to an LED number, + * blinks it, then holds it steady. + * + * @param ir_command NEC command code from IR receiver + * @param leds pointer to LED controller structure + * @param blink_count number of blinks before steady state + * @retval LED number activated (1-3), 0 if none, -1 if invalid + */ +static int process_ir_led_command(int ir_command, simple_led_ctrl_t *leds, uint8_t blink_count) { + if (!leds || ir_command < 0) return -1; + leds_all_off(leds); + int led_num = ir_to_led_number(ir_command); + if (led_num == 0) return 0; + uint8_t pin = get_led_pin(leds, led_num); + blink_led(pin, blink_count, 50); + gpio_put(pin, true); + return led_num; +} + +/** + * @brief Initialize all three LED GPIO pins as outputs + * + * @details Configures led1_pin, led2_pin, and led3_pin from the + * structure as GPIO outputs. + * + * @param leds pointer to the LED controller structure + * @retval None + */ +static void init_led_gpios(simple_led_ctrl_t *leds) { + gpio_init(leds->led1_pin); + gpio_set_dir(leds->led1_pin, GPIO_OUT); + gpio_init(leds->led2_pin); + gpio_set_dir(leds->led2_pin, GPIO_OUT); + gpio_init(leds->led3_pin); + gpio_set_dir(leds->led3_pin, GPIO_OUT); +} + +/** + * @brief Poll IR and handle a single received key + * + * @details Reads an IR key, processes the command with 3 blinks, + * and prints the result. + * + * @param leds pointer to the LED controller structure + * @retval None + */ +/** + * @brief Handle a valid IR key press + * + * @details Prints the NEC command, processes it with 3 blinks, + * and reports the activated LED. + * + * @param leds pointer to the LED controller structure + * @param key NEC command code + * @retval None + */ +static void handle_ir_key(simple_led_ctrl_t *leds, int key) { + printf("NEC command: 0x%02X\n", key); + int activated_led = process_ir_led_command(key, leds, 3); + if (activated_led > 0) { + printf("LED %d activated on GPIO %d\n", activated_led, + get_led_pin(leds, activated_led)); + } + sleep_ms(10); +} + +/** + * @brief Poll IR and handle a single received key + * + * @details Reads an IR key; if valid dispatches to handler, + * otherwise yields with a short sleep. + * + * @param leds pointer to the LED controller structure + * @retval None + */ +static void poll_and_handle_ir(simple_led_ctrl_t *leds) { + int key = ir_getkey(); + if (key >= 0) { + handle_ir_key(leds, key); + } else { + sleep_ms(1); + } +} + +/** + * @brief Build the default LED controller structure + * + * @details Initializes pins 16-18 with all LEDs off. + * + * @return simple_led_ctrl_t initialized structure + */ +static simple_led_ctrl_t make_default_leds(void) { + simple_led_ctrl_t leds = { + .led1_pin = 16, .led2_pin = 17, .led3_pin = 18, + .led1_state = false, .led2_state = false, .led3_state = false + }; + return leds; +} + +int main(void) { + stdio_init_all(); + simple_led_ctrl_t leds = make_default_leds(); + init_led_gpios(&leds); + ir_init(IR_PIN); + printf("IR receiver on GPIO %d ready\n", IR_PIN); + while (true) { + poll_and_handle_ir(&leds); + } +} diff --git a/0x0026_functions/CMakeLists.txt b/0x0026_functions/CMakeLists.txt new file mode 100644 index 0000000..b6ea0f9 --- /dev/null +++ b/0x0026_functions/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0026_functions C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0026_functions 0x0026_functions.c ir.c) + +pico_set_program_name(0x0026_functions "0x0026_functions") +pico_set_program_version(0x0026_functions "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0026_functions 1) +pico_enable_stdio_usb(0x0026_functions 0) + +# Add the standard library to the build +target_link_libraries(0x0026_functions + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0026_functions PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0026_functions) + diff --git a/0x0026_functions/ir.c b/0x0026_functions/ir.c new file mode 100644 index 0000000..9d5b42a --- /dev/null +++ b/0x0026_functions/ir.c @@ -0,0 +1,130 @@ +/** + * @file ir.c + * @brief Implementation of NEC IR receiver (decoder) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ir.h" +#include "pico/stdlib.h" +#include "pico/time.h" +#include "hardware/gpio.h" + +/** @brief GPIO pin connected to the IR receiver */ +static unsigned int ir_pin = 0; + +/** + * @brief Wait for a GPIO pin to reach a given logic level + * + * Spins until the pin matches the requested level or a microsecond timeout + * is exceeded. Returns the elapsed time in microseconds, or -1 on timeout. + * + * @param gpio GPIO pin to monitor + * @param level Desired logic level (true = HIGH, false = LOW) + * @param timeout_us Maximum wait in microseconds + * @return int64_t Elapsed microseconds, or -1 on timeout + */ +static int64_t wait_for_level(unsigned int gpio, bool level, uint32_t timeout_us) { + absolute_time_t start = get_absolute_time(); + while (gpio_get(gpio) != level) { + if (absolute_time_diff_us(start, get_absolute_time()) > (int64_t)timeout_us) + return -1; + } + return absolute_time_diff_us(start, get_absolute_time()); +} + +/** + * @brief Wait for the NEC 9 ms leader pulse and 4.5 ms space + * + * @return bool true if a valid leader was detected, false on timeout + */ +static bool wait_leader(void) { + if (wait_for_level(ir_pin, 0, 150000) < 0) return false; + int64_t t = wait_for_level(ir_pin, 1, 12000); + if (t < 8000 || t > 10000) return false; + t = wait_for_level(ir_pin, 0, 7000); + if (t < 3500 || t > 5000) return false; + return true; +} + +/** + * @brief Read a single NEC-encoded bit from the IR receiver + * + * Measures the mark/space timing and shifts the result into the + * appropriate byte of the data array. + * + * @param data 4-byte array accumulating received bits + * @param i Bit index (0-31) + * @return bool true on success, false on timeout or protocol error + */ +static bool read_nec_bit(uint8_t *data, int i) { + if (wait_for_level(ir_pin, 1, 1000) < 0) return false; + int64_t t = wait_for_level(ir_pin, 0, 2500); + if (t < 200) return false; + int byte_idx = i / 8; + int bit_idx = i % 8; + if (t > 1200) data[byte_idx] |= (1 << bit_idx); + return true; +} + +/** + * @brief Read all 32 data bits of an NEC frame + * + * @param data 4-byte array filled with the received address and command + * @return bool true if all 32 bits were read, false on timeout + */ +static bool read_32_bits(uint8_t *data) { + for (int i = 0; i < 32; ++i) + if (!read_nec_bit(data, i)) return false; + return true; +} + +/** + * @brief Validate an NEC frame and extract the command byte + * + * Checks that the address and command pairs are bitwise-inverted. + * + * @param data 4-byte NEC frame (addr, ~addr, cmd, ~cmd) + * @return int Command byte (0-255) on success, -1 on validation failure + */ +static int validate_nec_frame(const uint8_t *data) { + if ((uint8_t)(data[0] + data[1]) == 0xFF && (uint8_t)(data[2] + data[3]) == 0xFF) + return data[2]; + return -1; +} + +void ir_init(uint8_t pin) { + ir_pin = pin; + gpio_init(pin); + gpio_set_dir(pin, GPIO_IN); + gpio_pull_up(pin); +} + +int ir_getkey(void) { + if (!wait_leader()) return -1; + uint8_t data[4] = {0, 0, 0, 0}; + if (!read_32_bits(data)) return -1; + return validate_nec_frame(data); +} diff --git a/0x0026_functions/ir.h b/0x0026_functions/ir.h new file mode 100644 index 0000000..84aa67f --- /dev/null +++ b/0x0026_functions/ir.h @@ -0,0 +1,63 @@ +/** + * @file ir.h + * @brief Header for NEC IR receiver (decoder) API + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IR_H +#define IR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the IR receiver GPIO + * + * Configures the given `pin` as an input with pull-up for the NEC IR receiver. + * Call this once during board initialization before calling `ir_getkey()`. + * + * @param pin GPIO pin number connected to IR receiver output + */ +void ir_init(uint8_t pin); + +/** + * @brief Blocking NEC IR decoder + * + * Blocks while waiting for a complete NEC frame. On success returns the + * decoded command byte (0..255). On timeout or protocol error returns -1. + * + * @return decoded command byte (0..255) or -1 on failure + */ +int ir_getkey(void); + +#ifdef __cplusplus +} +#endif + +#endif // IR_H diff --git a/0x0026_functions/pico_sdk_import.cmake b/0x0026_functions/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/0x0026_functions/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/DDI0553B_y_armv8m_arm.pdf b/DDI0553B_y_armv8m_arm.pdf new file mode 100644 index 0000000..653e7c4 Binary files /dev/null and b/DDI0553B_y_armv8m_arm.pdf differ diff --git a/EHP2.fzz b/EHP2.fzz new file mode 100644 index 0000000..cf56c6d Binary files /dev/null and b/EHP2.fzz differ diff --git a/EHP2_bb.png b/EHP2_bb.png new file mode 100644 index 0000000..be809a8 Binary files /dev/null and b/EHP2_bb.png differ diff --git a/Embedded Hacking.png b/Embedded Hacking.png new file mode 100644 index 0000000..81c55fd Binary files /dev/null and b/Embedded Hacking.png differ diff --git a/Embedded-Hacking.pdf b/Embedded-Hacking.pdf new file mode 100644 index 0000000..af0b640 Binary files /dev/null and b/Embedded-Hacking.pdf differ diff --git a/FINAL/FINAL-01.md b/FINAL/FINAL-01.md new file mode 100644 index 0000000..cdd0c50 --- /dev/null +++ b/FINAL/FINAL-01.md @@ -0,0 +1,266 @@ +# Final Project Option 1: The InfuSafe Pro Incident + +## Project Requirements & Grading Criteria + +--- + +## 📋 Project Overview + +You are a Professional Embedded Reverse Engineer hired by the FDA's Digital Health Center of Excellence. MediTech Solutions deployed an insulin pump with firmware generated by an AI ("OopsieGPT") that contains **four critical bugs** responsible for patient harm. All source code has been destroyed. You must reverse engineer the compiled binary (`FINAL-01.uf2`), identify all four bugs, patch them, and produce a safe firmware image. + +This project covers concepts from **Weeks 1–7**: registers, stack, ARM assembly, memory layout, live debugging, boot process, variables, memory sections, Ghidra, binary patching, integer/float data types, IEEE 754, GPIO inputs, compiler optimizations, constants, I²C, and LCD displays. + +--- + +## 🎯 Learning Objectives + +Upon completion of this project, you will demonstrate the ability to: + +1. Configure Ghidra for ARM Cortex-M33 binary analysis +2. Navigate disassembled code to identify functions, loops, and control flow +3. Locate and modify `#define`-style immediate values in compiled ARM Thumb-2 instructions +4. Convert between decimal, hexadecimal, and IEEE 754 single-precision floating-point representations +5. Patch string literals in a binary without corrupting adjacent data +6. Analyze GPIO pull-up resistor behavior and conditional branch logic +7. Export a patched binary and convert it to UF2 format for flashing +8. Articulate the real-world safety implications of embedded firmware bugs + +--- + +## 📦 Deliverables Checklist + +You must submit **all** of the following. Missing deliverables will result in zero points for the corresponding task. + +| # | Deliverable | Format | Task | +|---|------------|--------|------| +| 1 | Ghidra project screenshot showing project name and processor settings | PNG/JPG | Task 1 | +| 2 | Address table listing `main()`, the `while(true)` loop, and all identified function calls | Markdown table or text | Task 1 | +| 3 | Bug #1 analysis: addresses, original instruction bytes, patched bytes, and written explanation of danger | Markdown/text | Task 2 | +| 4 | Bug #2 analysis: IEEE 754 calculations for both 18.5 and 18.0182, address of the constant, impact calculation | Markdown/text | Task 3 | +| 5 | Bug #3 analysis: string address, original bytes, patched bytes, character-by-character mapping | Markdown/text | Task 4 | +| 6 | Bug #4 analysis: GPIO read mechanism, conditional branch address, original vs. patched instruction, failure mode explanation | Markdown/text | Task 5 | +| 7 | `FINAL-01_fixed.bin` — the exported patched binary | BIN file | Task 6 | +| 8 | `FINAL-01_fixed.uf2` — the UF2-converted binary | UF2 file | Task 6 | +| 9 | Summary table of all patches with before/after byte values | Markdown table | Task 6 | +| 10 | Bonus written responses (optional) | Markdown/text | Bonus | + +--- + +## 📊 Grading Rubric — Detailed Breakdown + +### Total Points: 85 (+ 15 bonus) = 100 maximum + +--- + +### Task 1: Setup and Initial Analysis — 10 points + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Ghidra project created with correct name (`InfuSafe_Pro_Investigation`) | 2 | Project name matches exactly | Minor naming deviation | No project created | +| Processor configured as ARM Cortex 32-bit, little endian | 2 | Correct processor and endianness | Correct processor, wrong endianness | Wrong processor entirely | +| Base address set to `0x10000000` | 2 | Correct base address | Off by a nibble | Wrong or default base address | +| Address of `main()` identified | 2 | Correct address documented | Within one function of correct | Not found | +| Address of `while(true)` loop and at least 3 function calls identified | 2 | Loop + 3 calls with addresses | Loop OR 3 calls (not both) | Neither identified | + +--- + +### Task 2: Find Bug #1 — MAX_SINGLE_DOSE — 15 points + +**What to find:** The maximum single dose is set to 150 (0x96) instead of 50 (0x32). + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Found the `cmp` instruction comparing against 0x96 with correct address | 5 | Exact address and instruction documented | Correct value found, address off | Not found | +| Patched both the `cmp` and the `mov` cap value from 0x96 to 0x32 | 5 | Both instructions patched with correct byte values shown | Only one instruction patched | No patch attempted | +| Explained why 150 units is dangerous (clinical reasoning) | 5 | Mentions typical dose ranges, hypoglycemia risk, and potential for seizures/coma/death | Mentions danger but lacks clinical specifics | No explanation | + +--- + +### Task 3: Find Bug #2 — GLUCOSE_CONVERSION Float — 15 points + +**What to find:** The glucose conversion constant is 18.5 (should be 18.0182), stored as IEEE 754 float. + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Correct IEEE 754 representation of 18.5 (`0x41940000`) | 3 | Correct hex with work shown (sign, exponent, mantissa) | Correct hex but no work shown | Wrong value | +| Correct IEEE 754 representation of 18.0182 (`0x41902546`) | 3 | Correct hex with work shown | Correct hex but no work shown | Wrong value | +| Found the address of the float constant in the binary | 4 | Correct address with little-endian byte pattern documented | Correct value found at approximate location | Not found | +| Impact analysis: calculated the percentage error and explained effect on dosing | 5 | Shows 2.7% calculation, explains device reads glucose HIGH → calculates higher dose → patient gets excess insulin → hypoglycemia | Mentions error direction but no calculation | No analysis | + +--- + +### Task 4: Find Bug #3 — Wrong Display String — 15 points + +**What to find:** The LCD shows "SAFE Dose:" instead of "WARN Dose:" when dose exceeds 25 units. + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Found the string "SAFE Dose:" in the binary with correct address | 5 | Exact address documented, found via Ghidra string search or manual hex inspection | Approximate location | Not found | +| Patched string correctly: S→W (0x53→0x57), F→R (0x46→0x52), E→N (0x45→0x4E) | 5 | All three byte changes documented with addresses; "A" left unchanged; string length preserved | Correct text but missing byte-level documentation | Wrong length string or corrupted adjacent data | +| Explained patient safety implications | 5 | Explains that "SAFE" label on high doses gives false reassurance, patient may not verify, leading to acceptance of dangerous dose | Mentions it's misleading but lacks detail | No explanation | + +--- + +### Task 5: Find Bug #4 — Emergency Stop Logic Inversion — 20 points + +**What to find:** The emergency stop button logic is inverted because of pull-up resistor misunderstanding. Pressing the button DELIVERS insulin instead of stopping it. + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Found the GPIO read for pin 15 (may be inlined as SIO register read at `0xD0000000`) | 5 | Documents the inlined `gpio_get` with SIO base address, bit mask for GPIO 15, and explains that the compiler inlined it | Found a `gpio_get` call or equivalent, even if not perfectly explained | Not found | +| Identified the conditional branch (`beq`/`bne`) that checks button state | 5 | Correct branch instruction address, explains zero flag behavior after `ands` with bit mask | Found a conditional branch in the right area | Not found | +| Applied correct patch (e.g., `beq` → `bne`, byte change from `d0` → `d1`) | 5 | Correct byte change documented with address; binary verified to work after patch | Correct concept but wrong byte offset or encoding | No patch or patch breaks binary | +| Explained the failure mode: how inverted logic kills patients | 5 | Step-by-step: patient panics → presses stop → device DELIVERS insulin → blood sugar drops further → seizures/coma/death | Mentions inversion but does not trace through the patient scenario | No explanation | + +**Accepted alternative patches (full credit):** +- Change `beq` to `bne` (recommended) +- Change comparison value in `cmp` +- Add `eor r0, r0, #1` after GPIO read +- Swap the two code blocks after the branch + +--- + +### Task 6: Export and Verify — 10 points + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Exported `FINAL-01_fixed.bin` from Ghidra | 3 | File submitted and is a valid binary | File submitted but corrupted or wrong format | Not submitted | +| Converted to `FINAL-01_fixed.uf2` using `uf2conv.py` with correct flags | 3 | UF2 file submitted; used `--base 0x10000000 --family 0xe48bff59` | UF2 created but with wrong base or family | Not submitted | +| Summary table of ALL patches with addresses, original bytes, and patched bytes | 4 | Complete table covering all 4 bugs with exact addresses and hex values | Table present but missing entries or has errors | No summary | + +--- + +### Bonus Question 1: ARM Calling Convention (AAPCS) — 5 bonus points + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Explains r0–r3 for function arguments | 2 | Correct and gives example from the binary | Correct but generic (not tied to this binary) | Incorrect | +| Explains return value in r0 | 1 | Correct with relevant example | Correct but no example | Incorrect | +| Provides practical example of how AAPCS helped identify a parameter in this project | 2 | Specific example (e.g., "r0 = 15 before gpio_init call tells us it's initializing pin 15") | Generic example not from this project | No example | + +--- + +### Bonus Question 2: "The AI Wrote Bad Code" Defense — 5 bonus points + +**Requirement:** 200 words or less explaining why this is NOT a valid legal defense. + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Addresses manufacturer legal responsibility (FDA 21 CFR Part 820) | 2 | References specific regulation or standard | Mentions legal responsibility generally | Not addressed | +| Identifies specific failed processes (code review, testing, validation) | 2 | Names at least 3 specific failures (e.g., no code review, no unit testing, no HIL testing, no clinical validation) | Names 1–2 failures | Not addressed | +| References relevant precedent or regulatory framework | 1 | Mentions IEC 62304, ISO 14971, Therac-25 precedent, or equivalent | Mentions regulation generally | Not addressed | + +--- + +### Bonus Question 3: Secure Development Lifecycle — 5 bonus points + +**Requirement:** Propose THREE technical checks that would have caught these bugs. + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Check 1 is practical and specific | 2 | Names a specific tool/process AND explains which bug(s) it catches | Names a process but doesn't connect to bugs | Vague or impractical | +| Check 2 is practical and specific | 2 | Names a specific tool/process AND explains which bug(s) it catches | Names a process but doesn't connect to bugs | Vague or impractical | +| Check 3 is practical and specific | 1 | Names a specific tool/process AND explains which bug(s) it catches | Names a process but doesn't connect to bugs | Vague or impractical | + +--- + +## 📋 Partial Credit Policy + +The following partial credit guidelines apply across all tasks: + +| Scenario | Credit | +|----------|--------| +| Identified bug location correctly but patch is incorrect | 60% of task points | +| Explained concept correctly but could not locate in binary | 40% of task points | +| Patched correctly but could not explain why the fix works | 50% of task points | +| Used correct approach but minor byte/address error | 75% of task points | +| Documented analysis process even though result is wrong | 30% of task points | + +--- + +## ðŸ› ï¸ Required Tools + +| Tool | Purpose | Required For | +|------|---------|-------------| +| Ghidra | Static analysis and binary patching | Tasks 1–6 | +| Python | UF2 conversion (`uf2conv.py`) | Task 6 | +| Serial monitor (PuTTY/minicom/screen) | Verify serial output after flashing | Task 6 | +| Hex/IEEE 754 calculator | Float conversions | Task 3 | +| Raspberry Pi Pico 2 with hardware | Verify patched firmware | Task 6 | + +--- + +## 📎 Hardware Setup Reference + +| Component | GPIO Pin | Purpose | +|-----------|----------|---------| +| Blood Glucose Sensor | ADC (GPIO 26) | Reads patient's blood sugar | +| Status LED (Red) | GPIO 16 | Error/alarm indicator | +| Status LED (Green) | GPIO 17 | Normal operation indicator | +| Pump Active LED (Yellow) | GPIO 18 | Insulin delivery indicator | +| Emergency Stop Button | GPIO 15 (pull-up) | Patient halts delivery | +| I²C LCD Display | GPIO 2 (SDA), GPIO 3 (SCL) | Shows dosage and status | + +--- + +## 📎 Memory Map Reference + +| Region | Start Address | End Address | Purpose | +|--------|--------------|-------------|---------| +| Flash (XIP) | `0x10000000` | `0x10200000` | Code and constants | +| SRAM | `0x20000000` | `0x20082000` | Variables and stack | +| Peripherals | `0x40000000` | `0x50000000` | Hardware registers | +| SIO | `0xD0000000` | `0xD0000100` | Single-cycle I/O | + +--- + +## 📎 Bug Summary (Known Information Given to Students) + +You are told there are exactly **four** bugs. You are given these hints: + +| Bug # | Category | Severity | Hint | +|-------|----------|----------|------| +| 1 | `#define` value | CRITICAL | MAX_SINGLE_DOSE is way too high | +| 2 | `const float` | HIGH | GLUCOSE_CONVERSION factor is wrong | +| 3 | String literal | HIGH | Display shows wrong safety status | +| 4 | GPIO logic | CRITICAL | Emergency stop is completely inverted | + +--- + +## â° Deadline & Submission + +- This is a **take-home final project**. See the course syllabus for the due date. +- Submit all deliverables as a single ZIP file or as specified by your instructor. +- Late submissions: see syllabus late work policy. + +--- + +## 📊 Grade Scale + +| Grade | Percentage | Points (of 85 base) | +|-------|------------|-------------------| +| A | 90–100% | 77–85 (or 90–100 with bonus) | +| B | 80–89% | 68–76 | +| C | 70–79% | 60–67 | +| D | 60–69% | 51–59 | +| F | Below 60% | Below 51 | + +Bonus points can raise your score above the base 85 (up to 100 maximum). + +--- + +## 📜 Academic Integrity + +By submitting this final project, you certify that: +1. This is your own work +2. You have not shared answers with other students +3. You understand the ethical implications of embedded security research +4. You will only use these skills for lawful purposes + +--- + +## 📚 Reference Material + +- ARM Cortex-M33 Technical Reference Manual +- IEEE 754 single-precision floating-point standard +- Ghidra documentation: [https://ghidra-sre.org/](https://ghidra-sre.org/) diff --git a/FINAL/FINAL-02.md b/FINAL/FINAL-02.md new file mode 100644 index 0000000..6b8c29e --- /dev/null +++ b/FINAL/FINAL-02.md @@ -0,0 +1,300 @@ +# Final Project Option 2: Operation Dark Eclipse + +## Project Requirements & Grading Criteria + +--- + +## 📋 Project Overview + +You are an embedded reverse engineer working for an allied intelligence agency. A hostile underground facility is operating centrifuge cascades controlled by RP2350-based firmware. Your mission is to reverse engineer the centrifuge control binary (`FINAL-02.uf2`), modify it to sabotage the centrifuges (double the motor speed), mask the sabotage from operators (keep green LED on, LCD showing normal readings), and produce a weaponized firmware image — all without the operators detecting any anomaly. + +This project covers concepts from **Weeks 1–11**: registers, stack, ARM assembly, memory layout, live debugging, boot process, variables, memory sections, Ghidra, binary patching, integer/float data types, IEEE 754, GPIO inputs, compiler optimizations, constants, I²C, LCD displays, operators, floating-point hacking, conditionals, PWM, servo control, branch modification, NOP-ing, structures, functions, and log desynchronization. + +--- + +## 🎯 Learning Objectives + +Upon completion of this project, you will demonstrate the ability to: + +1. Configure Ghidra for ARM Cortex-M33 binary analysis +2. Identify inlined functions in optimized Thumb-2 code (the compiler inlines `update_display()`, `update_leds()`, and `set_motor_speed()` directly into `main()`) +3. Recognize RP2350 GPIO coprocessor instructions (`mcrr`) as `gpio_put` equivalents +4. Locate and modify immediate values controlling sweep range, LED thresholds, and display constants +5. Separate "actual" speed from "displayed" speed in a compiled binary to achieve log desynchronization +6. Export a patched binary and convert it to UF2 format +7. Analyze the ethical dimensions of offensive cyber operations +8. Compare your attack methodology to STUXNET and articulate defensive countermeasures + +--- + +## 📦 Deliverables Checklist + +You must submit **all** of the following. Missing deliverables will result in zero points for the corresponding task. + +| # | Deliverable | Format | Task | +|---|------------|--------|------| +| 1 | Ghidra project screenshot showing project name (`Dark_Eclipse`), processor settings (ARM Cortex, little endian), and base address (`0x10000000`) | PNG/JPG | Task 1 | +| 2 | Address table listing `main()`, `servo_set_angle()`, LED GPIO operations, LCD operations, sweep loop start, and sweep direction logic | Markdown table or text | Task 1 | +| 3 | Speed doubling analysis: addresses of sweep limit comparison and endpoint assignment, original bytes, patched bytes, and method explanation | Markdown/text | Task 2 | +| 4 | Overspeed masking analysis: addresses of LED control logic, original bytes, patched bytes (or explanation of why no patch is needed if using surgical method) | Markdown/text | Task 3 | +| 5 | Display falsification analysis: address of display constant, original bytes, patched bytes (or explanation of why no patch is needed if using surgical method) | Markdown/text | Task 4 | +| 6 | `FINAL-02_compromised.bin` — the exported patched binary | BIN file | Task 5 | +| 7 | `FINAL-02_compromised.uf2` — the UF2-converted binary | UF2 file | Task 5 | +| 8 | Verification report: description of observed servo sweep range, LED states, LCD reading, and serial output after flashing | Markdown/text | Task 5 | +| 9 | Written analysis: STUXNET comparison (500 words), ethical analysis (300 words), defensive measures (3 proposals) | Markdown/text | Questions 1–3 | + +--- + +## 📊 Grading Rubric — Detailed Breakdown + +### Total Points: 130 + +--- + +### Task 1: Initial Analysis — 15 points + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Ghidra project created with correct name (`Dark_Eclipse`) | 3 | Project name matches exactly | Minor naming deviation | No project created | +| Processor configured as ARM Cortex 32-bit, little endian | 3 | Correct processor and endianness selected | Correct processor, wrong endianness | Wrong processor entirely | +| Base address set to `0x10000000` | 3 | Correct base address | Off by a nibble | Wrong or default base address | +| `main()` function identified with correct address | 3 | Correct address documented | Within one function of correct | Not found | +| Sweep loop identified: loop body start address AND direction toggle logic located | 2 | Both sweep loop start and direction change documented | Only one of the two documented | Neither found | +| At least 3 additional key addresses documented (e.g., `servo_set_angle`, `lcd_puts`, `sleep_ms`, `snprintf`, LED GPIO operations) | 1 | 3+ addresses correct | 1–2 addresses correct | None found | + +--- + +### Task 2: Double the Motor Speed — 25 points + +**Objective:** The servo must sweep 0°→160°→0° instead of 0°→80°→0°. + +**Multiple valid methods exist.** Full credit is awarded for ANY method that achieves the objective with correct documentation. + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Identified the correct location(s) for speed modification | 10 | Correct address(es) documented; instruction(s) identified with hex bytes; explains which instructions control sweep range | Found the right area but wrong specific instruction | Not found | +| Correct patch applied and verified | 10 | Patch bytes documented (before/after); binary runs and servo sweeps to 160° | Patch concept is correct but byte value or address is slightly off | No patch or patch breaks binary | +| Documentation of method and reasoning | 5 | Explains WHY the chosen instructions control the sweep range; traces data flow from constant → comparison → endpoint assignment | States what was changed but not why | No documentation | + +**Accepted methods (full credit for any):** + +| Method | Description | Minimum Patches | +|--------|-------------|-----------------| +| A — Surgical (recommended) | Change only sweep limit `cmp` and sweep endpoint `movs` | 2 instructions, 3 bytes | +| B — Brute force | Change all occurrences of `0x50` (TARGET_SPEED) to `0xA0` | 5+ instructions (requires fixing display/LEDs separately) | +| C — Multiply | Insert a `vadd.f32 s15,s15,s15` before servo call | Requires careful instruction replacement without expanding binary | + +--- + +### Task 3: Mask the Overspeed Warning — 25 points + +**Objective:** Green LED must remain ON at all times. Red LED must NEVER illuminate, regardless of actual speed. + +**IMPORTANT:** If you used **Method A (surgical)** from Task 2, the green LED stays on automatically because `displayed_speed` was never changed from 80, which is below the 90 threshold. In this case, you must **explain why** no LED patch is needed for full credit. + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Identified LED control logic correctly (GPIO coprocessor instructions, pin numbers 0x10/0x11, overspeed threshold comparison) | 10 | Documents the `cmp` against threshold (0x5A), the branch instruction, and the GPIO `mcrr` instructions for both LED paths; OR explains why surgical method makes patching unnecessary | Found LED operations but missed the comparison or branch | Not found | +| Correct patch ensures green is always on (or explains why no patch is needed) | 10 | Patch bytes documented (before/after) with method explained; OR clear explanation of why Method A makes the display path always take the green branch (displayed_speed 80 < threshold 90) | Green is usually on but red sometimes activates | No patch and no explanation | +| Documentation and verification | 5 | States the expected LED behavior after patching and confirms it matches; OR provides complete logical trace for why surgical method guarantees green | Partial explanation | No documentation | + +**Accepted methods (full credit for any):** + +| Method | Description | +|--------|-------------| +| Built-in (Method A from Task 2) | No patch needed — explain why `displayed_speed = 80 < threshold 90` keeps green on | +| Swap GPIO pins | Change pin numbers in overspeed path: `0x10`↔`0x11` | +| Raise threshold | Change `cmp r3, #0x5a` to `cmp r3, #0xff` (unreachable) | +| Invert branch | Change `bhi` to `bls` | +| NOP the branch | Replace `bhi` with `nop` (0x00BF) so overspeed path is never taken | + +--- + +### Task 4: Falsify the Display Reading — 25 points + +**Objective:** The LCD must display the ORIGINAL speed range (0–80 RPM) while the servo physically sweeps 0°–160°. + +**IMPORTANT:** If you used **Method A (surgical)** from Task 2, the LCD already shows "Speed: 80 RPM" automatically because the display constant (`mov.w r12, #0x50`) was never changed. In this case, you must **explain why** no display patch is needed for full credit. + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Identified display/motor separation in the binary: the `displayed_speed` assignment uses a separate inlined constant from the sweep limit | 10 | Documents the `mov.w r12, #0x50` instruction (display constant) as distinct from the `movs r5, #0x50` (sweep endpoint); explains that the compiler inlined TARGET_SPEED as independent immediates; OR traces the `snprintf` argument back to `r12` | Found the display path but did not identify the separation from motor path | Not found | +| Correct patch preserves original display value (or explains why no patch is needed) | 10 | Patch bytes documented; LCD confirmed to show 80 while servo sweeps to 160; OR provides complete logical trace for why Method A inherently separates display from motor | Display sometimes shows correct value, sometimes not | No patch and no explanation | +| Verified LCD shows 80 RPM while servo sweeps to 160° | 5 | Confirms observation: LCD reads "Speed: 80 RPM" during full sweep AND explains why this constitutes successful log desynchronization | Partial verification | No verification | + +**Accepted methods (full credit for any):** + +| Method | Description | +|--------|-------------| +| Built-in (Method A from Task 2) | No patch needed — explain that the display constant at `0x1000033e` was untouched | +| Restore display constant | If brute-force changed `0x1000033e`, change it back from `0xA0` to `0x50` | +| Right-shift snprintf argument | Replace `mov r3, r12` with `lsr.w r3, r12, #1` before `snprintf` call | + +--- + +### Task 5: Export and Verify — 10 points + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Exported `FINAL-02_compromised.bin` from Ghidra | 2 | File submitted and is a valid binary | File submitted but corrupted | Not submitted | +| Converted to `FINAL-02_compromised.uf2` using `uf2conv.py` with correct flags (`--base 0x10000000 --family 0xe48bff59`) | 2 | UF2 file submitted; correct conversion command documented | UF2 created but wrong base or family | Not submitted | +| Servo sweeps 0°–160° (visibly wider arc than original) | 2 | Confirmed and documented | Sweep is wider but not exactly double | No change observed | +| Green LED always on, red LED never on | 2 | Both confirmed | Green on but red occasionally flickers | LEDs not correct | +| LCD shows "Speed: 80 RPM" constantly while servo sweeps to 160° | 1 | Confirmed | LCD shows correct value intermittently | LCD shows doubled value | +| Serial output shows actual sweep_pos reaching 160 at endpoint | 1 | Confirmed via serial monitor | Serial not checked | Serial shows original values | + +--- + +### Question 1: The STUXNET Parallel — 10 points + +**Requirement:** 500 words or less comparing your attack to the original STUXNET worm. + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Accurate comparison of objectives (both target centrifuges, both cause physical destruction via cyber means) | 4 | Specific parallels drawn: target type, method of sabotage, physical outcome through digital means | Generic comparison without specifics | Not addressed | +| Explains why log/display desynchronization is dangerous (operators trust instruments, no visual warning until catastrophic failure) | 3 | Articulates the trust model: humans cannot directly observe centrifuge speed → rely on instruments → when instruments lie, no warning exists | Mentions desynchronization but doesn't explain the trust model | Not addressed | +| Explains why this attack type is nearly impossible to detect remotely (appears as mechanical failure, no malware signatures, attribution-free) | 3 | Discusses: legitimate firmware appearance, no network indicators, failure looks like manufacturing defect or material fatigue | Mentions difficulty of detection but lacks analysis | Not addressed | + +--- + +### Question 2: Ethical Considerations — 10 points + +**Requirement:** 300 words or less discussing when offensive cyber operations are justified. + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Analyzes when this type of operation is justified (proportionality, prevention of greater harm, last resort) | 4 | Balanced analysis considering multiple perspectives; discusses proportionality principle | One-sided analysis (either fully for or fully against) | Not addressed | +| Proposes safeguards for offensive cyber operations (legal authorization, proportionality assessment, accountability, minimization of collateral effects) | 3 | Names at least 3 specific safeguards with brief justification | Names 1–2 safeguards | Not addressed | +| Reflects on Agent NIGHTINGALE's sacrifice in the ethical calculus (emotional weight should inform but not override ethical analysis) | 3 | Nuanced reflection: honor through excellence, not by abandoning ethical constraints | Mentions NIGHTINGALE but doesn't integrate into ethical framework | Not addressed | + +--- + +### Question 3: Defensive Measures — 10 points + +**Requirement:** Propose THREE technical countermeasures (one hardware-level, one firmware-level, one operational security). + +| Criterion | Points | Full Credit | Partial Credit | No Credit | +|-----------|--------|-------------|----------------|-----------| +| Hardware-level protection is practical and specific (e.g., HSM for firmware verification, secure boot, tamper-evident seals, redundant independent sensors) | 3 | Specific technology named, explained how it prevents the attack vector | Generic mention of "better hardware security" | Not addressed | +| Firmware-level protection is practical and specific (e.g., code signing, runtime integrity checks, watchdog comparing sensor readings with motor commands, MPU) | 4 | Specific technology named, explained how it prevents the attack vector | Generic mention of "better firmware security" | Not addressed | +| Operational security improvement is practical and specific (e.g., air-gapped programming, multi-person integrity for updates, independent tachometer, golden image comparison) | 3 | Specific process named, explained how it prevents the attack vector | Generic mention of "better operational security" | Not addressed | + +--- + +## 📋 Partial Credit Policy + +The following partial credit guidelines apply across all tasks: + +| Scenario | Credit | +|----------|--------| +| Correct concept and approach, wrong specific address or byte value | 75% of task points | +| Identified location correctly but patch is incorrect or breaks binary | 60% of task points | +| Explained concept correctly but could not locate in binary | 40% of task points | +| Patched correctly but could not explain why the fix works | 50% of task points | +| Documented analysis process thoroughly even though result is wrong | 30% of task points | +| Display blank but motor speed doubling works | 20% of display task points | +| Partial speed increase (e.g., 1.5x instead of 2x) | 60% of speed task points | +| Green LED on but red LED occasionally activates | 40% of LED task points | + +--- + +## âš ï¸ Common Pitfalls (Read Carefully) + +These are the most frequent mistakes. Avoid them. + +| Pitfall | Consequence | How to Avoid | +|---------|-------------|--------------| +| Looking for `update_leds` / `update_display` / `set_motor_speed` as separate `bl` calls | You will find nothing — the compiler **inlined** all three into `main()` | Look for what the code DOES (GPIO coprocessor writes, `snprintf` calls, `servo_set_angle` calls) | +| Not recognizing `mcrr p0,0x4,...` as `gpio_put` | You will miss all LED control logic | On RP2350, `gpio_put(pin, value)` compiles to a coprocessor instruction, not a function call | +| Using ARM (A32) instruction encoding instead of Thumb-2 | Patched binary will be corrupted | This is a Cortex-M33 binary — ALL instructions are Thumb/Thumb-2 (16-bit and 32-bit mixed) | +| Changing ALL `0x50` values blindly (brute force) | LCD shows 160 and red LED turns on — breaks deception | Use the surgical approach: only change sweep-limit instructions, leave display constant untouched | +| Only changing the endpoint set without adjusting the comparison | Sweep jumps from 79 to 160 instantly (not smooth) | Must change BOTH `cmp r5, #0x4e` AND `movs r5, #0x50` | +| Replacing a 16-bit Thumb instruction with a 32-bit Thumb-2 instruction | Shifts all subsequent instructions — corrupts binary | Replacements must be same byte width | + +--- + +## ðŸ› ï¸ Required Tools + +| Tool | Purpose | Required For | +|------|---------|-------------| +| Ghidra | Static analysis and binary patching | Tasks 1–5 | +| Python | UF2 conversion (`uf2conv.py`) | Task 5 | +| Serial monitor (PuTTY/minicom/screen) | Verify serial output after flashing | Task 5 | +| Raspberry Pi Pico 2 with servo, LEDs, LCD | Verify patched firmware on hardware | Task 5 | + +--- + +## 📎 Hardware Setup Reference + +| Component | GPIO Pin | Purpose | +|-----------|----------|---------| +| Servo Motor (SG90) | GPIO 6 | Simulates centrifuge motor | +| Green LED | GPIO 17 (via 200Ω) | Normal operation indicator | +| Red LED | GPIO 16 (via 200Ω) | Overspeed warning indicator | +| 1602 LCD (I²C) | GPIO 2 (SDA), GPIO 3 (SCL) | Speed and status display | +| 1000µF Capacitor | VBUS to GND | Servo power filtering | + +--- + +## 📎 Memory Map Reference + +| Region | Start Address | End Address | Purpose | +|--------|--------------|-------------|---------| +| Flash (XIP) | `0x10000000` | `0x10200000` | Code and constants | +| SRAM | `0x20000000` | `0x20082000` | Variables and stack | +| Peripherals | `0x40000000` | `0x50000000` | Hardware registers | +| PWM | `0x40050000` | `0x40058000` | PWM control | + +--- + +## 📎 Key Constants Reference + +| Constant | Value | Hex | Purpose | +|----------|-------|-----|---------| +| TARGET_SPEED | 80 | 0x50 | Sweep upper limit | +| SPEED_MAX | 180 | 0xB4 | Maximum servo angle | +| OVERSPEED_THRESHOLD | 90 | 0x5A | Red LED trigger point | +| SWEEP_STEP | 1 | 0x01 | Position increment per step | +| SWEEP_DELAY_MS | 20 | 0x14 | Delay per sweep step | +| LED_GREEN_PIN | 17 | 0x11 | Green LED GPIO pin | +| LED_RED_PIN | 16 | 0x10 | Red LED GPIO pin | + +--- + +## â° Deadline & Submission + +- This is a **take-home final project**. See the course syllabus for the due date. +- Submit all deliverables as a single ZIP file or as specified by your instructor. +- Late submissions: see syllabus late work policy. + +--- + +## 📊 Grade Scale + +| Grade | Percentage | Points (of 130) | +|-------|------------|-----------------| +| A | 90–100% | 117–130 | +| B | 80–89% | 104–116 | +| C | 70–79% | 91–103 | +| D | 60–69% | 78–90 | +| F | Below 60% | Below 78 | + +--- + +## 📜 Academic Integrity + +By submitting this final project, you certify that: +1. This is your own work +2. You have not shared answers with other students +3. You understand the ethical implications of embedded security research +4. You understand that the skills demonstrated here may only be used in authorized, lawful contexts +5. You recognize the difference between academic exercises and real-world operations + +--- + +## 📚 Reference Material + +- ARM Cortex-M33 Technical Reference Manual +- ARM Thumb-2 Instruction Set Reference +- Ghidra documentation: [https://ghidra-sre.org/](https://ghidra-sre.org/) +- STUXNET analysis: Langner, Ralph. "To Kill a Centrifuge" (for Q1 reference) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/PARTS.md b/PARTS.md new file mode 100644 index 0000000..6e635b6 --- /dev/null +++ b/PARTS.md @@ -0,0 +1,20 @@ +# Embedded Hacking - Hardware Parts and Products + +Here is a comprehensive list of hardware parts, products, and components referenced in the repository for the Embedded Hacking course: + +## Specific Components & Sensors +The following specific components are used throughout the course experiments and drivers (many of which are included in the component kit above): +- [1x Full-size Breadboard (Long)](https://www.amazon.com/s?k=full+size+breadboard) +- [1x Assorted Jumper Wires (Male-to-Male, Male-to-Female, Female-to-Female)](https://www.amazon.com/s?k=breadboard+jumper+wires+assortment) +- [1x Raspberry Pi Pico 2 w/ Header](https://www.amazon.com/s?k=raspberry+pi+pico+2+with+pre-soldered+header) +- [1x Raspberry Pi Pico Debug Probe](https://www.amazon.com/s?k=raspberry+pi+debug+probe) +- [2x USB A-Male to USB Micro-B Cables (1 for Pico 2, 1 for Debug Probe)](https://www.amazon.com/s?k=micro+usb+cable+2+pack) +- [3x 5mm LEDs (1 Red, 1 Green, 1 Yellow)](https://www.amazon.com/s?k=5mm+led+kit) +- [3x 100, 220 or 330 Ohm Resistors (for LEDs)](https://www.amazon.com/s?k=resistor+assortment+kit) +- [1x Push Button (Tactile switch)](https://www.amazon.com/s?k=tactile+push+button+assortment) +- [1x 1602 LCD (with PCF8574 I2C backpack)](https://www.amazon.com/s?k=1602+lcd+i2c+module) +- [1x DHT11 Temperature & Humidity Sensor](https://www.amazon.com/s?k=dht11+temperature+and+humidity+sensor) +- [1x SG90 Servo Motor](https://www.amazon.com/s?k=sg90+micro+servo+motor) +- [1x 1000uF 25V Capacitor (for Servo power stabilization)](https://www.amazon.com/Cionyce-Capacitor-Electrolytic-CapacitorsMicrowave/dp/B0B63CCQ2N) +- [1x Infrared (IR) Receiver (VS1838B)](https://www.amazon.com/s?k=vs1838b+ir+receiver+module) +- [1x Infrared (IR) Remote Controller (NEC-compatible)](https://www.amazon.com/s?k=arduino+ir+remote+control) diff --git a/README.md b/README.md new file mode 100644 index 0000000..b05f601 --- /dev/null +++ b/README.md @@ -0,0 +1,440 @@ +![image](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded%20Hacking.png?raw=true) + +## FREE Reverse Engineering Self-Study Course [HERE](https://github.com/mytechnotalent/Reverse-Engineering-Tutorial) + +
+ +# Today's Tutorial [July, 3 2026] +## Lesson 234: Go Hacking Course (Chapter 1: Hello Distributed System World) +This chapter covers the basics of setting up a dev environment and basic hello world style Go program for the x64 architecture. + +-> Click [HERE](https://github.com/mytechnotalent/Go-Hacking/blob/main/Go%20Hacking.pdf) to read the FREE pdf book. + +
+ +# Embedded Hacking +A FREE comprehensive step-by-step embedded hacking course covering Embedded Software Development to Reverse Engineering. + +VIDEO PROMO [HERE](https://www.youtube.com/watch?v=aD7X9sXirF8) + +
+ +# FREE Book [Download](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) + +
+ +# Required Skills +Students must have a working understanding of the following items: + +- **Intermediate C Programming:** In particular, you must understand how pointers reference memory addresses which we will use to perform live variable hijacking. + - Training Link [HERE](https://www.learn-c.org) +- **Bare-Metal Embedded Systems Assembly Basics:** You must have experience with bare-metal assembly as we will be using this extensively throughout the entire course specifically with GDB and Ghidra. + - Training Link [HERE](https://azeria-labs.com/writing-arm-assembly-part-1) +- **Computer Architecture Basics (Registers & Stack):** You need a working mental model of how a CPU/MCU uses registers and the stack to follow the boot process and function calls. + - Training Link [HERE](https://www.geeksforgeeks.org/computer-science-fundamentals/what-is-register-memory) + - Training Link [HERE](https://www.geeksforgeeks.org/computer-organization-architecture/memory-stack-organization-in-computer-architecture) +- **Command Line Interface (CLI) Proficiency:** You must be comfortable navigating directories and executing commands in a terminal to run the necessary OpenOCD and GDB tools. + - Training Link [HERE](https://www.freecodecamp.org/news/command-line-commands-cli-tutorial) +- **Breadboarding Proficiency:** You must be comfortable demonstrating breadboard proficiency by efficiently prototyping circuits and integrating diverse peripherals, sensors, and microcontrollers. + - Training Video [HERE](https://www.youtube.com/watch?v=fq6U5Y14oM4) + +
+ +# Hardware [View Full Parts List](PARTS.md) +## Raspberry Pi Pico 2 w/ Header [BUY](https://www.pishop.us/product/raspberry-pi-pico-2-with-header) +## USB A-Male to USB Micro-B Cable [BUY](https://www.pishop.us/product/usb-a-male-to-usb-micro-b-cable-6-inches) +## Raspberry Pi Pico Debug Probe [BUY](https://www.pishop.us/product/raspberry-pi-debug-probe) +## Complete Component Kit for Raspberry Pi [BUY](https://www.pishop.us/product/complete-component-kit-for-raspberry-pi) +## 10pc 25v 1000uF Capacitor [BUY](https://www.amazon.com/Cionyce-Capacitor-Electrolytic-CapacitorsMicrowave/dp/B0B63CCQ2N?th=1) +### 10% PiShop DISCOUNT CODE - KVPE_HS320548_10PC + +

+ +# Breadboard Design +![image](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/EHP2_bb.png?raw=true) + +
+ +# Syllabus + +## Week 1 +Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts + +### Week 1 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK01/WEEK01-SLIDES.pdf) + +### Week 1 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK01/WEEK01.md) + + +### Chapter 1: hello, world +This chapter covers the basics of setting up a dev environment and basic template firmware for the Pico 2 MCU in addition to printing hello, world. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 2: Debugging hello, world +This chapter covers the debugging of our firmware for the Pico 2 MCU hello, world program. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 2 +Hello, World - Debugging and Hacking Basics: Debugging and Hacking a Basic Program for the Pico 2 + +### Week 2 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK02/WEEK02-SLIDES.pdf) + +### Week 2 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK02/WEEK02.md) + + +### Chapter 3: Hacking hello, world +This chapter covers the hacking of our firmware for the Pico 2 MCU hello, world program. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 3 +Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis + +### Week 3 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK03/WEEK03-SLIDES.pdf) + +### Week 3 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK03/WEEK03.md) + + +### Chapter 4: Embedded System Analysis +This chapter covers a comprehensive embedded system analysis reviewing parts of the RP2350 datasheet and helpful firmware analysis tools. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 4 +Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics + +### Week 4 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK04/WEEK04-SLIDES.pdf) + +### Week 4 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK04/WEEK04.md) + + +### Chapter 5: Intro To Variables +This chapter covers an introduction to variables as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 6: Debugging Intro To Variables +This chapter covers debugging an introduction to variables as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 7: Hacking Intro To Variables +This chapter covers hacking an introduction to variables as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 8: Uninitialized Variables +This chapter covers uninitialized variables as well as an intro to GPIO outputs as we blink an LED as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 9: Debugging Uninitialized Variables +This chapter covers debugging uninitialized variables as well as an intro to GPIO outputs as we blink an LED as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 10: Hacking Uninitialized Variables +This chapter covers hacking uninitialized variables as well as an intro to GPIO outputs as we blink an LED as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 5 +Integers and Floats in Embedded Systems: Debugging and Hacking Integers and Floats w/ Intermediate GPIO Output Assembler Analysis + +### Week 5 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK05/WEEK05-SLIDES.pdf) + +### Week 5 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK05/WEEK05.md) + + +### Chapter 11: Integer Data Type +This chapter covers the integer data type in addition to a deeper assembler dive into GPIO outputs as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 12: Debugging Integer Data Type +This chapter covers debugging the integer data type in addition to a deeper assembler dive into GPIO outputs as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 13: Hacking Integer Data Type +This chapter covers hacking the integer data type in addition to a deeper assembler dive into GPIO outputs as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 14: Floating-Point Data Type +This chapter covers the floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 15: Debugging Floating-Point Data Type +This chapter covers debugging the floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 16: Hacking Floating-Point Data Type +This chapter covers hacking the floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 17: Double Floating-Point Data Type +This chapter covers the double floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 18: Debugging Double Floating-Point Data Type +This chapter covers debugging the double floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 19: Hacking Double Floating-Point Data Type +This chapter covers hacking the double floating-point data type as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 6 +Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics + +### Week 6 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK06/WEEK06-SLIDES.pdf) + +### Week 6 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK06/WEEK06.md) + + +### Chapter 20: Static Variables +This chapter covers static variables as well as an intro to GPIO inputs as we work with push buttons as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 21: Debugging Static Variables +This chapter covers debugging static variables as well as an intro to GPIO inputs as we work with push buttons as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 22: Hacking Static Variables +This chapter covers hacking static variables as well as an intro to GPIO inputs as we work with push buttons as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 7 +Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics + +### Week 7 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK07/WEEK07-SLIDES.pdf) + +### Week 7 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK07/WEEK07.md) + + +### Chapter 23: Constants +This chapter covers constants as well as an intro to I2C as we work a 1602 LCD as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 24: Debugging Constants +This chapter covers debugging constants as well as an intro to I2C as we work a 1602 LCD as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 25: Hacking Constants +This chapter covers hacking constants as well as an intro to I2C as we work a 1602 LCD as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 8 +### Midterm Exam + +## Week 9 +Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Temperature & Humidity Sensor Single-Wire Protocol Basics + +### Week 9 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK09/WEEK09-SLIDES.pdf) + +### Week 9 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK09/WEEK09.md) + + +### Chapter 26: Operators +This chapter covers operators as well as an intro to single-wire protocol as we work a DHT11 temperature and humidity sensor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 27: Debugging Operators +This chapter covers debugging operators as well as an intro to single-wire protocol as we work a DHT11 temperature and humidity sensor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 28: Hacking Operators +This chapter covers hacking operators as well as an intro to single-wire protocol as we work a DHT11 temperature and humidity sensor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 10 +Conditionals in Embedded Systems: Debugging and Hacking Static & Dynamic Conditionals w/ SG90 Servo Motor PWM Basics + +### Week 10 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK10/WEEK10-SLIDES.pdf) + +### Week 10 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK10/WEEK10.md) + + +### Chapter 29: Static Conditionals +This chapter covers static conditionals as well as an intro to PWM as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 30: Debugging Static Conditionals +This chapter covers debugging static conditionals as well as an intro to PWM as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 31: Hacking Static Conditionals +This chapter covers hacking static conditionals as well as an intro to PWM as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 32: Dynamic Conditionals +This chapter covers dynamic conditionals as well as additional PWM examples as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 33: Debugging Dynamic Conditionals +This chapter covers debugging dynamic conditionals as well as additional PWM examples as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 34: Hacking Dynamic Conditionals +This chapter covers hacking dynamic conditionals as well as additional PWM examples as we work a SG90 servo motor as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 11 +Structures and Functions in Embedded Systems: Debugging and Hacking w/ IR Remote Control and NEC Protocol Basics + +### Week 11 Slides [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK11/WEEK11-SLIDES.pdf) + +### Week 11 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK11/WEEK11.md) + + +### Chapter 35: Structures +This chapter covers structures as well as an intro to infrared basics as we work a infrared receiver and infrared remote controller as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 36: Debugging Structures +This chapter covers debugging structures as well as an intro to infrared basics as we work a infrared receiver and infrared remote controller as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 37: Hacking Structures +This chapter covers hacking structures as well as an intro to infrared basics as we work a infrared receiver and infrared remote controller as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 38: Functions, w/ Param, w/ Return +This chapter covers functions, w/ params and w/ a return value as well as additional infrared examples as we work a infrared receiver and infrared remote controller it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 39: Debugging Functions, w/ Param, w/ Return +This chapter covers debugging functions, w/ params and w/ a return value as well as additional infrared examples as we work a infrared receiver and infrared remote controller as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +### Chapter 40: Hacking Functions, w/ Param, w/ Return +This chapter covers hacking functions, w/ params and w/ a return value as it relates to embedded development on the Pico 2. + +-> Click [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/Embedded-Hacking.pdf) to read the FREE pdf book. + +## Week 12 +Unknown Firmware Debugging and Hacking + +## Week 13 +Final Review – Embedded Debugging and Hacking Techniques w/ Advanced Firmware Analysis Q&A + +## Week 14 +### Final +### Final Project Option 1: The InfuSafe Pro Incident +In the aftermath of a catastrophic medical device failure, you are thrust into the role of an FDA forensic investigator facing an impossible crisis: 23 patients dead, 100 million recalled insulin pumps sitting in warehouses worldwide, and 2.3 million lives hanging in the balance all while the only evidence remaining is raw binary firmware after a rogue engineer destroyed every line of source code before fleeing to Montenegro. Armed only with GDB, Ghidra, and the reverse engineering skills honed over the first seven weeks of this course, you must excavate the truth from machine code, identify the lethal bugs spawned by an AI code generator called "OopsieGPT," and determine whether these devices can be salvaged to save millions in underserved communities or if $4.7 billion in humanitarian medical technology must be incinerated. This is not a simulation; this is triage at the intersection of embedded systems security and human survival. + +#### Final Project Option 1 Requirements & Grading Criteria [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/FINAL/FINAL-01.md) + +### Final Project Option 2: Operation Dark Eclipse +Forty-two stories beneath frozen tundra, a shadow intelligence alliance called Dark Eyes operates centrifuges enriching weapons-grade material for a first strike against Washington, D.C. and Agent NIGHTINGALE gave her life to extract the single firmware file that now sits before you. Conventional warfare cannot reach this fortress buried beneath rock and concrete, but you can: as the architect of a precision cyber weapon in the tradition of Stuxnet, you must reverse engineer the RP2350-based centrifuge controller, craft binary patches that double the spin speed while falsifying every sensor readout to show nominal operation, and execute the sabotage that will cascade-destroy their enrichment program and set their nuclear ambitions back a decade. Every skill from the entire semester ARM assembly, Ghidra analysis, IEEE-754 floating-point manipulation, branch modification, log desynchronization converges in this final mission. Agent NIGHTINGALE's seven-year-old daughter still watches the driveway, waiting for a mother who will never return. Honor that sacrifice. Complete the mission. Do not fail. + +#### Final Project Option 2 Requirements & Grading Criteria [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/FINAL/FINAL-02.md) + +
+ +# C Bare-Metal Drivers +Register-level C drivers for the RP2350 with no SDK dependencies. Each driver folder uses a two-layer architecture: `rp2350.h` (peripheral register structs) and per-peripheral `rp2350_.h/.c` driver pairs. Built with `arm-none-eabi-gcc`, linked with a custom linker script, and flashed via UF2. + +| Driver | Description | Key Peripheral | +| ------------------------------------------------ | ------------------------------------------ | -------------- | +| [0x01_uart_cbm](drivers/0x01_uart_cbm) | Raw UART transmit / receive | UART0 | +| [0x02_blink_cbm](drivers/0x02_blink_cbm) | GPIO output LED blink | SIO, IO_BANK0 | +| [0x03_button_cbm](drivers/0x03_button_cbm) | GPIO input with debounce | SIO, IO_BANK0 | +| [0x04_pwm_cbm](drivers/0x04_pwm_cbm) | PWM output with frequency & duty control | PWM | +| [0x05_servo_cbm](drivers/0x05_servo_cbm) | SG90 servo angle & pulse control | PWM | +| [0x06_adc_cbm](drivers/0x06_adc_cbm) | ADC voltage & on-chip temperature reading | ADC | +| [0x07_i2c_cbm](drivers/0x07_i2c_cbm) | I2C bus init, probe & scan | I2C0 | +| [0x08_lcd1602_cbm](drivers/0x08_lcd1602_cbm) | 1602 LCD over I2C (PCF8574 backpack) | I2C0 | +| [0x09_dht11_cbm](drivers/0x09_dht11_cbm) | DHT11 temperature & humidity (single-wire) | SIO, IO_BANK0 | +| [0x0a_ir_cbm](drivers/0x0a_ir_cbm) | IR remote NEC protocol decoder | SIO, IO_BANK0 | +| [0x0b_spi_cbm](drivers/0x0b_spi_cbm) | SPI bus init & bidirectional transfer | SPI0 | +| [0x0c_multicore_cbm](drivers/0x0c_multicore_cbm) | Dual-core launch & FIFO messaging | SIO FIFO, PSM | +| [0x0d_timer_cbm](drivers/0x0d_timer_cbm) | Repeating timer alarm callbacks | TIMER0, TICKS | +| [0x0e_watchdog_cbm](drivers/0x0e_watchdog_cbm) | Watchdog enable, feed & reboot detection | WATCHDOG, PSM | +| [0x0f_flash_cbm](drivers/0x0f_flash_cbm) | On-chip flash erase, program & XIP read | ROM, XIP | + +
+ +# C Drivers +Self-contained C driver libraries for the Raspberry Pi Pico 2 (RP2350). Each driver folder contains a thin demo (`0xNN_name.c`), a reusable library implementation (`name.c`), and a library header with full Doxygen docstrings (`name.h`). + +| Driver | Description | Key Hardware | +| ---------------------------------------- | ------------------------------------------ | --------------------------------- | +| [0x01_uart](drivers/0x01_uart) | Raw UART transmit / receive | `hardware_uart` | +| [0x02_blink](drivers/0x02_blink) | GPIO output LED blink | `pico_stdlib` | +| [0x03_button](drivers/0x03_button) | GPIO input with debounce | `pico_stdlib` | +| [0x04_pwm](drivers/0x04_pwm) | PWM output with frequency & duty control | `hardware_pwm`, `hardware_clocks` | +| [0x05_servo](drivers/0x05_servo) | SG90 servo angle & pulse control | `hardware_pwm`, `hardware_clocks` | +| [0x06_adc](drivers/0x06_adc) | ADC voltage & on-chip temperature reading | `hardware_adc` | +| [0x07_i2c](drivers/0x07_i2c) | I2C bus init, probe & scan | `hardware_i2c` | +| [0x08_lcd1602](drivers/0x08_lcd1602) | 1602 LCD over I2C (PCF8574 backpack) | `hardware_i2c` | +| [0x09_dht11](drivers/0x09_dht11) | DHT11 temperature & humidity (single-wire) | `pico_stdlib` | +| [0x0a_ir](drivers/0x0a_ir) | IR remote NEC protocol decoder | `pico_stdlib` | +| [0x0b_spi](drivers/0x0b_spi) | SPI bus init & bidirectional transfer | `hardware_spi` | +| [0x0c_multicore](drivers/0x0c_multicore) | Dual-core launch & FIFO messaging | `pico_multicore` | +| [0x0d_timer](drivers/0x0d_timer) | Repeating timer callbacks | `pico_stdlib` | +| [0x0e_watchdog](drivers/0x0e_watchdog) | Watchdog enable, feed & reboot detection | `hardware_watchdog` | +| [0x0f_flash](drivers/0x0f_flash) | On-board flash erase, write & read | `hardware_flash`, `hardware_sync` | + +
+ +# Rust Drivers +Rust ports of the C drivers above using `rp235x-hal`. Each Rust driver folder contains a thin demo (`main.rs`) and a reusable library module (`driver.rs`) with full Rust doc comments. + +### Testing + +```bash +# Run unit tests on the host +cargo test --lib --target x86_64-pc-windows-msvc +``` + +| Driver | Description | Key Crates | +| -------------------------------------------------- | ------------------------------------------ | ------------------------------- | +| [0x01_uart_rust](drivers/0x01_uart_rust) | Raw UART transmit / receive | `rp235x-hal`, `embedded-hal-nb` | +| [0x02_blink_rust](drivers/0x02_blink_rust) | GPIO output LED blink | `rp235x-hal`, `embedded-hal` | +| [0x03_button_rust](drivers/0x03_button_rust) | Push-button input with debounce | `rp235x-hal`, `embedded-hal` | +| [0x04_pwm_rust](drivers/0x04_pwm_rust) | Generic PWM output | `rp235x-hal`, `embedded-hal` | +| [0x05_servo_rust](drivers/0x05_servo_rust) | SG90 servo motor control | `rp235x-hal`, `embedded-hal` | +| [0x06_adc_rust](drivers/0x06_adc_rust) | 12-bit ADC voltage + temperature | `rp235x-hal`, `embedded-hal` | +| [0x07_i2c_rust](drivers/0x07_i2c_rust) | I2C bus scanner (7-bit addresses) | `rp235x-hal`, `embedded-hal` | +| [0x08_lcd1602_rust](drivers/0x08_lcd1602_rust) | HD44780 16x2 LCD (PCF8574 I2C) | `rp235x-hal`, `embedded-hal` | +| [0x09_dht11_rust](drivers/0x09_dht11_rust) | DHT11 temperature & humidity (single-wire) | `rp235x-hal`, `fugit` | +| [0x0a_ir_rust](drivers/0x0a_ir_rust) | IR remote NEC protocol decoder | `rp235x-hal`, `fugit` | +| [0x0b_spi_rust](drivers/0x0b_spi_rust) | SPI bus init & bidirectional transfer | `rp235x-hal`, `embedded-hal` | +| [0x0c_multicore_rust](drivers/0x0c_multicore_rust) | Dual-core launch & FIFO messaging | `rp235x-hal` | +| [0x0d_timer_rust](drivers/0x0d_timer_rust) | Repeating timer callbacks | `rp235x-hal`, `fugit` | +| [0x0e_watchdog_rust](drivers/0x0e_watchdog_rust) | Watchdog enable, feed & reboot detection | `rp235x-hal` | +| [0x0f_flash_rust](drivers/0x0f_flash_rust) | On-board flash erase, write & read | `rp235x-hal` | + +
+ +# License +[Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) diff --git a/WEEK01/WEEK01-SLIDES.pdf b/WEEK01/WEEK01-SLIDES.pdf new file mode 100644 index 0000000..5fb1421 Binary files /dev/null and b/WEEK01/WEEK01-SLIDES.pdf differ diff --git a/WEEK01/WEEK01.md b/WEEK01/WEEK01.md new file mode 100644 index 0000000..893bfe6 --- /dev/null +++ b/WEEK01/WEEK01.md @@ -0,0 +1,727 @@ +# Week 1: Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts + +## What You'll Learn This Week + +By the end of this week, you will be able to: +- Understand what a microcontroller is and how it works +- Know the basic registers of the ARM Cortex-M33 processor +- Understand memory layout (Flash vs RAM) and why it matters +- Understand how the stack works in embedded systems +- Set up and connect GDB to your Pico 2 for debugging +- Use Ghidra for static analysis of your binary +- Read basic ARM assembly instructions and understand what they do + +--- + +## Part 1: Understanding the Basics + +### What is a Microcontroller? + +Think of a microcontroller as a tiny computer on a single chip. Just like your laptop has a processor, memory, and storage, a microcontroller has all of these packed into one small chip. The **RP2350** is the microcontroller chip that powers the **Raspberry Pi Pico 2**. + +### What is the ARM Cortex-M33? + +The RP2350 has two "brains" inside it - we call these **cores**. One brain uses ARM Cortex-M33 instructions, and the other can use RISC-V instructions. In this course, we'll focus on the **ARM Cortex-M33** core because it's more commonly used in the industry. + +### What is Reverse Engineering? + +Reverse engineering is like being a detective for code. Instead of writing code and compiling it, we take compiled code (the 1s and 0s that the computer actually runs) and figure out what it does. This is useful for: +- Understanding how things work +- Finding bugs or security issues +- Learning how software interacts with hardware + +--- + +## Part 2: Understanding Processor Registers + +### What is a Register? + +A **register** is like a tiny, super-fast storage box inside the processor. The processor uses registers to hold numbers while it's doing calculations. Think of them like the short-term memory your brain uses when doing math in your head. + +### The ARM Cortex-M33 Registers + +The ARM Cortex-M33 has several important registers: + +| Register | Also Called | Purpose | +| ------------ | -------------------- | ------------------------------------------- | +| `r0` - `r12` | General Purpose | Store numbers, pass data between functions | +| `r13` | SP (Stack Pointer) | Keeps track of where we are in the stack | +| `r14` | LR (Link Register) | Remembers where to go back after a function | +| `r15` | PC (Program Counter) | Points to the next instruction to run | + +##### General Purpose Registers (`r0` - `r12`) + +These 13 registers are your "scratch paper." When the processor needs to add two numbers, subtract, or do any calculation, it uses these registers to hold the values. + +**Example:** If you want to add 5 + 3: +1. Put 5 in `r0` +2. Put 3 in `r1` +3. Add them and store the result (8) in `r2` + +##### The Stack Pointer (`r13` / SP) + +The **stack** is a special area of memory that works like a stack of plates: +- When you add something, you put it on top (called a **PUSH**) +- When you remove something, you take it from the top (called a **POP**) + +The Stack Pointer always points to the top of this stack. On ARM systems, the stack **grows downward** in memory. This means when you push something onto the stack, the address number gets smaller! + +The two Arm ABI documents we verified give the formal proof for these rules. In AAPCS32, page 17 defines the core register roles used by the base procedure call standard: `r13` is `SP`, `r14` is `LR`, `r15` is `PC`, `r0`-`r3` are argument and scratch registers, and `r4`-`r11` are the longer-lived variable registers. In Advisory Note 132, page 7 states that `SP` must be aligned to a multiple of 8 at every conforming call site and must already be 8-byte aligned when control first enters conforming code. That is why compiler-generated prologues often push an even number of registers, such as `push {r3, lr}`, to preserve both saved state and the required ABI stack alignment. + +``` +Higher Memory Address (0x20082000) ++------------------+ +| | ↠Stack starts here (empty) ++------------------+ +| Pushed Item 1 | ↠SP points here after 1 push ++------------------+ +| Pushed Item 2 | ↠SP points here after 2 pushes ++------------------+ +Lower Memory Address (0x20081FF8) +``` + +##### The Link Register (`r14` / LR) + +When you call a function, the processor needs to remember where to come back to. The Link Register stores this "return address." + +**Example:** +``` +main() calls print_hello() + ↓ +LR = address right after the call in main() + ↓ +print_hello() runs + ↓ +print_hello() finishes, looks at LR + ↓ +Jumps back to main() at the address stored in LR +``` + +##### The Program Counter (`r15` / PC) + +The Program Counter always points to the **next instruction** the processor will execute. It's like your finger following along as you read a book - it always points to where you are. + +--- + +## Part 3: Understanding Memory Layout + +### XIP - Execute In Place + +The RP2350 uses something called **XIP (Execute In Place)**. This means the processor can run code directly from the flash memory (where your program is stored) without copying it to RAM first. + +**Key Memory Address:** `0x10000000` + +This is where your program code starts in flash memory. Remember this address - we'll use it a lot! + +### Memory Map Overview + +``` ++-------------------------------------+ +| Flash Memory (XIP) | +| Starts at: 0x10000000 | +| Contains: Your program code | ++-------------------------------------+ +| RAM | +| Starts at: 0x20000000 | +| Contains: Stack, Heap, Variables | ++-------------------------------------+ +``` + +### Stack vs Heap + +| Stack | Heap | +| ---------------------------------------- | ---------------------------------- | +| Automatic memory management | Manual memory management | +| Fast | Slower | +| Limited size | More flexible size | +| Used for function calls, local variables | Used for dynamic memory allocation | +| Grows downward | Grows upward | + +--- + +## Part 3.5: Reviewing Our Hello World Code + +Before we start debugging, let's understand the code we'll be working with. Here's our `0x0001_hello-world.c` program: + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + + while (true) + printf("hello, world\r\n"); +} +``` + +### Breaking Down the Code + +##### The Includes + +```c +#include +#include "pico/stdlib.h" +``` + +- **``** - This is the standard input/output library. It gives us access to the `printf()` function that lets us print text. +- **`"pico/stdlib.h"`** - This is the Pico SDK's standard library. It provides essential functions for working with the Raspberry Pi Pico hardware. + +##### The Main Function + +```c +int main(void) { +``` + +Every C program starts running from the `main()` function. The `void` means it takes no arguments, and `int` means it returns an integer (though our program never actually returns). + +##### Initializing Standard I/O + +```c +stdio_init_all(); +``` + +This function initializes all the standard I/O (input/output) for the Pico. It sets up: +- **USB CDC** (so you can see output when connected to a computer via USB) +- **UART** (serial communication pins) + +Without this line, `printf()` wouldn't have anywhere to send its output! + +##### The Infinite Loop + +```c +while (true) + printf("hello, world\r\n"); +``` + +- **`while (true)`** - This creates an infinite loop. The program will keep running forever (or until you reset/power off the Pico). +- **`printf("hello, world\r\n")`** - This prints the text "hello, world" followed by a carriage return (`\r`) and newline (`\n`). + +> Tip: **Why `\r\n` instead of just `\n`?** +> +> In embedded systems, we often use both carriage return (`\r`) and newline (`\n`) together. The `\r` moves the cursor back to the beginning of the line, and `\n` moves to the next line. This ensures proper display across different terminal programs. + +### What Happens When This Runs? + +1. **Power on** - The Pico boots up and starts executing code from flash memory +2. **`stdio_init_all()`** - Sets up USB and/or UART for communication +3. **Infinite loop begins** - The program enters the `while(true)` loop +4. **Print forever** - "hello, world" is sent over and over as fast as possible + +### Why This Code is Perfect for Learning + +This simple program is ideal for reverse engineering practice because: +- It has a clear, recognizable function call (`printf`) +- It has an infinite loop we can observe +- It's small enough to understand completely +- It demonstrates real hardware interaction (USB/UART output) + +When we debug this code, we'll be able to see how the C code translates to ARM assembly instructions! + +### Compiling and Flashing to the Pico 2 + +Now that we understand the code, let's get it running on our hardware: + +##### Step 1: Compile the Code + +In VS Code, look for the **Compile** button in the status bar at the bottom of the window. This is provided by the Raspberry Pi Pico extension. Click it to compile your project. + +The extension will run CMake and build your code, creating a `.uf2` file that can be loaded onto the Pico 2. + +##### Step 2: Put the Pico 2 in Flash Loading Mode + +To flash new code to your Pico 2, you need to put it into **BOOTSEL mode**: + +1. **Press and hold** the right-most button on your breadboard (the BOOTSEL button) +2. **While holding BOOTSEL**, press the white **Reset** button +3. **Release the Reset button** first +4. **Then release the BOOTSEL button** + +When done correctly, your Pico 2 will appear as a USB mass storage device (like a flash drive) on your computer. This means it's ready to receive new firmware! + +> Tip: **Tip:** You'll see a drive called "RP2350" appear in your file explorer when the Pico 2 is in flash loading mode. + +##### Step 3: Flash and Run + +Back in VS Code, click the **Run** button in the status bar. The extension will: +1. Copy the compiled `.uf2` file to the Pico 2 +2. The Pico 2 will automatically reboot and start running your code + +Once flashed, your Pico 2 will immediately start executing the hello-world program, printing "hello, world" continuously when we open PuTTY! + +--- + +## Part 4: Dynamic Analysis with GDB + +### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board +2. GDB (GNU Debugger) installed +3. OpenOCD or another debug probe connection +4. The sample "hello-world" binary loaded on your Pico 2 + +### Connecting to Your Pico 2 with OpenOCD + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +### Connecting to Your Pico 2 with GDB + +Open another terminal and start GDB with your binary: + +```cmd +arm-none-eabi-gdb build\0x0001_hello-world.elf +``` + +Connect to your target: + +```cmd +(gdb) target extended-remote :3333 +(gdb) monitor reset halt +``` + +### Basic GDB Commands: Your First Steps + +Now that we're connected, let's learn three essential GDB commands that you'll use constantly in embedded reverse engineering. + +##### Setting a Breakpoint with `b main` + +A **breakpoint** tells the debugger to pause execution when it reaches a specific point. Let's set one at our `main` function: + +```gdb +(gdb) b main +Breakpoint 1 at 0x10000234: file ../0x0001_hello-world.c, line 5. +``` + +**What this tells us:** +- GDB found our `main` function +- It's located at address `0x10000234` in flash memory +- The source file and line number are shown (because we have debug symbols) + +Now let's run to that breakpoint: + +```gdb +(gdb) c +Continuing. + +Breakpoint 1, main () at ../0x0001_hello-world.c:5 +5 stdio_init_all(); +``` + +The program has stopped right at the beginning of `main`! + +##### Disassembling with `disas` + +The `disas` (disassemble) command shows us the assembly instructions for the current function: + +```gdb +(gdb) disas +Dump of assembler code for function main: +=> 0x10000234 <+0>: push {r3, lr} + 0x10000236 <+2>: bl 0x1000156c + 0x1000023a <+6>: ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c <+8>: bl 0x100015fc <__wrap_puts> + 0x10000240 <+12>: b.n 0x1000023a + 0x10000242 <+14>: nop + 0x10000244 <+16>: adds r4, r1, r7 + 0x10000246 <+18>: asrs r0, r0, #32 +End of assembler dump. +``` + +**Understanding the output:** +- The `=>` arrow shows where we're currently stopped +- Each line shows: `address : instruction operands` +- We can see the calls to `stdio_init_all` and `__wrap_puts` (printf was optimized to puts) +- The `b.n 0x1000023a` at the end is our infinite loop - it jumps back to reload the string! + +##### Viewing ELF Sections with `info files` and `maintenance info sections` + +To see how the ELF is laid out in memory, use: + +```gdb +(gdb) info files +Symbols from "C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf". +Extended remote target using gdb-specific protocol: + `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm. + Entry point: 0x1000014c + 0x10000000 - 0x100019cc is .text + 0x100019cc - 0x10001b18 is .rodata + 0x10001b18 - 0x10001b20 is .ARM.exidx + 0x10001b20 - 0x10001b4c is .binary_info + 0x20000000 - 0x20000110 is .ram_vector_table + 0x20000110 - 0x200002ac is .data + 0x200002ac - 0x200002ac is .tdata + 0x200002ac - 0x200002ac is .tbss + 0x200002b0 - 0x200004d8 is .bss + 0x200004d8 - 0x20000cd8 is .heap + 0x20081000 - 0x20081800 is .stack_dummy + 0x10001ce8 - 0x10001cfc is .flash_end + While running this, GDB does not access memory from... +Local exec file: + `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm. + Entry point: 0x1000014c + 0x10000000 - 0x100019cc is .text + 0x100019cc - 0x10001b18 is .rodata + 0x10001b18 - 0x10001b20 is .ARM.exidx + 0x10001b20 - 0x10001b4c is .binary_info + 0x20000000 - 0x20000110 is .ram_vector_table + 0x20000110 - 0x200002ac is .data + 0x200002ac - 0x200002ac is .tdata + 0x200002ac - 0x200002ac is .tbss + 0x200002b0 - 0x200004d8 is .bss + 0x200004d8 - 0x20000cd8 is .heap + 0x20081000 - 0x20081800 is .stack_dummy + 0x10001ce8 - 0x10001cfc is .flash_end +(gdb) maintenance info sections +Exec file: `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm. + [0] 0x10000000->0x100019cc at 0x00001000: .text ALLOC LOAD READONLY CODE HAS_CONTENTS + [1] 0x100019cc->0x10001b18 at 0x000029cc: .rodata ALLOC LOAD READONLY DATA HAS_CONTENTS + [2] 0x10001b18->0x10001b20 at 0x00002b18: .ARM.exidx ALLOC LOAD READONLY DATA HAS_CONTENTS + [3] 0x10001b20->0x10001b4c at 0x00002b20: .binary_info ALLOC LOAD READONLY DATA HAS_CONTENTS + [4] 0x20000000->0x20000110 at 0x00004000: .ram_vector_table ALLOC + [5] 0x20000110->0x20000110 at 0x00003cfc: .uninitialized_data HAS_CONTENTS + [6] 0x20000110->0x200002ac at 0x00003110: .data ALLOC LOAD READONLY CODE HAS_CONTENTS + [7] 0x200002ac->0x200002ac at 0x00003cfc: .tdata ALLOC LOAD DATA HAS_CONTENTS + [8] 0x200002ac->0x200002ac at 0x00000000: .tbss ALLOC + [9] 0x200002b0->0x200004d8 at 0x000042b0: .bss ALLOC + [10] 0x200004d8->0x20000cd8 at 0x000044d8: .heap ALLOC READONLY + [11] 0x20080000->0x20080000 at 0x00003cfc: .scratch_x HAS_CONTENTS + [12] 0x20081000->0x20081000 at 0x00003cfc: .scratch_y HAS_CONTENTS + [13] 0x20081000->0x20081800 at 0x00004000: .stack_dummy ALLOC READONLY + [14] 0x10001ce8->0x10001cfc at 0x00003ce8: .flash_end ALLOC LOAD READONLY DATA HAS_CONTENTS + [15] 0x0000->0x0034 at 0x00003cfc: .ARM.attributes READONLY HAS_CONTENTS + [16] 0x0000->0x0045 at 0x00003d30: .comment READONLY HAS_CONTENTS + [17] 0x0000->0x2069a at 0x00003d75: .debug_info READONLY HAS_CONTENTS + [18] 0x0000->0x54ff at 0x0002440f: .debug_abbrev READONLY HAS_CONTENTS + [19] 0x0000->0x0af0 at 0x00029910: .debug_aranges READONLY HAS_CONTENTS + [20] 0x0000->0x2f86 at 0x0002a400: .debug_rnglists READONLY HAS_CONTENTS + [21] 0x0000->0x15526 at 0x0002d386: .debug_line READONLY HAS_CONTENTS + [22] 0x0000->0x56a7 at 0x000428ac: .debug_str READONLY HAS_CONTENTS + [23] 0x0000->0x1ed4 at 0x00047f54: .debug_frame READONLY HAS_CONTENTS + [24] 0x0000->0xffd1 at 0x00049e28: .debug_loclists READONLY HAS_CONTENTS + [25] 0x0000->0x0178 at 0x00059df9: .debug_line_str READONLY HAS_CONTENTS +``` + +**What each section means:** + +| Section | Purpose | +| ------- | ------- | +| `.text` | Executable machine code (your functions/instructions). | +| `.rodata` | Read-only constants (strings like `"hello, world"`, lookup tables, const data). | +| `.ARM.exidx` | ARM exception unwind index used for stack unwinding/backtraces. | +| `.binary_info` | Pico metadata used by tools (program identity/build information). | +| `.ram_vector_table` | Interrupt vector table copied/placed in RAM for runtime use. | +| `.uninitialized_data` | Deliberately non-zeroed RAM region that can survive certain reset paths. | +| `.data` | Initialized global/static variables in RAM (initial values come from flash). | +| `.tdata` | Initialized thread-local storage data (often empty in simple bare-metal apps). | +| `.tbss` | Zero-initialized thread-local storage (often empty). | +| `.bss` | Zero-initialized global/static variables in RAM. | +| `.heap` | Heap allocation region (`malloc/new`) reserved in RAM. | +| `.scratch_x` | RP2 scratch RAM bank X section (core-local/low-contention placement). | +| `.scratch_y` | RP2 scratch RAM bank Y section (core-local/low-contention placement). | +| `.stack_dummy` | Linker-reserved stack range marker used to size/place the stack. | +| `.flash_end` | Marker/metadata near the logical end of flash image region. | +| `.ARM.attributes` | ARM build attributes (ABI/architecture metadata for tools/linkers). | +| `.comment` | Compiler/build comment strings (toolchain identification). | +| `.debug_info` | Main DWARF debug database (types, variables, symbols, scopes). | +| `.debug_abbrev` | Abbreviation table referenced by `.debug_info`. | +| `.debug_aranges` | Address-to-compilation-unit lookup acceleration data. | +| `.debug_rnglists` | DWARF range lists for non-contiguous code/data ranges. | +| `.debug_line` | Address-to-source-line mapping used for stepping/breakpoints. | +| `.debug_str` | Shared string pool used by DWARF debug sections. | +| `.debug_frame` | Call frame information used for unwinding stack frames. | +| `.debug_loclists` | Variable location lists (where variables live over PC ranges). | +| `.debug_line_str` | Extra string pool used by `.debug_line` data. | + +> Tip: **Practical rule:** For reverse engineering runtime behavior, focus first on `.text`, `.rodata`, `.data`, `.bss`, heap/stack regions, and the vector table. Debug sections are for source-level mapping and symbol intelligence. + +**Fast interpretation checklist (use this every time):** + +1. **Find where code executes**: Verify `.text` starts at `0x10000000` (XIP flash) and note its end. +2. **Find immutable constants**: Use `.rodata` for strings/tables; cross-reference these addresses in disassembly. +3. **Find initialized RAM state**: `.data` lives in RAM at runtime, but its initial bytes come from flash. +4. **Find zeroed runtime state**: `.bss` is RAM that startup code clears to zero before `main`. +5. **Find interrupt control point**: Confirm `.ram_vector_table` location for exception/IRQ handler mapping. +6. **Bound dynamic memory**: Note `.heap` range so you can classify allocator activity vs static data. +7. **Bound call-stack activity**: Use `.stack_dummy` as linker stack reservation, then track live stack with `$sp`. +8. **Separate runtime vs debug-only sections**: `.debug_*`, `.comment`, and `.ARM.attributes` help tooling, not execution. +9. **Correlate any suspicious address quickly**: Flash/XIP (`0x100...`) usually code/const; SRAM (`0x200...`) usually data/stack/heap. +10. **Validate in memory**: After identifying a section, inspect it with `x` in GDB to confirm actual bytes/instructions. + +##### Viewing Registers with `i r` + +The `i r` (info registers) command shows the current state of all CPU registers: + +```gdb +(gdb) i r +r0 0x0 0 +r1 0x10000235 268436021 +r2 0x80808080 -2139062144 +r3 0xe000ed08 -536810232 +r4 0x100001d0 268435920 +r5 0x88526891 -2007865199 +r6 0x4f54710 83183376 +r7 0x400e0014 1074659348 +r8 0x43280035 1126694965 +r9 0x0 0 +r10 0x10000000 268435456 +r11 0x62707361 1651536737 +r12 0xed07f600 -318245376 +sp 0x20082000 0x20082000 +lr 0x1000018f 268435855 +pc 0x10000234 0x10000234
+xpsr 0x69000000 1761607680 +``` + +**Key registers to watch:** +| Register | Value | Meaning | +| -------- | ------------ | ----------------------------------------------- | +| `pc` | `0x10000234` | Program Counter - we're at the start of `main` | +| `sp` | `0x20081fc8` | Stack Pointer - top of our stack in RAM | +| `lr` | `0x100002d5` | Link Register - where we return after `main` | +| `r0-r3` | Various | Will hold function arguments and return values | + +> Tip: **Tip:** You can also use `i r pc sp lr` to show only specific registers you care about. + +### Quick Reference: Essential GDB Commands + +| Command | Short Form | What It Does | +| --------------------- | ---------- | ------------------------------------ | +| `break main` | `b main` | Set a breakpoint at main | +| `continue` | `c` | Continue execution until breakpoint | +| `disassemble` | `disas` | Show assembly for current function | +| `info registers` | `i r` | Show all register values | +| `stepi` | `si` | Execute one assembly instruction | +| `nexti` | `ni` | Execute one instruction (skip calls) | +| `x/10i $pc` | | Examine 10 instructions at PC | +| `monitor reset halt` | | Reset the target and halt | + +### Watching the Stack Change After `push {r3, lr}` + +The first instruction in `main` is `push {r3, lr}`. Before we step it, `SP` is `0x20082000`. After a single `si`, `SP` becomes `0x20081ff8`, which tells us the processor reserved 8 bytes on the stack for two 32-bit values. The first word at the new top of stack is `0xe000ed08`, which is the old value of `r3`, and the second word is `0x1000018f`, which is the saved `lr` return address. This matches the ABI rule we discussed earlier: the compiler pushes an even number of registers so the stack stays 8-byte aligned at the next call site before `stdio_init_all()` runs. + +Notice the difference between inspecting memory at `$sp` and inspecting `$lr`. `x/4x $sp` is enough here to show the relevant stack words in RAM, while `x/x $lr` shows the instruction word stored at the flash address held in the link register. In other words, `$sp` points to saved data on the stack, but `$lr` points to code that execution will return to later. + +```gdb +(gdb) x/x $sp +0x20082000: 0x00000000 +(gdb) si +0x10000236 5 stdio_init_all(); + +(gdb) x/x $sp +0x20081ff8: 0xe000ed08 +(gdb) x/4x $sp +0x20081ff8: 0xe000ed08 0x1000018f 0x00000000 0x00000000 +(gdb) x/x $lr +0x1000018f : 0x00478849 +``` + +> Tip: **What's Next?** In Week 2, we'll put these GDB commands to work with hands-on debugging exercises! We'll step through code, examine the stack, watch registers change, and ultimately use these skills to modify a running program. The commands you learned here are the foundation for everything that follows. + +--- + +## Part 5: Static Analysis with Ghidra + +### Setting Up Your First Ghidra Project + +Before we dive into GDB debugging, let's set up Ghidra to analyze our hello-world binary. Ghidra is a powerful reverse engineering tool that will help us visualize the disassembly and decompiled code. + +##### Step 1: Create a New Project + +1. Launch Ghidra +2. A window will appear - select **File -> New Project** +3. Choose **Non-Shared Project** and click **Next** +4. Enter the Project Name: `0x0001_hello-world` +5. Click **Finish** + +##### Step 2: Import the Binary + +1. Open your file explorer and navigate to the `Embedded-Hacking` folder +2. **Drag and drop** the `0x0001_hello-world.elf` file into the folder panel within the Ghidra application + +##### Step 3: Understand the Import Dialog + +In the small window that appears, you will see the file identified as an **ELF** (Executable and Linkable Format). + +> Tip: **What is an ELF file?** +> +> ELF stands for **Executable and Linkable Format**. This format includes **symbols** - human-readable names for functions and variables. These symbols make reverse engineering much easier because you can see function names like `main` and `printf` instead of just memory addresses. +> +> In future weeks, we will work with **stripped binaries** (`.bin` files) that do not contain these symbols. This is more realistic for real-world reverse engineering scenarios where symbols have been removed to make analysis harder. + +3. Click **Ok** to import the file +4. **Double-click** on the file within the project window to open it in the CodeBrowser + +##### Step 4: Auto-Analyze the Binary + +When prompted, click **Yes** to auto-analyze the binary. Accept the default analysis options and click **Analyze**. + +Ghidra will now process the binary, identifying functions, strings, and cross-references. This may take a moment. + +### Reviewing the Main Function in Ghidra + +Once analysis is complete, let's find our `main` function: + +1. In the **Symbol Tree** panel on the left, expand **Functions** +2. Look for `main` in the list (you can also use **Search -> For Address or Label** and type "main") +3. Click on `main` to navigate to it + +##### What You'll See + +Ghidra shows you two views of the code: + +**Listing View (Center Panel)** - The disassembled ARM assembly: +``` + ************************************************************* + * FUNCTION + ************************************************************* + int main (void ) + assume LRset = 0x0 + assume TMode = 0x1 + int r0:4 + main XREF[3]: Entry Point (*) , + _reset_handler:1000018c (c) , + .debug_frame::00000018 (*) + 0x0001_hello-world.c:4 (2) + 0x0001_hello-world.c:5 (2) + 10000234 08 b5 push {r3,lr} + 0x0001_hello-world.c:5 (4) + 10000236 01 f0 99 f9 bl stdio_init_all _Bool stdio_init_all(void) + LAB_1000023a XREF[1]: 10000240 (j) + 0x0001_hello-world.c:7 (6) + 0x0001_hello-world.c:8 (6) + 1000023a 02 48 ldr r0=>__EH_FRAME_BEGIN__ ,[DAT_10000244 ] = "hello, world\r" + = 100019CCh + 1000023c 01 f0 de f9 bl __wrap_puts int __wrap_puts(char * s) + 0x0001_hello-world.c:7 (8) + 10000240 fb e7 b LAB_1000023a + 10000242 00 ?? 00h + 10000243 bf ?? BFh + DAT_10000244 XREF[1]: main:1000023a (R) + 10000244 cc 19 00 10 undefine 100019CCh ? -> 100019cc + +``` + +**Decompile View (Right Panel)** - The reconstructed C code: +```c +int main(void) + +{ + stdio_init_all(); + do { + __wrap_puts("hello, world\r"); + } while( true ); +} +``` + +> **Notice how Ghidra reconstructed our original C code!** The decompiler recognized the infinite loop and the `puts` call (the compiler optimized `printf` to `puts` since we're just printing a simple string). + +##### Why We Start with .elf Files + +We're using the `.elf` file because it contains symbols that help us learn: +- Function names are visible (`main`, `stdio_init_all`, `puts`) +- Variable names may be preserved +- The structure of the code is easier to understand + +In future weeks, we'll work with `.bin` files that have been stripped of symbols. This will teach you how to identify functions and understand code when you don't have these helpful hints! + +--- + +## Part 6: Summary and Review + +### What We Learned + +1. **Registers**: The ARM Cortex-M33 has 13 general-purpose registers (`r0`-`r12`), plus special registers for the stack pointer (`r13`/SP), link register (`r14`/LR), and program counter (`r15`/PC). + +2. **The Stack**: + - Grows downward in memory + - PUSH adds items (SP decreases) + - POP removes items (SP increases) + - Used to save return addresses and register values + +3. **Memory Layout**: + - Code lives in flash memory starting at `0x10000000` + - Stack lives in RAM around `0x20080000` + +4. **GDB Basics**: We learned the essential commands for connecting to hardware and examining code: + +| Command | What It Does | +| --------------------- | -------------------------------------- | +| `target extended-remote :3333` | Connect to OpenOCD debug server | +| `monitor reset halt` | Reset and halt the processor | +| `b main` | Set breakpoint at main function | +| `c` | Continue running until breakpoint | +| `disas` | Disassemble current function | +| `i r` | Show all register values | + +5. **Ghidra Static Analysis**: We set up a Ghidra project and analyzed our binary: + - Imported the ELF file with symbols + - Found the `main` function + - Saw the decompiled C code + - Understood how assembly maps to C + +6. **Little-Endian**: The RP2350 stores multi-byte values with the least significant byte at the lowest address, making them appear "backwards" when viewed as a single value. + +### The Program Flow + +``` ++-----------------------------------------------------+ +| 1. push {r3, lr} | +| Save registers to stack | ++-----------------------------------------------------+ +| 2. bl stdio_init_all | +| Initialize standard I/O | ++-----------------------------------------------------+ +| 3. ldr r0, [pc, #8] ----------------+ | +| Load address of "hello, world" into r0| | ++-----------------------------------------------------+ +| 4. bl __wrap_puts | | +| Print the string | | ++-----------------------------------------------------+ +| 5. b.n (back to step 3) ----------------+ | +| Infinite loop! | ++-----------------------------------------------------+ +``` + +--- + +> **Note:** The detailed hands-on GDB debugging (stepping through code, watching the stack, examining memory) will be covered in Week 2! + +--- + +## Key Takeaways + +1. **Reverse engineering combines static and dynamic analysis** - we look at the code (static with Ghidra) and run it to see what happens (dynamic with GDB). + +2. **The stack is fundamental** - understanding how push/pop work is essential for following function calls. + +3. **GDB and Ghidra work together** - Ghidra helps you understand the big picture, GDB lets you watch it happen live. + +4. **Assembly isn't scary** - each instruction does one simple thing. Put them together and you understand the whole program! + +5. **Everything is just numbers** - whether it's code, data, or addresses, it's all stored as numbers in memory. + +--- + +## Glossary + +| Term | Definition | +| ------------------- | --------------------------------------------------------- | +| **Assembly** | Human-readable representation of machine code | +| **Breakpoint** | A marker that tells the debugger to pause execution | +| **GDB** | GNU Debugger - a tool for examining running programs | +| **Hex/Hexadecimal** | Base-16 number system (0-9, A-F) | +| **Little-Endian** | Storing the least significant byte at the lowest address | +| **Microcontroller** | A small computer on a single chip | +| **Program Counter** | Register that points to the next instruction | +| **Register** | Fast storage inside the processor | +| **Stack** | Memory region for temporary storage during function calls | +| **Stack Pointer** | Register that points to the top of the stack | +| **XIP** | Execute In Place - running code directly from flash | + + + diff --git a/WEEK01/slides/WEEK01-IMG00.svg b/WEEK01/slides/WEEK01-IMG00.svg new file mode 100644 index 0000000..e666d03 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 01 + + +Introduction and Overview of +Embedded Reverse Engineering: +Ethics, Scoping, and Basic Concepts + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK01/slides/WEEK01-IMG01.svg b/WEEK01/slides/WEEK01-IMG01.svg new file mode 100644 index 0000000..58888b0 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG01.svg @@ -0,0 +1,112 @@ + + + + + +ARM Cortex-M33 Regs +ARM Architecture & Registers + + + + Key Registers + + + + + r0 + Arg 1 / Return + + + + r1 + Arg 2 + + + + r2 + Arg 3 + + + + r3 + Arg 4 + + + + r0-r3 Caller-saved + r4-r11 Callee-saved + + + + SP (r13) + Stack Ptr + + + + LR (r14) + Return Addr + + + + PC (r15) + Next Instr + + + + xPSR + Status Flags + + + + Function Call Flow + + + + + main() + + + + + BL + + + + func() + + + + + BX LR + + + + + + How It Works + + r0 = first argument + puts(r0) passes the + string address in r0 + + + + LR saves return addr + BL: PC+4 stored in LR + BX LR: jump back + + + + All registers: 32 bits wide + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG02.svg b/WEEK01/slides/WEEK01-IMG02.svg new file mode 100644 index 0000000..3345c63 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG02.svg @@ -0,0 +1,101 @@ + + + + + +Stack Growth Direction +ARM Stack Mechanics + + + + Before PUSH + + + 0x20082000 + SP here + + + (empty) + + + (empty) + + + (empty) + + + (empty) + + 0x20080000 + + + Grows DOWN + + + + + + After PUSH + + + 0x20082000 + + + LR value + + + r3 value + + + (empty) + + + (empty) + + SP here now = 0x20081FF8 + + SP moved down + by 8 bytes + + 0x20080000 + + + + Key Concepts + + + Full Descending + SP points to the + last pushed item + + + + PUSH: SP -= 4 + Each 32-bit val + drops SP by 4 + Two vals = -8 + + + + POP: SP += 4 + Restores values + SP moves back up + + + + Initial SP + Set by vector + table at 0x00 + StackTop=0x20082000 + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG03.svg b/WEEK01/slides/WEEK01-IMG03.svg new file mode 100644 index 0000000..b684520 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG03.svg @@ -0,0 +1,98 @@ + + + + + +RP2350 Memory Map +RP2350 Address Space + + + + Address Space + + + + + ROM Boot + 0x0000_0000 + + + + XIP Flash + 16MB max + 0x1000_0000 + + + + SRAM + 520KB total + 0x2000_0000 + + + + APB Periph + 0x4000_0000 + + + + AHB Periph + 0x5000_0000 + + + + SIO + 0xD000_0000 + + + + PPB Cortex + 0xE000_0000 + + + addr+ + + + + Key Details + + + + XIP Flash + Code runs directly + from flash via cache + Execute-In-Place + + + + + SRAM Banks + SRAM0-7: 8x64KB + SRAM8-9: 2x4KB + Stack + Heap here + + + + + Peripherals + GPIO, UART, SPI + I2C, PWM, ADC + Memory-mapped I/O + + + + + SIO + PPB + Single-cycle I/O + Debug + NVIC + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG04.svg b/WEEK01/slides/WEEK01-IMG04.svg new file mode 100644 index 0000000..d6c7ad5 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG04.svg @@ -0,0 +1,90 @@ + + + + + +Stack vs Heap in RAM +RP2350 SRAM: Data, BSS, Heap, Stack + + + + SRAM Layout + + + + 0x2008_2000 (top) + + + + STACK + Grows DOWN + SP = top of stack + + + + + + + + FREE SPACE + + + + + + + + HEAP + Grows UP + heap_base = __end__ + + + + .data + .bss + + + 0x2000_0000 (base) + + + + How To Read Sizes Quickly + + + + 1) .data size + __data_end__ - __data_start__ + init non-zero globals/statics + + + + 2) .bss size + __bss_end__ - __bss_start__ + zero/uninit globals/statics + + + + 3) Heap starts at + __end__ (aka __HeapBase) + + + + 4) Stack starts at + vector_table[0] (initial SP) + typical: 0x20082000 on RP2350 + + + + 5) Safety rule + Heap must never collide with stack + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG05.svg b/WEEK01/slides/WEEK01-IMG05.svg new file mode 100644 index 0000000..7941c08 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG05.svg @@ -0,0 +1,86 @@ + + + + + +Link Register & Return +ARM Function Calls + + + + BL Call Flow + + + + Step 1: caller + + main: BL add + + + + + + + Step 2: hardware saves LR + + LR = return addr + + + + + + + Step 3: run function + + add: ADD r0, r1 + + + + + + + Step 4: return to caller + + BX LR (jump back) + + + + Key Concepts + + + BL instruction + Branch with Link + Saves return addr + in LR (r14) + + + + BX LR + Branch to addr + stored in LR + Returns to caller + + + + Nested Calls + Must PUSH LR first + PUSH {r3, lr} + POP {r3, pc} + POP into PC = return + + + + r14 = LR + Always check LR in GDB + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG06.svg b/WEEK01/slides/WEEK01-IMG06.svg new file mode 100644 index 0000000..3d6d502 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG06.svg @@ -0,0 +1,101 @@ + + + + + +Program Counter Flow +Instruction Execution + + + + PC Execution + + + + ADDR + INSTRUCTION + + + + 0x1000 + MOV r0, #5 + PC + + + + 0x1002 + MOV r1, #3 + + + + 0x1004 + ADD r0, r1 + + + + 0x1006 + BL func + + + + 0x1010 + func: PUSH {lr} + + + + 0x1012 + SUB r0, #1 + + + + 0x1014 + POP {pc} + + + + + BL = jump to function target + POP {pc} = return to caller + + + + Key Concepts + + + r15 = PC + Points to current + instruction + 4 + Prefetch pipeline + + + + Sequential + Thumb next = +2 or +4 + depends on instruction encoding + Cortex-M33 executes Thumb only + + + + Branch + B = unconditional + BL = save LR, jump + BX = branch reg + BEQ = branch if Z=1 + not linear +2 stepping + + + + GDB tip + stepi = step 1 instr + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG07.svg b/WEEK01/slides/WEEK01-IMG07.svg new file mode 100644 index 0000000..0acc44a --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG07.svg @@ -0,0 +1,110 @@ + + + + + +Little-Endian Bytes +Byte Ordering + + + + Byte Ordering + + + + 0xDEADBEEF + + + Big-Endian + MSB first + + + DE + + + AD + + + BE + + + EF + + + +0 + +1 + +2 + +3 + + + vs + + + Little-Endian + LSB first + + + EF + + + BE + + + AD + + + DE + + + +0 + +1 + +2 + +3 + + + Bytes are reversed! + Lowest address holds + least significant byte + ARM uses little-endian + + + + Key Concepts + + + ARM = Little-Endian + Cortex-M33 uses LE + by default + Also: x86, RISC-V + + + + Why It Matters + Memory dumps show + raw byte order + Must mentally flip + to get true value + + + + GDB Example + x/4xb 0x2000 + EF BE AD DE + = 0xDEADBEEF + + + + x/xw = word view + GDB auto-corrects + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG08.svg b/WEEK01/slides/WEEK01-IMG08.svg new file mode 100644 index 0000000..6b7faad --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG08.svg @@ -0,0 +1,90 @@ + + + + + +XIP Flash Model +Execute-in-Place + + + + Execute-In-Place + + + + + QSPI Flash + External 16MB + + + + + + + + QSPI Controller + + + + + + + + XIP Cache + 16KB cache + + + + + + + + Cortex-M33 CPU + Fetches via PC + + + Mapped at + 0x1000_0000 + + + + Key Details + + + What is XIP? + Code stays in flash + CPU reads it as if + it were normal memory + No copy to SRAM needed + + + + QSPI Interface + 4 data lines + Fast serial flash + CLK, CS, IO0-IO3 + + + + Cache Behavior + Used for XIP flash reads + (code + const data at 0x1000....) + Not used for SRAM/peripherals + or during flash program/erase + + + + RE Insight + Dump flash via SWD + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG09.svg b/WEEK01/slides/WEEK01-IMG09.svg new file mode 100644 index 0000000..7fc15e0 --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG09.svg @@ -0,0 +1,92 @@ + + + + + +ELF File Structure +Binary Format + + + + ELF File Layout (Offsets) + + Higher file offsets + + + + Section Headers + + + + .symtab + + + + .bss + + + + .data + + + + .rodata + + + + .text + Machine code + + + + Program Headers + + + + ELF Header + Magic: 7f 45 4c 46 (off 0x0000) + + + + Section Details + Mapping + + + ELF Header + Arch: ARM 32-bit + Entry point addr + Type: executable + + + + .text = code + All instructions + Maps to XIP flash + Disassemble this! + + + + .data / .rodata + .data = initialized + .rodata = constants + .bss = zeroed vars + + + + .symtab = symbols + Function names + ELF header is file metadata + not runtime address 0x1000.... + Use section VMA/LMA for memory map + \ No newline at end of file diff --git a/WEEK01/slides/WEEK01-IMG10.svg b/WEEK01/slides/WEEK01-IMG10.svg new file mode 100644 index 0000000..ecffd4b --- /dev/null +++ b/WEEK01/slides/WEEK01-IMG10.svg @@ -0,0 +1,94 @@ + + + + + +GDB-OpenOCD Chain +Debug Toolchain + + + + Debug Toolchain + + + + + GDB + Client + + + + + TCP + :3333 + + + + OpenOCD + Server + + + + + USB + + + + CMSIS-DAP + Probe + + + + + SWD + + + + RP2350 + Target + + + + GDB Commands + + + target remote :3333 + Connect to OpenOCD + + monitor reset halt + Reset + stop at entry + + break main + Set breakpoint + + info registers + Dump all regs + + + + SWD Protocol + + + 2 wires only + SWCLK = clock + SWDIO = data + + + + Capabilities + Read/write memory + Read/write regs + Set breakpoints + Full chip control + \ No newline at end of file diff --git a/WEEK02/WEEK02-SLIDES.pdf b/WEEK02/WEEK02-SLIDES.pdf new file mode 100644 index 0000000..be346bc Binary files /dev/null and b/WEEK02/WEEK02-SLIDES.pdf differ diff --git a/WEEK02/WEEK02.md b/WEEK02/WEEK02.md new file mode 100644 index 0000000..05bb942 --- /dev/null +++ b/WEEK02/WEEK02.md @@ -0,0 +1,1597 @@ +# Week 2: Hello, World - Debugging and Hacking Basics: Debugging and Hacking a Basic Program for the Pico 2 + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Connect to a live embedded system using OpenOCD and GDB +- Step through code instruction by instruction and watch the stack change +- Examine memory, registers, and decode little-endian values +- Set strategic breakpoints to pause execution at key moments +- Understand why direct string assignment fails in bare-metal systems +- Write custom data directly to SRAM memory +- Hijack register values to redirect program behavior +- Modify a running program's output in real-time + +## Review from Week 1 +This week builds directly on Week 1 concepts. You should already be comfortable with: +- **Registers** (`r0`-`r12`, SP, LR, PC) - We'll watch them change and manipulate `r0` to change program behavior +- **Memory Layout** (Flash at `0x10000000`, RAM at `0x20000000`) - Critical for understanding where we can write +- **The Stack** and how `push`/`pop` work - We'll watch this in action +- **Little-Endian** byte ordering - We'll decode values live +- **GDB basics** from Week 1's dynamic analysis section +- **Ghidra basics** from Week 1's static analysis section + +--- + +## Part 1: Understanding Live Hacking + +#### What is Live Hacking? + +**Live hacking** means modifying a program *while it's running* on real hardware. Instead of changing the source code and recompiling, we intercept the program mid-execution and change what it does on the fly. + +Think of it like this: imagine a train is heading to New York City. Live hacking is like switching the tracks while the train is moving so it goes to Los Angeles instead! + +#### Why is This Important? + +Live hacking techniques are used for: +- **Security Research**: Finding vulnerabilities in embedded systems +- **Penetration Testing**: Testing if systems can be compromised +- **Malware Analysis**: Understanding how malicious code works +- **Debugging**: Fixing bugs in systems that can't be easily reprogrammed + +#### Real-World Application + +> **"With great power comes great responsibility!"** + +Imagine you're a security researcher testing an industrial control system at a power plant. You need to verify that an attacker couldn't: +1. Change the values being displayed to engineers +2. Make dangerous equipment appear safe +3. Hide malicious activity from monitoring systems + +The techniques you'll learn today are *exactly* how this would be done. Understanding these attacks helps us build better defenses! + +--- + +## Part 2: Review - Memory Layout (from Week 1) + +> **REVIEW:** In Week 1, we learned about the RP2350's memory layout. This knowledge is essential for our hack! + +Before we hack, let's remember where things live in memory on the RP2350: + +#### The Code We're Hacking + +Remember our `0x0001_hello-world.c` program from Week 1: + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + + while (true) + printf("hello, world\r\n"); +} +``` + +This simple program: +1. Initializes I/O with `stdio_init_all()` +2. Enters an infinite `while(true)` loop +3. Prints `"hello, world\r\n"` forever + +Our goal: **Make it print something else WITHOUT changing the source code!** + +#### Memory Map + +``` ++-----------------------------------------------------+ +| Flash Memory (XIP) - READ ONLY | +| Starts at: 0x10000000 | +| Contains: Program code, constant strings | +| NOTE: We CANNOT write to flash during runtime! | ++-----------------------------------------------------+ +| SRAM - READ/WRITE | +| Starts at: 0x20000000 | +| Contains: Stack, Heap, Variables | +| NOTE: We CAN write to SRAM during runtime! | ++-----------------------------------------------------+ +``` + +> **REVIEW:** In Week 1, we saw SP values in the `0x20081xxx` range (for example `0x20081fc8`) - that's in the SRAM region. In this run you may see values like `0x20081ff8` depending on where execution is paused. The stack "grows downward" from the top of SRAM. + +#### Why This Matters for Our Hack + +The string `"hello, world"` is stored in **flash memory** (around `0x100019cc`). Flash memory is **read-only** during normal operation - we can't just overwrite it. + +But SRAM (starting at `0x20000000`) is **read-write**! This is where we'll create our hacked string. + +--- + +## Part 3: The Attack Plan + +Here's our step-by-step attack strategy: + +``` ++-----------------------------------------------------+ +| STEP 1: Start the debug server (OpenOCD) | ++-----------------------------------------------------+ +| STEP 2: Connect with GDB and halt the program | ++-----------------------------------------------------+ +| STEP 3: Set a breakpoint right before puts() | ++-----------------------------------------------------+ +| STEP 4: When we hit the breakpoint, r0 contains | +| the address of "hello, world" | ++-----------------------------------------------------+ +| STEP 5: Create our malicious string in SRAM | ++-----------------------------------------------------+ +| STEP 6: Change r0 to point to OUR string | ++-----------------------------------------------------+ +| STEP 7: Continue execution - HACKED! | ++-----------------------------------------------------+ +``` + +--- + +## Part 4: Setting Up Your Environment + +#### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board with debug probe connected +2. OpenOCD installed and configured +3. GDB (arm-none-eabi-gdb) installed +4. A serial monitor application (like PuTTY, minicom, or screen) +5. The "hello-world" binary loaded on your Pico 2 + +#### What You'll Need Open + +You will need **THREE** terminal windows: +1. **Terminal 1**: Running OpenOCD (the debug server) +2. **Terminal 2**: Running GDB (where we do the hacking) +3. **PuTTY**: Running your serial monitor (to see output) + +--- + +## Part 5: GDB Deep Dive - Exploring the Binary + +Before we start hacking, let's use GDB to thoroughly understand our program. This hands-on tutorial will teach you to examine memory, step through code, and watch the stack in action. + +#### Starting the Debug Environment + +##### Step 0a: Start OpenOCD (Terminal 1) + +OpenOCD is the bridge between your computer and the Pico 2's debug interface. It creates a server that GDB can connect to. + +**Open Terminal 1 and type:** + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +**What this command means:** +- `openocd` = the OpenOCD program +- `-s ...` = path to OpenOCD scripts folder +- `-f interface/cmsis-dap.cfg` = use the CMSIS-DAP debug probe configuration +- `-f target/rp2350.cfg` = configure for the RP2350 chip +- `-c "adapter speed 5000"` = set the debug speed to 5000 kHz + +**You should see output like:** + +``` +Open On-Chip Debugger 0.12.0 +Licensed under GNU GPL v2 +Info : Listening on port 3333 for gdb connections +Info : CMSIS-DAP: SWD supported +Info : CMSIS-DAP: Interface ready +``` + +**Important:** Leave this terminal running! Don't close it. + +##### Step 0b: Start Your Serial Monitor - PuTTY (Terminal 2) + +PuTTY will show us the output from our Pico 2. When we hack the program, we'll see the results here! + +**To set up PuTTY:** + +1. Open **PuTTY** +2. In the **Session** category: + - **Connection type**: Select **Serial** + - **Serial line**: Enter your COM port (e.g., `COM3` - check Device Manager to find yours) + - **Speed**: Enter `115200` +3. Click **Open** + +> Tip: **Finding your COM port:** Open Device Manager -> Ports (COM & LPT) -> Look for "USB Serial Device" or "Pico" - note the COM number. + +**You should see:** + +``` +hello, world +hello, world +hello, world +hello, world +... +``` + +The program is running and printing `"hello, world"` in an infinite loop! + +**Important:** Leave PuTTY running! We'll watch it change when we hack the system. + +##### Step 0c: Start GDB (Terminal 3) + +**Open Terminal 3** and start GDB with your binary: + +```cmd +arm-none-eabi-gdb build\0x0001_hello-world.elf +``` + +**What this command means:** +- `arm-none-eabi-gdb` = the ARM version of GDB +- `build\0x0001_hello-world.elf` = our compiled program with debug symbols + +**You should see:** + +``` +GNU gdb (Arm GNU Toolchain 13.2) 13.2 +Reading symbols from build\0x0001_hello-world.elf... +(gdb) +``` + +The `(gdb)` prompt means GDB is ready for commands! + +##### Step 0d: Connect GDB to OpenOCD + +Now we need to connect GDB to OpenOCD. OpenOCD is listening on port `3333`. + +**Type this command:** + +```gdb +(gdb) target extended-remote :3333 +``` + +**You should see:** + +``` +Remote debugging using :3333 +main () + at C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c:5 +5 stdio_init_all(); +``` + +We're connected! GDB shows us the program is currently in the `main` function. + +##### Step 0e: Halt the Running Program + +The program is still running (you can see "hello, world" still printing in PuTTY). Let's stop it: + +**Type this command:** + +```gdb +(gdb) monitor reset halt +``` + +**What this command means:** +- `monitor` = send a command to OpenOCD (not GDB) +- `reset` = reset the processor +- `halt` = stop execution immediately + +**You should see:** + +``` +[rp2350.cm0] halted due to debug-request, current mode: Thread +xPSR: 0xf9000000 pc: 0x00000088 msp: 0xf0000000 +[rp2350.cm1] halted due to debug-request, current mode: Thread +xPSR: 0xf9000000 pc: 0x00000088 msp: 0xf0000000 +``` + +**Check PuTTY:** The "hello, world" messages should have stopped! The processor is now frozen, waiting for our commands. + +--- + +#### Exploring the Binary + +Now that we're connected and the processor is halted, let's explore! + +##### Step 1: Examine Memory Starting at XIP Base Address + +Our program code starts at address `0x10000000`. Let's look at the first 1000 instructions to find our `main` function. + +**Type this command in GDB:** + +```gdb +(gdb) x/1000i 0x10000000 +``` + +**What this command means:** +- `x` = examine memory +- `/1000i` = show 1000 instructions +- `0x10000000` = starting address + +**What you're looking for:** + +Scroll through the output and look for something like this: + +``` +0x10000234
: push {r3, lr} +``` + +The `
` label tells us we found our main function! The address `0x10000234` is where main starts. + +##### Step 2: Examine the Main Function in Detail + +Now let's look at just the main function. We'll examine 5 instructions starting at the main function address: + +**Type this command:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**You should see:** + +``` +(gdb) x/5i 0x10000234 +=> 0x10000234
: push {r3, lr} + 0x10000236 : bl 0x1000156c + 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +**Let's understand each instruction:** + +| Address | Instruction | What It Does | +| ------------ | -------------------------------- | --------------------------------------------------- | +| `0x10000234` | `push {r3, lr}` | Save `r3` and the return address onto the stack | +| `0x10000236` | `bl 0x1000156c ` | Call the `stdio_init_all` function | +| `0x1000023a` | `ldr r0, [pc, #8]` | Load the address of our string into `r0` | +| `0x1000023c` | `bl 0x100015fc <__wrap_puts>` | Call `puts` to print our string | +| `0x10000240` | `b.n 0x1000023a` | Jump back to the `ldr` instruction (infinite loop!) | + +##### Step 3: Set a Breakpoint at Main + +A **breakpoint** is like a stop sign for your program. When the processor reaches that address, it will pause and let us examine things. + +**Type this command:** + +```gdb +(gdb) b *0x10000234 +``` + +**You should see:** + +``` +Breakpoint 1 at 0x10000234: file C:/Users/.../0x0001_hello-world.c, line 5. +Note: automatically using hardware breakpoints for read-only addresses. +``` + +**What this means:** +- `b` = set breakpoint +- `*0x10000234` = at this exact memory address +- GDB confirms the breakpoint is set and even tells us which line of C code this corresponds to! + +##### Step 4: Continue Execution Until Breakpoint + +Now let's run the program until it hits our breakpoint: + +**Type this command:** + +```gdb +(gdb) c +``` + +**You should see:** + +``` +Continuing. + +Thread 1 "rp2350.cm0" hit Breakpoint 1, main () + at C:/Users/.../0x0001_hello-world.c:5 +5 stdio_init_all(); +``` + +**What happened:** +- The processor ran until it reached address `0x10000234` +- It stopped right before executing the instruction at that address +- GDB shows us we're at line 5 of our C source code + +##### Step 5: Examine Instructions with Arrow + +Let's look at our instructions again: + +**Type this command:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**You should see:** + +``` +(gdb) x/5i 0x10000234 +=> 0x10000234
: push {r3, lr} + 0x10000236 : bl 0x1000156c + 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +**Notice the arrow `=>`!** This arrow shows which instruction we're about to execute. We haven't executed it yet - we're paused right before it. + +--- + +#### Understanding the Stack in Action + +##### Step 6: Examine the Stack Before Push + +Before we execute the `push` instruction, let's see what's on the stack: + +**Type this command:** + +```gdb +(gdb) x/10x $sp +``` + +**What this command means:** +- `x` = examine memory +- `/10x` = show 10 values in hexadecimal +- `$sp` = starting at the stack pointer address + +**You should see:** + +``` +0x20082000: 0x00000000 0x00000000 0x00000000 0x00000000 +0x20082010: 0x00000000 0x00000000 0x00000000 0x00000000 +0x20082020: 0x00000000 0x00000000 +``` + +**What this shows:** +- The stack pointer is at address `0x20082000` +- The stack is empty (all zeros) +- This is the "top" of our stack in RAM + +**Verify where this value came from (the Vector Table):** + +Now let's trace back where this initial `0x20082000` value came from. It comes from the first entry in the vector table at `0x10000000`: + +```gdb +(gdb) x/x $sp +0x20082000: 0x00000000 +(gdb) x/x 0x10000000 +0x10000000 <__vectors>: 0x20082000 +``` + +**What this shows:** +- The first command `x/x $sp` reads one word at the stack pointer (currently `0x00000000`) +- The second command `x/x 0x10000000` reads the **first entry in the vector table** at address `0x10000000` +- That vector table entry contains `0x20082000` - this is the **initial stack pointer value**! +- The Boot ROM reads this value and puts it into the SP register when the chip starts +- This is why our SP register already has `0x20082000` before `main()` runs + +##### Step 7: Execute One Instruction (Step Into) + +Now let's execute just ONE assembly instruction: + +**Type this command:** + +```gdb +(gdb) si +``` + +**What this command means:** +- `si` = step instruction (execute one assembly instruction) + +**You should see:** + +``` +0x10000236 5 stdio_init_all(); +``` + +Let's verify where we are: + +**Type this command:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**You should see:** + +``` +(gdb) x/5i 0x10000234 + 0x10000234
: push {r3, lr} +=> 0x10000236 : bl 0x1000156c + 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +**Notice:** The arrow `=>` has moved! We've executed the `push` instruction and are now about to execute the `bl` (branch with link) instruction. + +##### Step 8: Examine the Stack After Push + +Now let's see what the push instruction did to our stack: + +**Type this command:** + +```gdb +(gdb) x/10x $sp +``` + +**You should see:** + +``` +0x20081ff8: 0xe000ed08 0x1000018f 0x00000000 0x00000000 +0x20082008: 0x00000000 0x00000000 0x00000000 0x00000000 +0x20082018: 0x00000000 0x00000000 +``` + +**What changed:** +- The stack pointer moved from `0x20082000` to `0x20081ff8` +- That's 8 bytes lower (2 * 4-byte values) +- Two new values appeared: `0xe000ed08` and `0x1000018f` + +##### Step 9: Verify What Was Pushed + +Let's prove that these values came from `r3` and `lr`: + +**Check `r3`:** + +```gdb +(gdb) x/x $r3 +``` + +**You should see:** + +``` +0xe000ed08: Cannot access memory at address 0xe000ed08 +``` + +This error is expected! The value `0xe000ed08` is in `r3`, and when we try to examine it as an address, that memory location isn't accessible. But we can see the value matches what's on the stack! + +**Check `lr` (Link Register):** + +```gdb +(gdb) x/x $lr +``` + +**You should see:** + +``` +0x1000018f : 0x00478849 +``` + +The value `0x1000018f` is in `lr` - this is the return address! This matches the second value on our stack. + +##### Step 10: Verify Stack Layout + +Let's look at each pushed value individually: + +**First pushed value (`r3`):** + +```gdb +(gdb) x/x $sp +``` + +**You should see:** + +``` +0x20081ff8: 0xe000ed08 +``` + +This is the value from `r3`, pushed first. + +**Second pushed value (`lr`):** + +```gdb +(gdb) x/x $sp+4 +``` + +**You should see:** + +``` +0x20081ffc: 0x1000018f +``` + +This is the value from `lr` (the return address), pushed second. + +### Understanding the Stack Diagram + +``` +Before push {r3, lr}: After push {r3, lr}: + +Address Value Address Value +--------------------- --------------------- +0x20082000 (empty) ↠SP 0x20082000 (old SP location) + 0x20081ffc 0x1000018f (lr) + 0x20081ff8 0xe000ed08 (r3) ↠SP +``` + +**Key Points:** +1. The stack grows DOWNWARD (addresses get smaller) +2. The SP always points to the last item pushed +3. `r3` was pushed first, then `lr` was pushed on top of it + +--- + +#### Continuing Through the Program + +##### Step 11: Step Over the stdio_init_all Function + +We don't need to examine every instruction inside `stdio_init_all` - it's just setup code. Let's "step over" it: + +**First, verify where we are:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**You should see:** + +```gdb +(gdb) x/5i 0x10000234 + 0x10000234
: push {r3, lr} +=> 0x10000236 : bl 0x1000156c + 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +**Now step over the function call:** + +```gdb +(gdb) n +``` + +**What this command means:** +- `n` = next (step over function calls, don't go inside them) + +**You should see:** + +``` +8 printf("hello, world\r\n"); +``` + +**Verify where we are now:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**You should see:** + +```gdb +(gdb) x/5i 0x10000234 + 0x10000234
: push {r3, lr} + 0x10000236 : bl 0x1000156c +=> 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) + 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +The arrow has moved past the function call! + +#### Understanding the LDR Instruction + +We're now at: +``` +ldr r0, [pc, #8] @ (0x10000244 ) +``` + +**What does this instruction do?** + +1. Take the current Program Counter (PC) value +2. Add 8 to it +3. Go to that memory address (`0x10000244`) +4. Load the value stored there into `r0` + +This is loading a **pointer** - the address of our "hello, world" string! + +##### Step 13: Execute the LDR and Examine `r0` + +**Execute one instruction:** + +```gdb +(gdb) si +``` + +**You should see:** + +``` +0x1000023c 8 printf("hello, world\r\n"); +``` + +**Now examine what's in `r0`:** + +```gdb +(gdb) x/x $r0 +``` + +**You should see:** + +``` +0x100019cc: 0x6c6c6568 +``` + +##### Step 14: Decoding the Mystery Value + +The value `0x6c6c6568` looks strange, but it's actually ASCII characters! Let's decode it: + +**ASCII Table Reference:** +| Hex | Character | +| ------ | --------- | +| `0x68` | h | +| `0x65` | e | +| `0x6c` | l | +| `0x6c` | l | + +So `0x6c6c6568` = "lleh" backwards! + +**Why is it backwards?** + +This is called **little-endian** byte order. The RP2350 stores bytes in reverse order in memory. When we read them as a 32-bit value, they appear reversed. + +##### Step 15: View the Full String + +Let's tell GDB to show this as a string instead of a hex number: + +```gdb +(gdb) x/s $r0 +``` + +**What this command means:** +- `x` = examine memory +- `/s` = show as a string +- `$r0` = at the address stored in `r0` + +**You should see:** + +``` +0x100019cc: "hello, world\r" +``` + +There's our string! The `\r` is a carriage return character (part of `\r\n`). + +> **Key Discovery:** The string `"hello, world"` is stored at address `0x100019cc` in flash memory. This is the value that gets loaded into `r0` before calling `puts()`. We'll use this knowledge in our hack! + +--- + +## Part 6: Continuing the Debug Session for the Hack + +You're right where you need to be from Part 5, so we **do not restart** OpenOCD or GDB here. + +Use this as a quick checkpoint before the hack: + +- OpenOCD is still running and listening on `:3333` +- GDB is still connected (`target extended-remote :3333` already done) +- The target is halted at a known point (or easy to re-halt) + +If your session is still live, continue directly to Part 7. + +If you got disconnected, run only this minimal recovery in GDB: + +```gdb +(gdb) target extended-remote :3333 +(gdb) monitor reset halt +``` + +After that, continue to the analysis steps below. + +--- + +## Part 7: Analyzing the Target + +> **REVIEW:** We're using the same GDB commands we learned earlier. The `x` command examines memory, and `/5i` shows 5 instructions. + +##### Step 6: Examine the Main Function + +Let's look at the main function to understand what we're dealing with: + +**Type this command:** + +```gdb +(gdb) x/5i 0x10000234 +``` + +**What this command means:** +- `x` = examine memory (Week 1 review!) +- `/5i` = show 5 instructions +- `0x10000234` = the address of main (we found this in Week 1!) + +**You should see:** + +```gdb +(gdb) x/5i 0x10000234 + 0x10000234
: push {r3, lr} + 0x10000236 : bl 0x1000156c + 0x1000023a : ldr r0, [pc, #8] @ (0x10000244 ) +=> 0x1000023c : bl 0x100015fc <__wrap_puts> + 0x10000240 : b.n 0x1000023a +``` + +> **REVIEW:** This is the same disassembly we analyzed in Week 1! Remember: +> - `push {r3, lr}` saves registers to the stack +> - `bl` is "branch with link" - it calls a function and saves the return address in LR +> - `b.n` is the infinite loop that jumps back to the `ldr` instruction + +#### Understanding the Code Flow + +> **REVIEW:** In Week 1, we learned that `r0`-`r3` are used to pass arguments to functions. The first argument always goes in `r0`! + +Let's break down what happens each time through the loop: + +| Address | Instruction | What Happens | +| ------------ | ------------------ | ------------------------------------------------------ | +| `0x1000023a` | `ldr r0, [pc, #8]` | Load the address of `"hello, world"` into `r0` | +| `0x1000023c` | `bl __wrap_puts` | Call `puts()` - it reads the string address from `r0`! | +| `0x10000240` | `b.n 0x1000023a` | Jump back to the `ldr` instruction (loop forever) | + +#### How This Maps to Our C Code + +```c +while (true) + printf("hello, world\r\n"); // The compiler optimized this to puts() +``` + +The compiler: +1. Loads the string address into `r0` (first argument) +2. Calls `puts()` (optimized from `printf()` since we're just printing a string) +3. Loops back forever with `b.n` + +**The Key Insight:** Right before the `bl __wrap_puts` instruction (at `0x1000023c`), the register `r0` contains the address of the string to print! + +If we can change what `r0` points to, we can make it print **anything we want**! + +--- + +## Part 8: Setting the Trap + +> **REVIEW:** In Week 1, we used `b main` and `b *0x10000234` to set breakpoints. Now we'll use the same technique at a more strategic location! + +##### Step 7: Set a Strategic Breakpoint + +We want to stop the program RIGHT BEFORE it calls `puts()`. That's at address `0x1000023c`. + +**Type this command:** + +```gdb +(gdb) b *0x1000023c +``` + +**What this command means:** +- `b` = set a breakpoint (same as Week 1!) +- `*0x1000023c` = at this exact memory address (the asterisk means "address") + +**You should see:** + +``` +Breakpoint 2 at 0x1000023c: file C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c, line 8 +``` + +**What does "hardware breakpoints" mean?** + +Because our code is in flash memory (read-only), GDB can't insert a software breakpoint by modifying the code. Instead, it uses a special feature of the ARM processor called a **hardware breakpoint**. The processor has a limited number of these (usually 4-8), but they work on any memory type. + +##### Step 8: Continue Execution and Hit the Breakpoint + +Now let's run the program until it hits our breakpoint: + +**Type this command:** + +```gdb +(gdb) c +``` + +**What this command means:** +- `c` = continue (run until something stops us) + +**You should see:** + +```gdb +Continuing. + +Thread 1 "rp2350.cm0" hit Breakpoint 2, 0x1000023c in main () + at C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c:8 +8 printf("hello, world\r\n"); +``` + +The program has stopped RIGHT BEFORE calling `puts()`! The string address is loaded into `r0`, but the function hasn't been called yet. + +##### Step 9: Verify Our Position with Disassembly + +Let's double-check where we are using the `disas` command: + +**Type this command:** + +```gdb +(gdb) disas +``` + +**What this command means:** +- `disas` = disassemble the current function + +**You should see:** + +```gdb +(gdb) disas +Dump of assembler code for function main: + 0x10000234 <+0>: push {r3, lr} + 0x10000236 <+2>: bl 0x1000156c + 0x1000023a <+6>: ldr r0, [pc, #8] @ (0x10000244 ) +=> 0x1000023c <+8>: bl 0x100015fc <__wrap_puts> + 0x10000240 <+12>: b.n 0x1000023a + 0x10000242 <+14>: nop + 0x10000244 <+16>: adds r4, r1, r7 + 0x10000246 <+18>: asrs r0, r0, #32 +``` + +**Notice the arrow `=>`** pointing to `0x1000023c`! This confirms we're about to execute the `bl __wrap_puts` instruction. Perfect! + +--- + +## Part 9: Examining the Current State + +> **REVIEW:** In Week 1, we used `x/s $r0` to view the "hello, world" string. We also learned about **little-endian** byte ordering - remember how `0x6c6c6568` spelled "lleh" backwards? + +##### Step 10: Examine What's in r0 + +Let's see what string `r0` is currently pointing to: + +**Type this command:** + +```gdb +(gdb) x/s $r0 +``` + +**What this command means:** +- `x` = examine memory (Week 1 review!) +- `/s` = display as a string +- `$r0` = the address stored in register `r0` + +**You should see:** + +```gdb +0x100019cc: "hello, world\r" +``` + +There it is! The register `r0` contains `0x100019cc`, which is the address of our `"hello, world"` string in flash memory. + +> **REVIEW:** This is the same address `0x100019cc` we discovered in Week 1, Step 15 when we used `x/s $r0` after executing the `ldr` instruction! + +--- + +## Part 10: The Failed Hack Attempt (Learning Why) + +##### Step 11: Try to Directly Change the String (This Will Fail!) + +Your first instinct might be to just assign a new string to `r0`. Let's try it and see what happens: + +**Type this command:** + +```gdb +(gdb) set $r0 = "hacky, world\r" +``` + +**You should see an error:** + +``` +evaluation of this expression requires the program to have a function "malloc". +``` + +**Oh no! It didn't work!** + +#### Why Did This Fail? + +This is a very important lesson! Here's what happened: + +1. When you type `"hacky, world\r"` in GDB, GDB interprets this as: "Create a new string and give me its address" + +2. To create a new string at runtime, GDB tries to allocate target memory using `malloc()`. + +3. If the target program exposed a working `malloc()` that GDB could call, this style of assignment can work. + +4. In our case, this is a bare-metal firmware image and there is no usable `malloc()` path for GDB expression evaluation, so GDB cannot create temporary storage for that string literal. + +5. That is why this command fails here, and why we switch to writing bytes directly into SRAM ourselves. + +**Let's verify nothing changed:** + +```gdb +(gdb) x/s $r0 +``` + +**You should see:** + +``` +0x100019cc: "hello, world\r" +``` + +The original string is still there. Our hack attempt failed... but we're not giving up! + +--- + +## Part 11: The Real Hack - Writing to SRAM + +##### Step 12: Understanding the Solution + +Since we can't use `malloc()`, we need to manually create our string somewhere in memory. Remember our memory map? + +- Flash (`0x10000000`): **Read-only** - can't write here +- SRAM (`0x20000000`): **Read-write** - we CAN write here! + +> **REVIEW:** In Week 1 we observed SP in the `0x20081xxx` range (for example `0x20081fc8`). In this run, after `push {r3, lr}`, you are seeing `0x20081ff8`, which is also correct and still near the top of SRAM. We'll write our string at a safer SRAM address (`0x20040000`) to avoid vector-table and stack conflicts. + +We'll write our malicious string directly to SRAM, then point `r0` to it. + +##### Step 13: Create Our Malicious String in SRAM + +We need to write 13 bytes (12 characters + null terminator) to SRAM: + +| Character | ASCII Hex | +| --------- | --------- | +| h | - | +| a | - | +| c | - | +| k | - | +| y | - | +| , | - | +| (space) | - | +| w | - | +| o | - | +| r | - | +| l | - | +| d | - | +| \r | - | +| \0 | - | + +**Type this command:** + +```gdb +(gdb) set {char[13]} 0x20040000 = "hacky, world" +``` + +**What this command means:** +- `set` = modify memory +- `{char[13]}` = treat the target as an array of 13 characters +- `0x20040000` = the address where we're writing (safe SRAM offset) +- `= "hacky, world"` = the string bytes to write + +**No output means success!** + +##### Step 14: Verify Our String Was Written + +Let's confirm our malicious string is in SRAM: + +**Type this command:** + +```gdb +(gdb) x/s 0x20040000 +``` + +**You should see:** + +```gdb +0x20040000: "hacky, world" +``` + +**OUR STRING IS IN MEMORY!** + +The important thing is our string is in writable SRAM at a safe offset and ready to use. + +--- + +## Part 12: Hijacking the Register + +> **REVIEW:** In Week 1, we learned that `r0` holds the first argument to a function. When `puts()` is called, it expects `r0` to contain a pointer to the string it should print. By changing `r0`, we change what gets printed! + +##### Step 15: Change r0 to Point to Our String + +Now for the magic moment! We'll change `r0` from pointing to the original string to pointing to OUR string: + +**Type this command:** + +```gdb +(gdb) set $r0 = 0x20040000 +``` + +**What this command means:** +- `set` = modify a value +- `$r0` = the `r0` register +- `= 0x20040000` = change it to this address (where our string is) + +**No output means success!** + +##### Step 16: Verify the Register Was Changed + +Let's confirm `r0` now points to our malicious string: + +**First, check one byte (explicit byte view):** + +```gdb +(gdb) x/bx $r0 +``` + +**You should see:** + +``` +0x20040000: 0x68 +``` + +The value `0x68` is ASCII `'h'`, the first byte of `"hacky, world"`. + +**Now check one 32-bit word (explicit word view):** + +```gdb +(gdb) x/wx $r0 +``` + +**You should see:** + +``` +0x20040000: 0x6b636168 +``` + +This is the first 4 bytes (`h a c k`) packed into one 32-bit little-endian word. + +**Now check it as a string:** + +```gdb +(gdb) x/s $r0 +``` + +**You should see:** + +``` +0x20040000: "hacky, world" +``` + +**THE HIJACK IS COMPLETE!** When `puts()` runs, it will read the string address from `r0` - which now points to our malicious string! + +--- + +## Part 13: Executing the Hack + +##### Step 17: Continue Execution + +This is the moment of truth! Let's continue the program and watch our hack take effect: + +**Type this command:** + +```gdb +(gdb) c +``` + +**You should see:** + +```gdb +Continuing. + +Thread 1 "rp2350.cm0" hit Breakpoint 2, 0x1000023c in main () + at C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c:8 +8 printf("hello, world\r\n"); +``` + +The program ran through one loop iteration and hit our breakpoint again. + +##### Step 18: Check Your Serial Monitor! + +**Look at Terminal 2 (your serial monitor)!** + +**You should see:** + +``` +hello, world +hello, world +hello, world +hacky, world <-- OUR HACK! +``` + + **BOOM! WE DID IT!** + +You just modified a running program on real hardware! The processor executed code that was supposed to print "hello, world" but instead printed "hacky, world" because we hijacked the data it was using! + +--- + +## Part 14: Static Analysis with Ghidra - Understanding the Hack + +Now that we've performed the hack dynamically with GDB, let's use Ghidra to understand the same concepts through static analysis. This shows how you could plan such an attack without even connecting to the hardware! + +#### Opening the Project in Ghidra + +If you haven't already set up the Ghidra project from Week 1: + +1. Launch Ghidra +2. Select **File -> New Project** -> **Non-Shared Project** +3. Name it `0x0001_hello-world` +4. Drag and drop `0x0001_hello-world.elf` into the project +5. Double-click to open in CodeBrowser +6. Click **Yes** to auto-analyze + +##### Step 1: Navigate to Main + +1. In the **Symbol Tree** panel (left side), expand **Functions** +2. Find and click on `main` + +**What you'll see in the Listing View:** + +``` + ************************************************************* + * FUNCTION + ************************************************************* + int main (void ) + assume LRset = 0x0 + assume TMode = 0x1 + int r0:4 + main XREF[3]: Entry Point (*) , + _reset_handler:1000018c (c) , + .debug_frame::00000018 (*) + 0x0001_hello-world.c:4 (2) + 0x0001_hello-world.c:5 (2) + 10000234 08 b5 push {r3,lr} + 0x0001_hello-world.c:5 (4) + 10000236 01 f0 99 f9 bl stdio_init_all _Bool stdio_init_all(void) + LAB_1000023a XREF[1]: 10000240 (j) + 0x0001_hello-world.c:7 (6) + 0x0001_hello-world.c:8 (6) + 1000023a 02 48 ldr r0=>__EH_FRAME_BEGIN__ ,[DAT_10000244 ] = "hello, world\r" + = 100019CCh + 1000023c 01 f0 de f9 bl __wrap_puts int __wrap_puts(char * s) + 0x0001_hello-world.c:7 (8) + 10000240 fb e7 b LAB_1000023a + 10000242 00 ?? 00h + 10000243 bf ?? BFh + DAT_10000244 XREF[1]: main:1000023a (R) + 10000244 cc 19 00 10 undefine 100019CCh ? -> 100019cc +``` + +**What you'll see in the Decompile View:** + +```c +int main(void) + +{ + stdio_init_all(); + do { + __wrap_puts("hello, world\r"); + } while( true ); +} +``` + +##### Step 2: Identify the Attack Point + +In our GDB hack, we set a breakpoint at `0x1000023c` - right before `bl __wrap_puts`. Let's understand why this was the perfect attack point: + +**Click on address `0x1000023c` in the Listing view.** + +Notice: +- The instruction is `bl __wrap_puts` - a function call +- The previous instruction at `0x1000023a` loaded `r0` with the string address +- Ghidra shows `= "hello, world\r"` right in the listing! + +> **Key Insight:** Ghidra already tells us the string value! In the Listing, you can see `= "hello, world\r"` and `= 100019CCh`. This is the exact address we discovered through GDB! + +##### Step 3: Find the String in Memory + +Let's trace where the string actually lives: + +1. In the Listing view, look at address `0x1000023a`: + ``` + LAB_1000023a XREF[1]: 10000240 (j) + 0x0001_hello-world.c:7 (6) + 0x0001_hello-world.c:8 (6) + 1000023a 02 48 ldr r0=>__EH_FRAME_BEGIN__ ,[DAT_10000244 ] = "hello, world\r" + = 100019CCh + ``` + +2. **Double-click on `DAT_10000244`** to go to the data reference + +3. You'll see: + ``` + DAT_10000244 XREF[1]: main:1000023a (R) + 10000244 cc 19 00 10 undefine 100019CCh ? -> 100019cc + ``` + +4. **Double-click on `100019CCh`** to navigate to the actual string + +**You'll arrive at the string data:** + +``` + // + // .rodata + // SHT_PROGBITS [0x100019cc - 0x10001b17] + // ram:100019cc-ram:10001b17 + // + __init_array_end XREF[5]: Entry Point (*) , + __boot2_start__ frame_dummy:10000218 (*) , + __boot2_end__ main:1000023a (*) , + __EH_FRAME_BEGIN__ runtime_init:1000138a (R) , + _elfSectionHeaders::0000005c (*) + 100019cc 68 65 6c ds "hello, world\r" + 6c 6f 2c + 20 77 6f +``` + +##### Step 4: Understand Why We Needed SRAM + +Look at the string address: `0x100019cc` + +This starts with `0x10...` which means it's in **Flash memory (XIP region)**! + +| Address Range | Memory Type | Writable? | +| ------------- | ----------- | --------- | +| `0x10000000`+ | Flash (XIP) | **NO** - Read Only | +| `0x20000000`+ | SRAM | **YES** - Read/Write | + +> **This is why our direct string modification failed in GDB!** The string lives in flash memory, which is read-only at runtime. We had to create our malicious string in SRAM at a safe address (`0x20040000`) instead. + +##### Step 5: Examine Cross-References + +Ghidra's cross-reference feature shows everywhere a value is used: + +1. Navigate back to `main` (press **G**, type `main`, press Enter) +2. Click on `__wrap_puts` at address `0x1000023c` +3. Right-click and select **References -> Show References to __wrap_puts** + +This shows every place that calls `puts()`. In a larger program, you could find ALL the print statements and potentially modify any of them! + +##### Step 6: Use the Decompiler to Plan Attacks + +The Decompile view makes attack planning easy: + +```c +int main(void) + +{ + stdio_init_all(); + do { + __wrap_puts("hello, world\r"); + } while( true ); +} +``` + +From this view, you can immediately see: +- The program loops forever (`do { } while (true)`) +- It calls `__wrap_puts()` with a string argument +- To change the output, you need to change what's passed to `puts()` + +##### Step 7: Viewing the String in the .rodata Section + +When you navigate to the string address `0x100019cc`, you'll see the string stored in the `.rodata` (read-only data) section: + +``` + // + // .rodata + // SHT_PROGBITS [0x100019cc - 0x10001b17] + // ram:100019cc-ram:10001b17 + // + __init_array_end XREF[5]: Entry Point (*) , + __boot2_start__ frame_dummy:10000218 (*) , + __boot2_end__ main:1000023a (*) , + __EH_FRAME_BEGIN__ runtime_init:1000138a (R) , + _elfSectionHeaders::0000005c (*) + 100019cc 68 65 6c ds "hello, world\r" + 6c 6f 2c + 20 77 6f +``` + +This shows the raw bytes of our string: `68 65 6c 6c 6f 2c 20 77 6f...` which spell out "hello, world\r" in ASCII. + +##### Step 8: Patching Data in Ghidra (Preview) + +Ghidra allows you to modify data directly in the binary! Here's how to patch the string: + +1. **Navigate to the string** at address `0x100019cc` +2. **Right-click** on the string `"hello, world\r"` in the Listing view +3. **Select** **Patch Data** from the context menu +4. **Type** your new string: `"hacky, world\r"` +5. **Press Enter** to apply the patch + +> **Important:** The new string must be the **same length or shorter** than the original! If your new string is longer, it will overwrite adjacent data and likely crash the program. + +| Original String | Patched String | Result | +| --------------- | -------------- | ------ | +| `hello, world\r` (14 bytes) | `hacky, world\r` (14 bytes) | Works perfectly | +| `hello, world\r` (14 bytes) | `PWNED!\r` (7 bytes) | Works (shorter is OK) | +| `hello, world\r` (14 bytes) | `this is a much longer string\r` | Overwrites other data! | + +After patching, you'll see the change reflected in the Listing view: + +``` + 100019cc 68 61 63 ds "hacky, world\r" + 6b 79 2c + 20 77 6f +``` + +Notice how the bytes changed: `68 65 6c 6c 6f` ("hello") became `68 61 63 6b 79` ("hacky")! + +#### Looking Ahead: Persistent Binary Patching + +> **Coming in Future Lessons:** What we've done in Ghidra so far is just a **preview** of the patch - it modifies the data in Ghidra's view, but doesn't save it back to the actual binary file. + +In future lessons, we will learn how to: + +1. **Export the patched binary** from Ghidra to create a modified `.elf` or `.bin` file +2. **Flash the patched binary** to the Pico 2, making the hack **persistent** + +The key difference: + +| Technique | Persistence | When It's Useful | +| --------- | ----------- | ---------------- | +| **GDB Live Hacking** (this week) | Temporary - lost on reset | Testing, debugging, quick exploitation | +| **Ghidra Patch Preview** (this step) | None - just visualization | Planning and verifying patches | +| **Binary Patching** (future lessons) | **Permanent** - survives reboot | Persistent backdoors, firmware mods | + +This step helps you understand the mechanics of modifying binary data. Once you're comfortable with this concept, you'll be ready to create truly persistent modifications! + +#### Comparing GDB and Ghidra Approaches + +| Task | GDB (Dynamic) | Ghidra (Static) | +| ---- | ------------- | --------------- | +| Find main address | `x/1000i 0x10000000` + search | Symbol Tree -> Functions -> main | +| Find string address | Step through `ldr`, examine `$r0` | Click on `ldr` - shows `= 100019CCh` | +| See string content | `x/s $r0` | Double-click address -> see `ds "hello, world"` | +| Identify attack point | Set breakpoints, step, observe | Read decompiled code, find function calls | +| Verify memory type | Know address ranges | Check address prefix (`0x10...` vs `0x20...`) | + +#### Why Use Both Tools? + +- **Ghidra** helps you **plan** the attack by understanding code structure +- **GDB** lets you **execute** the attack and modify live values +- Together, they form a complete reverse engineering workflow! + +#### Ghidra Tips for Attack Planning + +1. **Use the Decompiler** - It shows you the high-level logic without decoding assembly +2. **Follow Cross-References** - Find all places a function or variable is used +3. **Check Address Ranges** - Quickly identify Flash vs SRAM locations +4. **Add Comments** - Press `;` to annotate what you discover for later +5. **Rename Variables** - Right-click -> Rename to give meaningful names + +--- + +## Part 15: Summary and Review + +#### What We Accomplished + +We successfully performed a **live memory injection attack**: + +1. **Connected** to a running embedded system using OpenOCD and GDB +2. **Analyzed** the program flow to find the perfect attack point +3. **Set a breakpoint** right before the critical function call +4. **Discovered** that direct string assignment doesn't work without `malloc()` +5. **Wrote** our malicious data directly to SRAM +6. **Hijacked** the `r0` register to point to our data +7. **Executed** the hack and watched the output change! + +#### Week 1 Concepts We Applied + +| Week 1 Concept | How We Used It This Week | +| -------------- | ------------------------ | +| Memory Layout (Flash vs RAM) | We knew flash is read-only, so we wrote to SRAM | +| Registers (`r0`) | We hijacked `r0` to point to our malicious string | +| GDB `x` command | We examined memory and verified our injected string | +| GDB breakpoints (`b`) | We set a strategic breakpoint before `puts()` | +| Disassembly (`disas`) | We found the exact instruction to target | +| Little-endian | We understood how our string bytes are stored | + +#### The Attack Flow Diagram + +``` +BEFORE OUR HACK: ++-----------------+ +------------------------------+ +| r0 = 0x100019cc| ---> | Flash: "hello, world\r" | ++-----------------+ +------------------------------+ + | + ▼ + puts() prints "hello, world" + +AFTER OUR HACK: ++-----------------+ +------------------------------+ +| r0 = 0x20040000| ---> | SRAM: "hacky, world" | ++-----------------+ +------------------------------+ + | + ▼ + puts() prints "hacky, world" +``` + +#### New GDB Commands We Learned + +| Command | What It Does | New/Review | +| ---------------------------- | ----------------------------------- | ---------- | +| `target extended-remote :3333` | Connect to OpenOCD debug server | **New** | +| `monitor reset halt` | Reset and halt the processor | **New** | +| `disas` | Disassemble the current function | Review | +| `x/Ni ADDRESS` | Examine N instructions at ADDRESS | Review | +| `x/s ADDRESS` | Examine memory as a string | Review | +| `b *ADDRESS` | Set breakpoint at exact address | Review | +| `c` | Continue execution | Review | +| `set $r0 = VALUE` | Change a register's value | **New** | +| `set {char[N]} ADDR = {...}` | Write characters directly to memory | **New** | + +#### Key Memory Addresses + +| Address | What's There | Read/Write? | +| ------------ | -------------------------------- | ----------- | +| `0x10000234` | Start of `main()` function | Read-only | +| `0x1000023c` | The `bl __wrap_puts` call | Read-only | +| `0x100019cc` | Original `"hello, world"` string | Read-only | +| `0x20040000` | Safe SRAM location (hack target) | Read-Write | + +--- + +--- + +## Key Takeaways + +#### Building on Week 1 + +1. **Memory layout knowledge is power** - Understanding that flash is read-only and SRAM is read-write was essential for our hack. This directly built on Week 1's memory map lesson. + +2. **Registers control everything** - In Week 1, we watched registers change during execution. This week, we CHANGED them ourselves to alter program behavior. + +3. **GDB is a hacking tool** - The same commands we used for learning (`x`, `b`, `c`, `disas`) are the same commands used for exploitation. + +#### New Concepts + +4. **Flash is Read-Only at Runtime** - You can't modify code or constant strings in flash memory while the program runs. You must use SRAM. + +5. **Bare-Metal Means No Runtime** - Without an operating system, there's no `malloc()`, no dynamic memory allocation. You have to manage memory manually. + +6. **Registers Are the Key** - Function arguments are passed in registers (`r0`, `r1`, etc.). By changing these registers at the right moment, you can change what functions do. + +7. **Timing is Everything** - We had to set our breakpoint at exactly the right instruction. One instruction earlier, and `r0` wouldn't be loaded yet. One instruction later, and `puts()` would already have the wrong address. + +8. **This is Real Hacking** - The techniques you learned today are used by security researchers, penetration testers, and yes, attackers. Understanding these attacks helps us build more secure systems. + +--- + +## Security Implications + +#### How Would This Work in the Real World? + +Imagine an attacker with physical access to an industrial control system: + +| Scenario | Attack | +| ---------------------- | ----------------------------------------------------------------- | +| **Nuclear Centrifuge** | Change the displayed RPM from dangerous (15,000) to safe (10,000) | +| **Medical Device** | Modify dosage readings to hide an overdose | +| **Vehicle ECU** | Alter speedometer reading while car actually speeds | +| **Smart Lock** | Change the "locked" status to "unlocked" | + +#### How Do We Defend Against This? + +1. **Disable Debug Ports** - Production devices should have JTAG/SWD disabled +2. **Secure Boot** - Verify firmware hasn't been tampered with +3. **Memory Protection** - Use ARM's MPU to restrict memory access +4. **Tamper Detection** - Hardware that detects physical intrusion +5. **Encryption** - Keep sensitive data encrypted in memory + +--- + +## Glossary + +#### New Terms This Week + +| Term | Definition | +| ------------------------- | ---------------------------------------------------------------------- | +| **Bare-Metal** | Programming directly on hardware without an operating system | +| **CMSIS-DAP** | A standard debug interface protocol for ARM processors | +| **Hijack** | Taking control of a value or flow that was intended for something else | +| **Hardware Breakpoint** | A breakpoint implemented in CPU hardware, works on any memory | +| **Memory Injection** | Writing attacker-controlled data into a program's memory space | +| **OpenOCD** | Open On-Chip Debugger - software that interfaces with debug hardware | +| **Register Manipulation** | Changing the values stored in CPU registers | +| **SRAM** | Static Random Access Memory - fast, volatile, read-write memory | +| **Software Breakpoint** | A breakpoint implemented by modifying code (requires writable memory) | + +#### Review Terms from Week 1 + +| Term | Definition | Where We Used It | +| ------------------- | --------------------------------------------------------- | ---------------- | +| **Breakpoint** | A marker that pauses program execution at a specific location | Part 7 - Setting the trap | +| **Register** | Fast storage inside the processor | Part 11 - Hijacking `r0` | +| **Stack Pointer** | Register that points to the top of the stack | Part 2 - Memory layout | +| **XIP** | Execute In Place - running code directly from flash | Part 2 - Why we can't write to flash | +| **Little-Endian** | Storing the least significant byte at the lowest address | Part 10 - String storage | + + + diff --git a/WEEK02/slides/WEEK02-IMG00.svg b/WEEK02/slides/WEEK02-IMG00.svg new file mode 100644 index 0000000..44adb6f --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 02 + + +Hello, World - Debugging and +Hacking Basics: Debugging and Hacking +a Basic Program for the Pico 2 + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK02/slides/WEEK02-IMG01.svg b/WEEK02/slides/WEEK02-IMG01.svg new file mode 100644 index 0000000..c48c43a --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG01.svg @@ -0,0 +1,73 @@ + + + + + +Live Hacking Overview +Introduction to Live Hacking + + + + What Is Live Hacking? + + + Modify a program + WHILE it is running + on real hardware + + + + The Train Analogy + Train heading to NYC + Switch the tracks + while it moves + Now it goes to LA! + + + + Why It Matters + Security research + Penetration testing + Malware analysis + Hardware debugging + + No recompile needed! + + + + This Week's Goal + + + Target Program + hello-world.c + Prints "hello, world" + in infinite loop + + + + Our Mission + Make it print + something ELSE + without changing + the source code + + + + Tools Used + GDB = live debug + OpenOCD = HW bridge + Ghidra = analysis + + Hack the running binary + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG02.svg b/WEEK02/slides/WEEK02-IMG02.svg new file mode 100644 index 0000000..718b3de --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG02.svg @@ -0,0 +1,85 @@ + + + + + +GDB Debug Session +GDB Fundamentals + + + + Setup Steps + + + + Step 1: Start OpenOCD + + openocd -s <scripts> + -f interface/cmsis-dap.cfg + -f target/rp2350.cfg + -c "adapter speed 5000" + + + Step 2: Launch GDB + + arm-none-eabi-gdb + build\0x0001_hello-world.elf + + + Step 3: Connect to target + + target extended-remote :3333 + + + Step 4: Reset + halt + + monitor reset halt + + + Step 5: Set breakpoint + + break main + + Then: continue (c) + + + + What Each Does + + + openocd + Loads probe + chip + config files + Then listens on :3333 + + + + arm-none-eabi-gdb + ARM debugger from + the embedded toolchain + + + + target extended-remote + GDB connects to + OpenOCD server + + + + monitor reset halt + Reset chip + stop + at very first instr + Clean starting state + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG03.svg b/WEEK02/slides/WEEK02-IMG03.svg new file mode 100644 index 0000000..3932205 --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG03.svg @@ -0,0 +1,88 @@ + + + + + +Breakpoints +GDB Breakpoint Types + + + + How They Work + + + + Normal Execution + + + MOV r0, #5 + + + MOV r1, #3 + + + BL printf + + + With Breakpoint + + + MOV r0, #5 + + + MOV r1, #3 + STOP + + + BL printf + paused + + CPU halts BEFORE + executing breakpoint + instruction + + Now you can inspect + + + + GDB Breakpoints + + + break main + Stop at function + By symbol name + + + + break *0x10000340 + Stop at exact addr + By hex address + + + + info break + List all active + breakpoints + + + + continue (c) + Resume running + until next break + + + + delete 1 + Remove breakpoint #1 + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG04.svg b/WEEK02/slides/WEEK02-IMG04.svg new file mode 100644 index 0000000..494fdda --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG04.svg @@ -0,0 +1,102 @@ + + + + + +Stack in Action +Runtime Stack Analysis + + + + Before Call + + + 0x20082000 + SP here + + + empty (0x20081FFC) + + + empty (0x20081FF8) + + + free stack space + + + unused lower space + + 0x20080000 + + Grows DOWN + + + + + + After PUSH + + + 0x20082000 + PUSH {r4, lr} + + + saved LR + + + saved r4 + + + free stack space + + + free stack space + + SP now = 0x20081FF8 + + SP moved down + by 8 bytes + GDB: x/4xw $sp + saved regs are now visible + + + + Key Points + + + PUSH saves + Preserves regs + before function + body runs + + + + POP restores + Puts values + back when func + returns + + + + Watch in GDB + x/4xw $sp + See stack data + + + + stepi + Step 1 instr + watch stack + change live + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG05.svg b/WEEK02/slides/WEEK02-IMG05.svg new file mode 100644 index 0000000..83ce46a --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG05.svg @@ -0,0 +1,79 @@ + + + + + +LDR Instruction +ARM Load Instructions + + + + How LDR Works + + + + Instruction: + + LDR r0, [pc, #12] + + + Step 1: Calculate addr + + addr = PC + 12 + + + Step 2: Read memory + + value = *(addr) + + + Step 3: Load into reg + + r0 = value + + + r0 now holds the + address of our + "hello, world" string + + + + Why It Matters + + + String Loading + printf needs addr + of string in r0 + r0 = first argument + + + + PC-Relative + Address computed + relative to current + PC position + Works from any addr + + + + The Attack Point + If we change r0 + AFTER the LDR + printf prints OUR + string instead! + + + + This is the hack! + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG06.svg b/WEEK02/slides/WEEK02-IMG06.svg new file mode 100644 index 0000000..52029b8 --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG06.svg @@ -0,0 +1,93 @@ + + + + + +The Attack Plan +Exploit Strategy + + + + Attack Flow (4 Steps) + + + + + 1. Break at + printf call + + + + + + + + 2. Write new + string to SRAM + + + + + + + + 3. Set r0 to + SRAM addr + + + + + + + + 4. Continue + execution + + printf reads r0, prints "hacky, world"! + + + + Normal Flow + + + + LDR r0, ="hello" + + + BL printf + + Output: + "hello, world" + + Prints original string + + + + Hacked Flow + + + + LDR r0, ="hello" + + + r0 = 0x20040000 + + + BL printf + + Output: + "hacky, world" + + Prints our string + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG07.svg b/WEEK02/slides/WEEK02-IMG07.svg new file mode 100644 index 0000000..984e441 --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG07.svg @@ -0,0 +1,80 @@ + + + + + +Failed vs Real Hack +Attack Methodology + + + + Failed Attempt + + + The Bad Idea + Set r0 to point + at a string literal + like "hacky" + + + + Why It Fails + r0 only holds a + 32-bit number + Not a string itself! + + + + set $r0 = "HACK" + GDB interprets this + as an address value + pointing to garbage + + + + Result: CRASH + or prints garbage + + + + Real Hack + + + The Right Way + 1. Write string + bytes to SRAM + 2. Point r0 to + that SRAM addr + + + + GDB Commands + + + set {char[13]}0x20040000 + + + = "hacky, world" + + + set $r0 = 0x20040000 + + + + String exists in + writable SRAM + r0 points to it + + "hacky, world" printed! + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG08.svg b/WEEK02/slides/WEEK02-IMG08.svg new file mode 100644 index 0000000..f9626eb --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG08.svg @@ -0,0 +1,83 @@ + + + + + +Writing to SRAM +Memory Manipulation + + + + SRAM at 0x20040000 + + + + Before (empty) + + + 00 00 00 00 00 00 00 00 + + + 00 00 00 00 00 00 00 00 + + + After writing + + + 68 61 63 6b 79 2c 20 77 + + h a c k y , w + + + GDB Command: + + + set {char[13]} + 0x20040000 = "hacky, world" + + + Verify with: + + x/s 0x20040000 + + + + Why SRAM? + + + SRAM = writable + RAM at 0x20000000 + We can write any + data here via GDB + + + + Flash = read-only + XIP at 0x10000000 + Cannot write to it + during execution + That's why we use RAM + + + + Choosing Address + 0x20040000 is safe + Far from stack + and heap regions + + + + Null terminator + \0 ends the string + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG09.svg b/WEEK02/slides/WEEK02-IMG09.svg new file mode 100644 index 0000000..4cf346e --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG09.svg @@ -0,0 +1,77 @@ + + + + + +Register Hijack +Control Flow Attack + + + + Before Hijack + + + r0 loaded by LDR: + + + r0 = 0x10001234 + + Points to flash: + + + "hello, world\r\n" + + printf will read r0 + and print that string + + + + The Hijack Command + + + set $r0 = 0x20040000 + + Now r0 points to + OUR string in SRAM + instead of flash + + + + After Hijack + + + r0 now contains: + + + r0 = 0x20040000 + + Points to SRAM: + + + "hacky, world" + + + + Then: continue + printf reads r0 + Follows pointer + to 0x20040000 + Finds "hacky, world" + Prints it! + + + + Output changed + without touching code + \ No newline at end of file diff --git a/WEEK02/slides/WEEK02-IMG10.svg b/WEEK02/slides/WEEK02-IMG10.svg new file mode 100644 index 0000000..3582678 --- /dev/null +++ b/WEEK02/slides/WEEK02-IMG10.svg @@ -0,0 +1,75 @@ + + + + + +GDB vs Ghidra +Static vs Dynamic Analysis + + + + GDB (Dynamic) + + + Live analysis + Program is running + on real hardware + + + + Capabilities + Set breakpoints + Read/write memory + Modify registers + Step instructions + Watch values change + + + + Best For + Live modification + Runtime behavior + Testing exploits + Verifying attacks + + Needs running target + + + + Ghidra (Static) + + + Offline analysis + Just the binary file + No hardware needed + + + + Capabilities + Disassembly view + Decompile to C + Find functions + Cross-references + String search + + + + Best For + Planning attacks + Understanding code + Finding targets + Mapping functions + + Works with just ELF + \ No newline at end of file diff --git a/WEEK03/WEEK03-SLIDES.pdf b/WEEK03/WEEK03-SLIDES.pdf new file mode 100644 index 0000000..3d7e59a Binary files /dev/null and b/WEEK03/WEEK03-SLIDES.pdf differ diff --git a/WEEK03/WEEK03.md b/WEEK03/WEEK03.md new file mode 100644 index 0000000..e8a67c7 --- /dev/null +++ b/WEEK03/WEEK03.md @@ -0,0 +1,1545 @@ +# Week 3: Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Understand how the RP2350 boots from the on-chip bootrom +- Know what the vector table is and why it's important +- Trace the complete boot sequence from power-on to `main()` +- Understand XIP (Execute In Place) and how code runs from flash +- Read and analyze the startup assembly code (`crt0.S`) +- Use GDB to examine the boot process step by step +- Use Ghidra to statically analyze the boot sequence +- Understand the difference between Thumb mode addressing and actual addresses + +## Review from Weeks 1-2 +This week builds on your GDB and Ghidra skills from previous weeks: +- **GDB Commands** (`x`, `b`, `c`, `si`, `disas`, `i r`) - We'll use all of these to trace the boot process +- **Memory Layout** (Flash at `0x10000000`, RAM at `0x20000000`) - Understanding where code and data live +- **Registers** (`r0`-`r12`, SP, LR, PC) - We'll watch how they're initialized during boot +- **Ghidra Analysis** - Decompiling and understanding assembly in a visual tool +- **Thumb Mode** - Remember addresses with LSB=1 indicate Thumb code + +--- + +## The Code We're Analyzing + +Throughout this week, we'll continue working with our `0x0001_hello-world.c` program: + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + + while (true) + printf("hello, world\r\n"); +} +``` + +But this week, we're going **deeper** - we'll understand everything that happens BEFORE `main()` even runs! How does the chip know where `main()` is? How does the stack get initialized? Let's find out! + +--- + +## Part 1: Understanding the Boot Process + +### What Happens When You Power On? + +When you plug in your Raspberry Pi Pico 2, a lot happens before your `main()` function runs! Think of it like waking up in the morning: + +1. **First, your alarm goes off** (Power is applied to the chip) +2. **You open your eyes** (The bootrom starts running) +3. **You check your phone** (The bootrom looks for valid code in flash) +4. **You get out of bed** (The bootrom jumps to your program) +5. **You brush your teeth, get dressed** (Startup code initializes everything) +6. **Finally, you start your day** (Your `main()` function runs!) + +Each of these steps has a corresponding piece of code. Let's explore them all! + +### The RP2350 Boot Sequence Overview + +``` ++-----------------------------------------------------------------+ +| STEP 1: Power On | +| - The Cortex-M33 core wakes up | +| - Execution begins at address 0x00000000 (Bootrom) | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| STEP 2: Bootrom Executes (32KB on-chip ROM) | +| - This code is burned into the chip - can't be changed! | +| - It looks for valid firmware in flash memory | +| - It scans the first 4 kB of the image for a valid IMAGE_DEF | +| (Datasheet §4.1, p. 338: 32KB ROM; §5.9.5, p. 429: IMAGE_DEF) | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| STEP 3: Flash XIP Setup (bootrom-managed) | +| - The bootrom configures the flash interface automatically | +| - Sets up XIP (Execute In Place) mode | +| - NOTE: Unlike RP2040, there is NO separate boot2 in flash! | +| (Datasheet §5.2, p. 375: "removal of a boot2 in the first | +| 256 bytes of the image") | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| STEP 4: Vector Table & Reset Handler | +| - Bootrom reads the vector table at 0x10000000 | +| - Gets the initial stack pointer from offset 0x00 | +| - Gets the reset handler address from offset 0x04 | +| - Jumps to the reset handler! | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| STEP 5: C Runtime Startup (crt0.S) | +| - Copies initialized data from flash to RAM | +| - Zeros out the BSS section | +| - Calls runtime_init() | +| - Finally calls main()! | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 2: The Bootrom - Where It All Begins + +### What is the Bootrom? + +The **bootrom** is a 32KB piece of code that is permanently burned into the RP2350 chip at the factory. You cannot change it - it's "mask ROM" (Read Only Memory). + +Think of the bootrom like the BIOS in your computer - it's the first thing that runs and is responsible for finding and loading your actual program. + +### Key Bootrom Facts + +| Property | Value | Description | +| ----------- | ------------- | ---------------------------------- | +| Size | 32 KB | Small but powerful | +| Location | `0x00000000` | The very first address in memory | +| Modifiable? | **NO** | Burned into silicon at the factory | +| Purpose | Boot the chip | Find and load your firmware | + +### What Does the Bootrom Do? + +1. **Initialize Hardware**: Sets up clocks, resets peripherals +2. **Check Boot Sources (Discovery)**: Scans configured boot sources (for this course: flash) to find a candidate firmware image region. +3. **Validate Firmware (Validation)**: Verifies that candidate by finding IMAGE_DEF start/end markers and parsing the block. +4. **Configure Flash**: Sets up the XIP interface +5. **Jump to Your Code**: Reads the vector table and jumps to your reset handler + +### The IMAGE_DEF Structure + +The bootrom looks for a special marker in your firmware called **IMAGE_DEF**. This tells the bootrom "Hey, there's valid code here!" + +Here's what it looks like in the Pico SDK: + +```assembly +.section .picobin_block, "a" // placed in flash +.word 0xffffded3 // PICOBIN_BLOCK_MARKER_START ↠ROM looks for this! +.byte 0x42 // PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE +.byte 0x1 // item is 1 word in size +.hword 0b0001000000100001 // SECURE mode (0x1021) +.byte 0xff // PICOBIN_BLOCK_ITEM_2BS_LAST +.hword 0x0001 // item is 1 word in size +.byte 0x0 // pad +.word 0x0 // relative pointer to next block (0 = loop to self) +.word 0xab123579 // PICOBIN_BLOCK_MARKER_END +``` + +**The magic numbers:** +- `0xffffded3` = Start marker ("I'm a valid Pico binary!") +- `0xab123579` = End marker ("End of the header block") + +### See This Exact Block in Your ELF (Commands + Real Output) + +Use these commands to view the IMAGE_DEF bytes directly in the ELF: + +```cmd +arm-none-eabi-objdump -s --start-address=0x1000013c --stop-address=0x10000150 build/0x0001_hello-world.elf +arm-none-eabi-objdump -s --start-address=0x10000130 --stop-address=0x10000154 build/0x0001_hello-world.elf +arm-none-eabi-gdb build/0x0001_hello-world.elf -ex "x/20bx 0x1000013c" -ex quit +``` + +Actual output from this lesson build: + +```text +build/0x0001_hello-world.elf: file format elf32-littlearm + +Contents of section .text: + 1000013c 42012110 ff010000 b01b0000 793512ab B.!.........y5.. + 1000014c 4ff00000 O... + +build/0x0001_hello-world.elf: file format elf32-littlearm + +Contents of section .text: + 10000130 a0010010 90a31ae7 d3deffff 42012110 ............B.!. + 10000140 ff010000 b01b0000 793512ab 4ff00000 ........y5..O... + 10000150 1e490860 .I.` +``` + +Command 1 explained (`--start-address=0x1000013c --stop-address=0x10000150`): +- Starts at `0x1000013c`, so it does **not** include the start marker at `0x10000138` (`d3deffff`). +- Shows IMAGE_DEF body fields and the end marker: + - `42012110` = `42 01 21 10` (item type/size + secure mode field) + - `ff010000` = last-item marker + size + pad + - `b01b0000` = next word in the block payload for this build + - `793512ab` = `PICOBIN_BLOCK_MARKER_END` (`0xab123579` in little-endian) +- `4ff00000` at `0x1000014c` is already the next instruction word after IMAGE_DEF. + +Command 2 explained (`--start-address=0x10000130 --stop-address=0x10000154`): +- Starts earlier, so it captures context **and** both IMAGE_DEF markers. +- `a0010010 90a31ae7` = binary-info context before IMAGE_DEF. +- `d3deffff` at `0x10000138` = `PICOBIN_BLOCK_MARKER_START`. +- `793512ab` at `0x10000148` = `PICOBIN_BLOCK_MARKER_END`. +- `4ff00000 1e490860` = code words after the IMAGE_DEF block. +- This command proves the full block location for this build: `0x10000138` to `0x1000014b`. + +Important: IMAGE_DEF offset can vary by build. In this build, the start marker `d3deffff` +is at `0x10000138` (not `0x1000013c`), so always search for the marker bytes instead of +assuming a fixed address. + +--- + +## Part 3: Understanding XIP (Execute In Place) + +> **REVIEW:** In Week 1, we learned that our code lives at `0x10000000` in flash memory. We used `x/1000i 0x10000000` to find our `main` function. Now we'll understand WHY code is at this address! + +### What is XIP? + +**XIP (Execute In Place)** means the processor can run code directly from flash memory without copying it to RAM first. + +Think of it like reading a book: +- **Without XIP**: You photocopy every page into a notebook, then read from the notebook +- **With XIP**: You just read directly from the book! + +### Why Use XIP? + +| Advantage | Explanation | +| ----------- | ------------------------------------------- | +| Saves RAM | Code stays in flash, RAM is free for data | +| Faster Boot | No need to copy entire program to RAM first | +| Simpler | Less memory management needed | + +### XIP Memory Address + +The XIP flash region starts at address `0x10000000`. This is where your compiled code lives! + +``` ++-----------------------------------------------------+ +| Address: 0x10000000 (XIP Base) | +| +-------------------------------------------------+| +| | Vector Table (first thing here!) || +| | - Stack Pointer at offset 0x00 || +| | - Reset Handler at offset 0x04 || +| | - Other exception handlers... || +| +-------------------------------------------------+| +| | Your Code || +| | - Reset handler || +| | - main() function || +| | - Other functions || +| +-------------------------------------------------+| +| | Read-Only Data || +| | - Strings like "hello, world" || +| | - Constant values || +| +-------------------------------------------------+| ++-----------------------------------------------------+ +``` + +--- + +## Part 4: The Vector Table - The CPU's Instruction Manual + +### What is the Vector Table? + +The **vector table** is a list of addresses at the very beginning of your program. It tells the CPU: +1. Where to set the stack pointer +2. Where to start executing code (reset handler) +3. Where to go when errors or interrupts happen + +Think of it like the table of contents in a book - it tells you where to find everything! + +### Vector Table Layout + +The vector table lives at `0x10000000` and looks like this: + +| Offset | Address | Content | Description | +| ------ | ------------ | ------------ | --------------------------- | +| `0x00` | `0x10000000` | `0x20082000` | Initial Stack Pointer (SP) | +| `0x04` | `0x10000004` | `0x1000015d` | Reset Handler (entry point) | +| `0x08` | `0x10000008` | `0x1000011b` | NMI Handler | +| `0x0C` | `0x1000000C` | `0x1000011d` | HardFault Handler | + +### Understanding Thumb Mode Addressing + +**Important Concept Alert!** + +Look at the reset handler address: `0x1000015d`. Notice it ends in `d` (an odd number)? + +On ARM Cortex-M processors, all code runs in **Thumb mode**. The processor uses the **least significant bit (LSB)** of an address to indicate this: + +| LSB | Mode | Meaning | +| ---------- | ----- | ----------------------------------------- | +| `1` (odd) | Thumb | "This is Thumb code" | +| `0` (even) | ARM | "This is ARM code" (not used on Cortex-M) | + +So `0x1000015d` means: +- The actual code is at `0x1000015c` (even address) +- The `+1` tells the processor "use Thumb mode" + +**GDB vs Ghidra:** +- GDB shows `0x1000015d` (with Thumb bit) +- Ghidra shows `0x1000015c` (actual instruction address) +- Both are correct! They're just displaying it differently. + +--- + +## Part 5: The Linker Script - Memory Mapping + +### What is a Linker Script? + +The **linker script** tells the compiler where to put different parts of your program in memory. It's like an architect's blueprint for memory! + +### Finding the Linker Script + +On Windows with the Pico SDK 2.2.0, you'll find it at: +``` +C:\Users\\.pico-sdk\sdk\2.2.0\src\rp2_common\pico_crt0\rp2350\memmap_default.ld +``` + +### Key Parts of the Linker Script + +```ld +MEMORY +{ + INCLUDE "pico_flash_region.ld" + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k + SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k +} +``` + +**What this means:** + +| Region | Start Address | Size | Purpose | +| --------- | ------------- | -------- | --------------------- | +| Flash | `0x10000000` | (varies) | Your code (XIP) | +| RAM | `0x20000000` | 512 KB | Main RAM | +| SCRATCH_X | `0x20080000` | 4 KB | Core 0 scratch memory | +| SCRATCH_Y | `0x20081000` | 4 KB | Core 0 stack | + +### Where Does the Stack Come From? + +The linker script calculates the initial stack pointer: + +```ld +__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); +``` + +Let's do the math: +- `ORIGIN(SCRATCH_Y)` = `0x20081000` +- `LENGTH(SCRATCH_Y)` = `0x1000` (4 KB) +- `__StackTop` = `0x20081000` + `0x1000` = **`0x20082000`** + +This value (`0x20082000`) is what we see at offset `0x00` in the vector table! + +--- + +## Part 6: Setting Up Your Environment (GDB - Dynamic Analysis) + +> **REVIEW:** This setup is identical to Weeks 1-2. If you need a refresher on OpenOCD and GDB connection, refer back to Week 1 Part 4 or Week 2 Part 5. + +### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board with debug probe connected +2. OpenOCD installed and configured +3. GDB (`arm-none-eabi-gdb`) installed +4. The "hello-world" binary loaded on your Pico 2 +5. Access to the Pico SDK source files (for reference) + +### Starting the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +**Terminal 2 - Start GDB:** + +```cmd +arm-none-eabi-gdb build\0x0001_hello-world.elf +``` + +**Connect to target:** + +```gdb +(gdb) target extended-remote :3333 +(gdb) monitor reset halt +``` + +--- + +## Part 7: Hands-On GDB Tutorial - Examining the Vector Table + +> **REVIEW:** We're using the same `x` (examine) command from Week 1. Remember: `x/Nx` shows N hex values, `x/Ni` shows N instructions, `x/s` shows strings. + +### Step 1: Examine the Vector Table + +Let's look at the first 4 entries of the vector table at `0x10000000`: + +**Type this command:** + +```gdb +(gdb) x/4x 0x10000000 +``` + +**What this command means:** +- `x` = examine memory (Week 1 review!) +- `/4x` = show 4 values in hexadecimal +- `0x10000000` = the address of the vector table + +**You should see:** + +``` +0x10000000 <__vectors>: 0x20082000 0x1000015d 0x1000011b 0x1000011d +``` + +### Step 2: Understanding What We See + +> **REVIEW:** In Weeks 1-2, we saw both `sp = 0x20082000` at the clean breakpoint at `main` and lower values like `0x20081fc8` or `0x20081ff8` after additional stack activity. Here we are looking at the *initial* stack pointer from the vector table before any code runs. + +Let's decode each value: + +| Address | Value | Meaning | +| ------------ | ------------ | ---------------------------------------- | +| `0x10000000` | `0x20082000` | Initial Stack Pointer - top of SCRATCH_Y | +| `0x10000004` | `0x1000015d` | Reset Handler + 1 (Thumb bit) | +| `0x10000008` | `0x1000011b` | NMI Handler + 1 (Thumb bit) | +| `0x1000000C` | `0x1000011d` | HardFault Handler + 1 (Thumb bit) | + +**Key Insight:** The stack pointer (`0x20082000`) is exactly what the linker script calculated! And all the handler addresses have their LSB set to `1` for Thumb mode. + +### Step 3: Verify the Stack Pointer Calculation + +Let's confirm our math by examining what's at `0x10000000`: + +**Type this command:** + +```gdb +(gdb) x/x 0x10000000 +``` + +**You should see:** + +``` +0x10000000 <__vectors>: 0x20082000 +``` + +This matches: +- `SCRATCH_Y` starts at `0x20081000` +- `SCRATCH_Y` is 4 KB (`0x1000` bytes) +- `0x20081000` + `0x1000` = `0x20082000` + +--- + +## Part 8: Examining the Reset Handler + +> **REVIEW:** We used `x/5i` extensively in Weeks 1-2 to examine our `main` function. Now we'll use the same technique to examine the code that runs BEFORE `main`! + +### Step 4: Disassemble the Reset Handler + +The reset handler is where execution begins after the bootrom hands off control. Let's look at it: + +**Type this command:** + +```gdb +(gdb) x/3i 0x1000015c +``` + +**Note:** We use `0x1000015c` (even) not `0x1000015d` (odd) because we want to see the actual instructions! + +**You should see:** + +``` + 0x1000015c <_reset_handler>: mov.w r0, #3489660928 @ 0xd0000000 + 0x10000160 <_reset_handler+4>: ldr r0, [r0, #0] + 0x10000162 <_reset_handler+6>: + cbz r0, 0x1000016a +``` + +### Step 5: Understanding the Reset Handler + +Let's break down what these first three instructions do: + +**Instruction 1: `mov.w r0, #0xd0000000`** + +This loads the address `0xd0000000` into register `r0`. But what's at that address? + +That's the **SIO (Single-cycle I/O) base address**! The SIO block contains a special register called **CPUID** that tells us which core we're running on. + +**Instruction 2: `ldr r0, [r0, #0]`** + +This reads the value at address `0xd0000000` (the CPUID register) into `r0`. + +| Core | CPUID Value | +| ------ | ----------- | +| Core 0 | `0` | +| Core 1 | `1` | + +**Instruction 3: `cbz r0, 0x1000016a`** + +This is "Compare and Branch if Zero". If `r0` is `0` (meaning we're on Core 0), branch to `0x1000016a` to continue with startup. Otherwise, we're on Core 1 and need to handle that differently. + +### Why Check Which Core We're On? + +The RP2350 has **two cores**, but only **Core 0** should run the startup code! If both cores tried to initialize the same memory and peripherals, chaos would ensue. + +So the reset handler checks: +- **Core 0?** -> Continue with startup +- **Core 1?** -> Go back to the bootrom and wait + +--- + +## Part 9: The Complete Reset Handler Flow + +### Step 6: Examine More of the Reset Handler + +Let's look at more instructions to see the full picture: + +**Type this command:** + +```gdb +(gdb) x/20i 0x1000015c +``` + +**You should see:** + +``` + 0x1000015c <_reset_handler>: mov.w r0, #3489660928 @ 0xd0000000 + 0x10000160 <_reset_handler+4>: ldr r0, [r0, #0] + 0x10000162 <_reset_handler+6>: + cbz r0, 0x1000016a + 0x10000164 : mov.w r0, #0 + 0x10000168 : + b.n 0x10000150 <_enter_vtable_in_r0> + 0x1000016a : + add r4, pc, #52 @ (adr r4, 0x100001a0 ) + 0x1000016c : ldmia r4!, {r1, r2, r3} + 0x1000016e : cmp r1, #0 + 0x10000170 : + beq.n 0x10000178 + 0x10000172 : + bl 0x1000019a + 0x10000176 : + b.n 0x1000016c + 0x10000178 : + ldr r1, [pc, #84] @ (0x100001d0 ) + 0x1000017a : + ldr r2, [pc, #88] @ (0x100001d4 ) + 0x1000017c : movs r0, #0 + 0x1000017e : + b.n 0x10000182 + 0x10000180 : stmia r1!, {r0} + 0x10000182 : cmp r1, r2 + 0x10000184 : bne.n 0x10000180 + 0x10000186 : + ldr r1, [pc, #80] @ (0x100001d8 ) + 0x10000188 : blx r1 +``` + +### Step 7: Understanding the Startup Phases + +The reset handler performs several phases: + +``` ++-----------------------------------------------------------------+ +| PHASE 1: Core Check (0x1000015c - 0x10000168) | +| - Check CPUID to see which core we're on | +| - If not Core 0, go back to bootrom | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| PHASE 2: Data Copy Setup & Loop (0x1000016a - 0x10000176) | +| - Set up the data_cpy_table pointer and load each copy triplet | +| - Copy initialized variables from flash to RAM | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| PHASE 3: BSS Setup & Clear (0x10000178 - 0x10000184) | +| - Load the BSS start/end addresses into r1 and r2 | +| - GDB labels those literals as `data_cpy_table+48/+52` | +| - Zero out all uninitialized global variables | ++-----------------------------------------------------------------+ + ↓ ++-----------------------------------------------------------------+ +| PHASE 4: Platform Entry Begins (0x10000186 - 0x10000188 shown) | +| - Load the runtime_init() pointer from the table | +| - Branch to runtime_init() with `blx r1` | +| - `main()` and `exit()` appear a few instructions later | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 10: Understanding the Data Copy Phase + +### What is the Data Copy Phase? + +> **REVIEW:** In Week 2, we learned that flash is read-only and SRAM is read-write. That's why the startup code must COPY initialized variables from flash to RAM - they can't be modified in flash! + +When you write C code like this: + +```c +int my_counter = 42; // Initialized global variable +``` + +The value `42` is stored in flash memory (because flash is non-volatile). But variables need to live in RAM to be modified! So the startup code **copies** these initial values from flash to RAM. + +### Step 8: Find the Data Copy Table + +The data copy table contains entries that describe what to copy where. Let's examine it: + +**Type this command:** + +```gdb +(gdb) x/12x 0x100001a0 +``` + +**You should see something like:** + +``` +0x100001a0 : 0x10001b4c 0x20000110 0x200002ac 0x10001ce8 +0x100001b0 : 0x20080000 0x20080000 0x10001ce8 0x20081000 +0x100001c0 : 0x20081000 0x00000000 0x00004770 0xe000ed08 +``` + +The data_cpy_table contains multiple entries. Each entry has three values: +1. **Source address** (in flash) +2. **Destination address** (in RAM) +3. **End address** (where to stop copying) + +In the output above, we see: +- **First entry**: `0x10001b4c` (source), `0x20000110` (dest), `0x200002ac` (end) +- **Second entry starts**: `0x10001ce8` (source of next entry), ... + +The table ends with an entry where the source address is `0x00000000` (which signals "no more entries"). + +### Step 9: Watch the Data Copy Loop + +The data copy loop works like this: + +``` ++---------------------------------------------+ +| 1. Load source, dest, end from table | +| 2. If source == 0, we're done | +| 3. Otherwise, copy word by word | +| 4. Go back to step 1 for next entry | ++---------------------------------------------+ +``` + +The actual code (starting at **`0x1000016c`** in the reset handler): + +```assembly +0x1000016c : ldmia r4!, {r1, r2, r3} +0x1000016e : cmp r1, #0 +0x10000170 : +beq.n 0x10000178 +0x10000172 : +bl 0x1000019a +0x10000176 : +b.n 0x1000016c +``` + +> Tip: **Note:** You can see this code in **Step 6** earlier where we examined the reset handler with `x/20i 0x1000015c`. + +--- + +## Part 11: Understanding the BSS Clear Phase + +### What is BSS? + +**BSS** stands for "Block Started by Symbol" (historical name). It's the section of memory for **uninitialized global variables**. + +When you write: + +```c +int my_counter; // Uninitialized - will be in BSS +``` + +The C standard says this variable **must start at zero**. The BSS clear phase zeros out this entire region. + +### Step 10: Examine the BSS Clear Loop + +**Type this command:** + +```gdb +(gdb) x/5i 0x10000178 +``` + +**You should see:** + +``` +0x10000178 : +ldr r1, [pc, #84] @ (0x100001d0 ) +0x1000017a : +ldr r2, [pc, #88] @ (0x100001d4 ) +0x1000017c : movs r0, #0 +0x1000017e : +b.n 0x10000182 +0x10000180 : stmia r1!, {r0} +``` + +The first two `ldr` instructions are still part of the **BSS clear setup**, even though GDB shows the source words as `data_cpy_table+48` and `data_cpy_table+52`. That label means the two literal words live in the same nearby constant block as the copy-table entries; it does **not** mean the code is still performing `.data` copies. At this point, `r1` becomes the BSS start address, `r2` becomes the BSS end address, and the loop beginning at `0x10000180` zeros that range. + +### Understanding the Loop + +``` ++---------------------------------------------+ +| r1 = start of BSS section | +| r2 = end of BSS section | +| r0 = 0 | +| | +| LOOP: | +| Store 0 at address r1 | +| Increment r1 by 4 bytes | +| If r1 != r2, repeat | ++---------------------------------------------+ +``` + +--- + +## Part 12: Examining Exception Handlers + +### Step 11: Look at the Default Exception Handlers + +What happens if an exception occurs (like a HardFault)? Let's look: + +**Type this command:** + +```gdb +(gdb) x/10i 0x10000110 +``` + +**You should see:** + +``` +0x10000110 : mrs r0, IPSR +0x10000114 : subs r0, #16 +0x10000116 : bkpt 0x0000 +0x10000118 : bkpt 0x0000 +0x1000011a : bkpt 0x0000 +0x1000011c : bkpt 0x0000 +0x1000011e : bkpt 0x0000 +0x10000120 : bkpt 0x0000 +0x10000122 : bkpt 0x0000 +0x10000124 <__default_isrs_end>: + @ instruction: 0xebf27188 +``` + +### What is `bkpt`? + +The `bkpt` instruction is a **breakpoint**. When executed, it stops the processor and triggers the debugger! + +These are the **default** exception handlers - they just stop the program so you can debug. In your own code, you can override these with real handlers. + +### Why So Many Handlers? + +Each type of exception has its own handler: + +| Handler | Purpose | +| --------------- | ------------------------------------------ | +| `isr_nmi` | Non-Maskable Interrupt (can't be disabled) | +| `isr_hardfault` | Serious error (bad memory access, etc.) | +| `isr_svcall` | Supervisor Call (used by RTOSes) | +| `isr_pendsv` | Pendable Supervisor (also for RTOSes) | +| `isr_systick` | System Timer tick interrupt | + +--- + +## Part 13: Finding Where Main is Called + +### Step 12: Look at Platform Entry + +After all the setup, the code finally calls `main()`. Let's find it: + +**Type this command:** + +```gdb +(gdb) x/10i 0x10000186 +``` + +**You should see:** + +``` +0x10000186 : +ldr r1, [pc, #80] @ (0x100001d8 ) +0x10000188 : blx r1 +0x1000018a : +ldr r1, [pc, #80] @ (0x100001dc ) +0x1000018c : blx r1 +0x1000018e : +ldr r1, [pc, #80] @ (0x100001e0 ) +0x10000190 : blx r1 +0x10000192 : bkpt 0x0000 +0x10000194 : +b.n 0x10000192 +0x10000196 : ldmia r1!, {r0} +0x10000198 : stmia r2!, {r0} +``` + +### Understanding Platform Entry + +The platform entry code makes **three function calls** using `ldr` + `blx`: + +1. **First call**: `runtime_init()` - SDK initialization +2. **Second call**: `main()` - YOUR CODE! +3. **Third call**: `exit()` - Called when main returns + +After `main()` returns, `exit()` is called to handle cleanup. The `bkpt` instruction after `exit()` should never be reached - it's there to catch errors if `exit()` somehow returns. + +### Step 13: Set a Breakpoint at Main + +> **REVIEW:** We've used `b main` and `b *ADDRESS` many times in Weeks 1-2. This is the same technique! + +Let's verify we understand the boot process by setting a breakpoint at main: + +**Type this command:** + +```gdb +(gdb) b main +``` + +**You should see:** + +``` +Breakpoint 1 at 0x10000234: file C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c, line 5. +Note: automatically using hardware breakpoints for read-only addresses. +``` + +**Now continue:** + +```gdb +(gdb) c +``` + +**You should see:** + +``` +Continuing. + +Thread 1 "rp2350.cm0" hit Breakpoint 1, main () + at C:/Users/assem.KEVINTHOMAS/OneDrive/Documents/Embedded-Hacking/0x0001_hello-world/0x0001_hello-world.c:5 +5 stdio_init_all(); +(gdb) +``` + + We've traced the entire boot process from power-on to `main()`! + +--- + +## Part 14: Understanding the Binary Info Header + +### Step 14: Examine the Binary Info Header + +Between the default ISRs and the reset handler, there's a special data structure called the **binary info header**. Let's look at it: + +**Type this command:** + +```gdb +(gdb) x/5x 0x10000138 +``` + +**You should see:** + +``` +0x10000138 <__binary_info_header_end>: 0xffffded3 0x10210142 0x000001ff 0x00001bb0 +0x10000148 <__binary_info_header_end+16>: 0xab123579 +``` + +### Decoding the Binary Info Header + +| Address | Value | Meaning | +| ------------ | ------------ | ----------------------------------------- | +| `0x10000138` | `0xffffded3` | Start marker (PICOBIN_BLOCK_MARKER_START) | +| `0x1000013c` | `0x10212142` | Image type descriptor | +| `0x10000140` | `0x000001ff` | Item header/size field | +| `0x10000144` | `0x00001bb0` | Link to next block or data | +| `0x10000148` | `0xab123579` | End marker (PICOBIN_BLOCK_MARKER_END) | + +**Why does GDB show this as instructions?** + +GDB doesn't know this is data, not code! It tries to disassemble it as Thumb instructions, which results in nonsense. This is why you'll see things like: + +```gdb +(gdb) x/i 0x10000138 +``` + +``` + 0x10000138 <__binary_info_header_end>: udf #211 @ 0xd3 +``` + +That's not real code - it's the magic number `0xffffded3` being misinterpreted! + +--- + +## Part 15: Static Analysis with Ghidra - Examining the Boot Sequence + +> **REVIEW:** In Week 1, we set up a Ghidra project and analyzed our hello-world binary. Now we'll use Ghidra to understand the boot sequence from a static analysis perspective! + +### Why Use Ghidra for Boot Analysis? + +While GDB is excellent for dynamic analysis (watching code execute), Ghidra excels at: +- **Seeing the big picture** - Understanding code flow without running it +- **Cross-references** - Finding all places that call a function +- **Decompilation** - Seeing C-like code even for assembly routines +- **Annotation** - Adding notes and renaming functions for clarity + +### Step 15: Open Your Project in Ghidra + +> **REVIEW:** If you haven't created the project yet, refer back to Week 1 Part 5 for setup instructions. + +1. Launch Ghidra and open your `0x0001_hello-world` project +2. Double-click on the `.elf` file to open it in the CodeBrowser +3. If prompted to auto-analyze, click **Yes** + +### Step 16: Navigate to the Vector Table + +1. In the **Navigation** menu, select **Go To...** +2. Type `0x10000000` and press Enter +3. You should see the vector table data + +**What you'll see in the Listing view:** + +``` + // + // .text + // SHT_PROGBITS [0x10000000 - 0x100019cb] + // ram:10000000-ram:100019cb + // + assume spsr = 0x0 (Default) + __vectors XREF[4]: Entry Point (*) , + __flash_binary_start runtime_init_install_ram_vector_ + __VECTOR_TABLE _elfProgramHeaders::00000028 (*) , + __logical_binary_start _elfSectionHeaders::00000034 (*) + 10000000 00 undefine 00h + 10000001 20 ?? 20h + 10000002 08 ?? 08h + 10000003 20 ?? 20h + 10000004 5d ?? 5Dh ] ? -> 1000015d + 10000005 01 ?? 01h + 10000006 00 ?? 00h + 10000007 10 ?? 10h + 10000008 1b ?? 1Bh ? -> 1000011b + 10000009 01 ?? 01h + 1000000a 00 ?? 00h + 1000000b 10 ?? 10h + 1000000c 1d ?? 1Dh ? -> 1000011d + 1000000d 01 ?? 01h + 1000000e 00 ?? 00h + 1000000f 10 ?? 10h +... +``` + +> Tip: **Notice:** Ghidra shows the vector table data as individual bytes by default. You can see it has labeled the start as `__vectors`, `__flash_binary_start`, `__VECTOR_TABLE`, and `__logical_binary_start`. The arrows (like `? -> 1000015d`) show that Ghidra recognizes these bytes as pointers to code addresses! To see the data formatted as 32-bit addresses instead of bytes, you can right-click and retype the data. + +### Step 17: Navigate to the Reset Handler + +1. In the Symbol Tree panel (left side), expand **Functions** +2. Find and click on `_reset_handler` (or search for it) +3. Alternatively, double-click on `_reset_handler` in the vector table listing + +**What you'll see in the Decompile view (right panel):** + +Ghidra will show you a decompiled version of the reset handler. While it won't be perfect C code (since this is hand-written assembly), it helps visualize the flow: + +```c +void _reset_handler(void) + +{ + bool bVar1; + undefined4 uVar2; + int iVar3; + undefined4 *puVar4; + int *piVar5; + int *piVar6; + int *piVar7; + + if (_DAT_d0000000 != 0) { + _DAT_e000ed08 = 0; + bVar1 = (bool)isCurrentModePrivileged(); + if (bVar1) { + setMainStackPointer(_gpio_set_function_masked64); + } + /* WARNING: Could not recover jumptable at 0x1000015a. Too many branches */ + /* WARNING: Treating indirect jump as call */ + (*pcRam00000004)(8,_gpio_set_function_masked64); + return; + } + piVar5 = &data_cpy_table; + uVar2 = 0; + while( true ) { + iVar3 = *piVar5; + piVar6 = piVar5 + 1; + piVar7 = piVar5 + 2; + piVar5 = piVar5 + 3; + if (iVar3 == 0) break; + uVar2 = data_cpy(uVar2,iVar3,*piVar6,*piVar7); + } + for (puVar4 = (undefined4 *)&__TMC_END__; puVar4 != (undefined4 *)&end; puVar4 = puVar4 + 1) { + *puVar4 = 0; + } + runtime_init(); + iVar3 = main(); + /* WARNING: Subroutine does not return */ + exit(iVar3); +} +``` + +### Step 18: Trace the Path to Main + +Let's find how the boot code eventually calls `main()`: + +1. In the Symbol Tree, find the `main` function +2. Right-click on `main` and select **References -> Show References to main** +3. This shows everywhere `main` is called from! + +**You should see:** + +| Location | Type | Label | +| ------------------------- | ---- | ------------------ | +| `1000018c` | CALL | `blx r1` (to main) | + +4. Double-click on the reference to jump to `1000018c` + +### Step 19: Examine Platform Entry + +In Ghidra, look at `platform_entry`: + +**Listing View:** +``` + platform_entry + crt0.S:512 (2) + 10000186 14 49 ldr r1,[DAT_100001d8 ] = 1000137Dh + crt0.S:513 (2) + 10000188 88 47 blx r1=>runtime_init void runtime_init(void) + crt0.S:514 (2) + 1000018a 14 49 ldr r1,[DAT_100001dc ] = 10000235h + crt0.S:515 (2) + 1000018c 88 47 blx r1=>main int main(void) + crt0.S:516 (2) + 1000018e 14 49 ldr r1,[DAT_100001e0 ] = 10001375h + crt0.S:517 (2) + 10000190 88 47 blx r1=>exit void exit(int status) + LAB_10000192 XREF[1]: 10000194 (j) + crt0.S:521 (2) + 10000192 00 be bkpt 0x0 + crt0.S:522 (2) + 10000194 fd e7 b LAB_10000192 +``` + +> **Key Insight:** Ghidra's decompiler makes the boot sequence crystal clear! You can see exactly what functions are called before `main()`. + +### Step 20: Create a Boot Sequence Graph + +Ghidra can visualize the call flow: + +1. With `_reset_handler` selected, go to **Window -> Function Call Graph** +2. This shows a visual graph of all function calls from the reset handler +3. You will see `_reset_handler` at the top with arrows going down to its four direct callees: `data_cpy`, `runtime_init`, `main`, and `exit` + +### Comparing GDB and Ghidra for Boot Analysis + +| Aspect | GDB (Dynamic) | Ghidra (Static) | +| ------ | ------------- | --------------- | +| **Sees runtime values** | Yes - register contents, memory | No - must infer from code | +| **Needs hardware** | Yes - Pico 2 must be connected | No - works offline | +| **Shows code flow** | Step-by-step execution | Full graph visualization | +| **Best for** | Watching what happens | Understanding structure | +| **Thumb bit handling** | Shows with +1 (0x1000015d) | Shows actual addr (0x1000015c) | + +### Ghidra Tips for Boot Analysis + +1. **Rename functions** - Right-click and rename unclear labels for future reference +2. **Add comments** - Press `;` to add inline comments explaining code +3. **Set data types** - Help Ghidra understand structures like the vector table +4. **Use bookmarks** - Mark important locations with **Ctrl+D** + +--- + +## Part 16: Summary and Review + +### The Complete Boot Sequence + +``` ++-----------------------------------------------------------------+ +| 1. POWER ON | +| Cortex-M33 begins at 0x00000000 (bootrom) | ++-----------------------------------------------------------------+ +| 2. BOOTROM | +| - Initializes hardware | +| - Configures flash XIP (no separate boot2 on RP2350) | +| - Finds IMAGE_DEF within first 4 kB of flash image | ++-----------------------------------------------------------------+ +| 3. VECTOR TABLE (0x10000000) | +| - Reads SP from offset 0x00 -> 0x20082000 | +| - Reads Reset Handler from offset 0x04 -> 0x1000015d | ++-----------------------------------------------------------------+ +| 4. RESET HANDLER (0x1000015c) | +| - Checks CPUID (Core 0 continues, Core 1 waits) | +| - Copies .data from flash to RAM | +| - Zeros .bss section | ++-----------------------------------------------------------------+ +| 5. PLATFORM ENTRY (0x10000186) | +| - Calls runtime_init() | +| - Calls main() | +| - Calls exit() when main returns | ++-----------------------------------------------------------------+ +| 6. YOUR CODE RUNS! | +| main() at 0x10000234 | ++-----------------------------------------------------------------+ +``` + +### Key Addresses to Remember + +| Address | What's There | +| ------------ | ---------------------------------------- | +| `0x00000000` | Bootrom (32KB, read-only) | +| `0x10000000` | Vector table / XIP flash start | +| `0x1000015c` | Reset handler (`_reset_handler`) | +| `0x10000234` | Your `main()` function | +| `0x20000000` | Start of RAM | +| `0x20082000` | Initial stack pointer (top of SCRATCH_Y) | +| `0xd0000000` | SIO base (CPUID register) | + +### Weeks 1-2 Concepts We Applied + +| Previous Concept | How We Used It This Week | +| ---------------- | ------------------------ | +| Memory Layout (Flash/RAM) | Understood why data must be copied from flash to RAM | +| GDB `x` command | Examined vector table, reset handler, and boot code | +| Breakpoints (`b`) | Set breakpoints to trace the boot sequence | +| Thumb Mode Addresses | Recognized LSB=1 means Thumb code in vector table | +| Stack Pointer | Saw how SP is initialized from the vector table | +| Ghidra Analysis | Used decompiler to understand boot flow | + +### GDB Commands Reference + +| Command | What It Does | New/Review | +| ---------------- | --------------------------------- | ---------- | +| `x/Nx ADDRESS` | Examine N hex values at ADDRESS | Review | +| `x/Ni ADDRESS` | Examine N instructions at ADDRESS | Review | +| `b main` | Set breakpoint at main function | Review | +| `b *ADDRESS` | Set breakpoint at exact address | Review | +| `si` | Step one instruction | Review | +| `c` | Continue execution | Review | +| `info registers` | Show all register values | Review | +| `monitor reset halt` | Reset and halt the target | Review | + +### Key Concepts + +| Concept | Definition | +| ---------------- | ----------------------------------------------------- | +| **Bootrom** | 32KB factory-programmed ROM that initializes the chip | +| **Vector Table** | List of addresses for SP and exception handlers | +| **XIP** | Execute In Place - running code directly from flash | +| **Thumb Mode** | ARM's compact instruction set (LSB=1 in addresses) | +| **BSS** | Section for uninitialized globals (must be zeroed) | +| **crt0.S** | C Runtime startup assembly file | +| **Reset Handler**| First function called after power-on/reset | +| **CPUID** | Register identifying which CPU core is executing | + +### Ghidra Actions We Used + +| Action | How to Access | Purpose | +| ------ | ------------- | ------- | +| Go To Address | Navigation -> Go To... | Jump to specific memory address | +| Show References | Right-click -> References -> Show References to | Find all callers of a function | +| Function Call Graph | Window -> Function Call Graph | Visualize call flow | +| Add Comment | Press `;` | Document your analysis | +| Rename Symbol | Right-click -> Rename | Give meaningful names to functions | + +--- + +--- + +## Key Takeaways + +### Building on Weeks 1-2 + +1. **GDB skills compound** - The `x`, `b`, `si`, and `disas` commands you learned in Weeks 1-2 are essential for understanding the boot process. Each week adds new applications for the same core skills. + +2. **Memory layout is fundamental** - Understanding flash vs RAM from Week 2 explains why startup code must copy data and zero BSS. + +3. **Ghidra complements GDB** - Dynamic analysis (GDB) shows what happens at runtime; static analysis (Ghidra) reveals the overall structure. Use both together! + +### New Concepts This Week + +4. **The boot process is deterministic** - Every RP2350 boots the same way, and understanding this helps you debug startup problems. + +5. **The bootrom can't be changed** - It's burned into silicon. Security features depend on this immutability. + +6. **The vector table is critical** - It tells the CPU where to start and how to handle errors. + +7. **Thumb mode uses the LSB** - Address `0x1000015d` means "run Thumb code at `0x1000015c`". + +8. **Startup code does essential work** - Copying data, zeroing BSS, and initializing the runtime all happen before `main()`. + +9. **Only Core 0 runs startup** - Core 1 waits in the bootrom until explicitly started. + +--- + +## Security Implications + +### How Boot Sequence Knowledge Applies to Security + +Understanding the boot process is critical for both attackers and defenders. Knowledge of how the RP2350 boots reveals potential attack vectors and defense strategies. + +#### Attack Scenarios + +| Scenario | Attack | Boot Process Knowledge Required | +| -------- | ------ | ------------------------------- | +| **Firmware Replacement** | Replace the entire flash image with malicious firmware | Understanding IMAGE_DEF structure and how bootrom validates firmware | +| **Vector Table Hijacking** | Modify the reset handler address to point to malicious code | Knowing the vector table location at `0x10000000` | +| **Bootrom Exploitation** | Find bugs in the immutable bootrom to bypass security | Understanding bootrom behavior and sequence | +| **Debug Port Attack** | Use SWD/JTAG to dump firmware or inject code | Knowledge of how to halt and examine the boot process | +| **Startup Code Modification** | Change how data is copied or BSS is cleared | Understanding crt0 and runtime_init sequences | + +#### Real-World Applications + +**Industrial Control Systems:** +- An attacker with physical access could replace firmware to hide malicious behavior +- Understanding the boot sequence helps identify the earliest point where security checks can be added + +**IoT Devices:** +- Compromised boot code could establish backdoors before the main application runs +- Secure boot implementations verify the vector table and reset handler integrity + +**Medical Devices:** +- Boot-time attacks could modify critical safety parameters before device operation +- Understanding initialization helps implement tamper detection + +### Defense Strategies + +#### 1. Secure Boot Implementation + +``` ++-----------------------------------------------------+ +| SECURE BOOT FLOW | ++-----------------------------------------------------+ +| Bootrom (immutable) | +| ↓ | +| Verify IMAGE_DEF signature | +| ↓ | +| Verify application image signature | +| ↓ | +| If all valid: Jump to reset handler | +| If any invalid: Refuse to boot | ++-----------------------------------------------------+ +``` + +**Implementation:** Use cryptographic signatures to verify each boot stage before execution. + +#### 2. Debug Port Protection + +- **Production devices:** Permanently disable SWD/JTAG in final products +- **Debug authentication:** Require cryptographic challenge-response before allowing debug access +- **Fuses:** Blow hardware fuses to disable debug ports permanently + +#### 3. Flash Protection + +- **Read protection:** Enable flash read protection to prevent dumping firmware +- **Write protection:** Make critical boot sectors write-protected after initial programming +- **Encrypted storage:** Store firmware encrypted in flash + +#### 4. Memory Protection Unit (MPU) + +Configure the Cortex-M33's MPU to: +- Mark code regions as execute-only (no reading code as data) +- Separate privileged and unprivileged memory regions +- Prevent execution from RAM regions (defend against code injection) + +#### 5. Boot-Time Integrity Checks + +```c +// Early in reset handler or runtime_init +void verify_boot_integrity(void) { + // Check vector table hasn't been modified + uint32_t vector_table_checksum = calculate_checksum(0x10000000, VECTOR_TABLE_SIZE); + if (vector_table_checksum != EXPECTED_CHECKSUM) { + // Vector table tampered - refuse to boot + secure_halt(); + } + + // Check critical data structures + // Verify stack pointer is in valid range + // etc. +} +``` + +#### 6. Anti-Tampering Hardware + +- **Tamper detection:** Sensors that detect case opening or voltage glitching +- **Response actions:** Erase sensitive keys, refuse to boot, or alert monitoring systems +- **Secure elements:** Store cryptographic keys in separate tamper-resistant chips + +### Lessons for Defenders + +1. **The bootrom is your trust anchor** - Its immutability makes it the foundation of security. RP2350's secure boot features leverage this. + +2. **Early is critical** - Security checks in the reset handler or runtime_init run before any application code, making them harder to bypass. + +3. **Defense in depth** - Multiple layers (hardware fuses, encrypted storage, secure boot, MPU) make attacks much harder. + +4. **Physical access = game over** - If an attacker can connect a debug probe, they can potentially compromise the device. Physical security matters! + +5. **Know your boot sequence** - Understanding exactly what runs when helps you identify where to add security checks and what assets need protection. + +### Security Research Value + +For security researchers and penetration testers, boot sequence analysis helps: + +- **Find vulnerabilities:** Many security bugs exist in startup code that runs before normal security checks +- **Develop exploits:** Understanding memory layout and initialization is essential for exploit development +- **Assess attack surface:** Knowing what's accessible at boot time reveals potential attack vectors +- **Build better defenses:** You can't defend what you don't understand + +> **"To know your enemy, you must become your enemy."** - Sun Tzu + +Understanding how an attacker would analyze and exploit the boot sequence is essential for building robust defenses. + +--- + +## Glossary + +### New Terms This Week + +| Term | Definition | +| ----------------- | ----------------------------------------------------------------------- | +| **Bootrom** | Factory-programmed ROM containing first-stage bootloader | +| **BSS** | Block Started by Symbol - section for uninitialized global variables | +| **CPUID** | Register that identifies which CPU core is executing | +| **crt0** | C Runtime Zero - the startup code that runs before main | +| **IMAGE_DEF** | Structure that marks valid firmware for the bootrom | +| **Linker Script** | File that defines memory layout for the compiled program | +| **Reset Handler** | First function called after reset/power-on | +| **Thumb Mode** | Compact instruction encoding used by Cortex-M | +| **Vector Table** | Array of addresses for stack pointer and exception handlers | +| **VTOR** | Vector Table Offset Register - tells CPU where to find the vector table | +| **XIP** | Execute In Place - running code directly from flash memory | + +### Review Terms from Weeks 1-2 + +| Term | Definition | How We Used It | +| ---- | ---------- | -------------- | +| **Breakpoint** | Marker that pauses program execution | Set at reset handler and main | +| **Register** | Fast storage inside the processor | Watched SP, LR, PC during boot | +| **Stack Pointer** | Register pointing to top of stack | Saw initial value in vector table | +| **Flash Memory** | Read-only storage for code | Contains vector table and boot code | +| **SRAM** | Read-write memory for data | Where stack and variables live | + +--- + +## Additional Resources + +### RP2350 Datasheet + +For more details on the boot process, see Chapter 5 of the RP2350 Datasheet: +https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf + +### Pico SDK Source Code + +The startup code lives in: +- `crt0.S` - Main startup assembly (vector table at `.section .vectors`, reset handler, data copy, BSS clear, platform_entry) +- `memmap_default.ld` - Default linker script (section ordering: `.vectors` -> `.binary_info_header` -> `.embedded_block` -> `.reset`) +- `embedded_start_block.inc.S` - IMAGE_DEF block (replaces RP2040's `boot2_generic_03h.S`) + +> **Note:** The RP2040 used a `boot2_generic_03h.S` second-stage bootloader occupying the first 256 bytes of flash. The RP2350 eliminated this; the bootrom handles flash XIP setup directly. The SDK still includes a `boot2` mechanism for compatibility, but it is **not** placed at flash address 0 - it is embedded in the data copy table and executed from the stack during startup. + +### Bootrom Source + +The bootrom source is available at: +https://github.com/raspberrypi/pico-bootrom-rp2350 + +--- + +## Part 17: Proving the Boot Sequence with objdump + +Everything we have learned about the boot sequence can be proven directly from the compiled ELF binary using `arm-none-eabi-objdump`. The bootrom is not in your ELF (it is mask ROM burned into the chip at `0x00000000`), but everything your firmware provides - the vector table, the IMAGE_DEF, and the reset handler - lives in your ELF starting at `0x10000000`. + +### Step 1: List All Sections + +```bash +arm-none-eabi-objdump -h build/0x0001_hello-world.elf +``` + +Expected output (key sections): + +``` +Idx Name Size VMA LMA + 0 .text 000019cc 10000000 10000000 + 3 .binary_info 0000002c 10001b20 10001b20 + 4 .ram_vector_table 00000110 20000000 20000000 + 6 .data 0000019c 20000110 10001b4c +``` + +> Tip: The Pico SDK merges `.vectors`, `.embedded_block`, and `.reset` all into `.text` at `0x10000000`. They are not separate named ELF sections - they are sub-regions inside `.text`. + +### Step 2: Dump the First 0x150 Bytes of Flash - One Command, Zero Skips + +```bash +arm-none-eabi-objdump -s --start-address=0x10000000 --stop-address=0x10000150 build/0x0001_hello-world.elf +``` + +Raw output from that command: + +``` + 10000000 00200820 5d010010 1b010010 1d010010 . . ]........... + 10000010 11010010 11010010 11010010 11010010 ................ + 10000020 11010010 11010010 11010010 11010010 ................ + 10000030 11010010 11010010 11010010 11010010 ................ + 10000040 11010010 11010010 11010010 11010010 ................ + 10000050 11010010 11010010 11010010 11010010 ................ + 10000060 11010010 11010010 11010010 11010010 ................ + 10000070 11010010 11010010 11010010 11010010 ................ + 10000080 11010010 11010010 11010010 11010010 ................ + 10000090 11010010 11010010 11010010 11010010 ................ + 100000a0 11010010 11010010 11010010 11010010 ................ + 100000b0 11010010 11010010 11010010 11010010 ................ + 100000c0 11010010 11010010 11010010 11010010 ................ + 100000d0 11010010 11010010 11010010 11010010 ................ + 100000e0 11010010 11010010 11010010 11010010 ................ + 100000f0 11010010 11010010 11010010 11010010 ................ + 10000100 11010010 11010010 11010010 11010010 ................ + 10000110 eff30580 103800be 00be00be 00be00be .....8.......... + 10000120 00be00be f2eb8871 201b0010 4c1b0010 .......q ...L... + 10000130 a0010010 90a31ae7 d3deffff 42012110 ............B.!. + 10000140 ff010000 b01b0000 793512ab ........y5.. +``` + +Every address annotated, no skips: + +#### 0x10000000 - Vector Table, Mandatory Entries + +| Address | Raw Bytes (LE) | Decoded | What it is | +|---------|----------------|---------|------------| +| `0x10000000` | `00 20 08 20` | `0x20082000` | **Initial SP** - top of SCRATCH_Y RAM. Bootrom loads MSP from here before doing anything else. | +| `0x10000004` | `5d 01 00 10` | `0x1000015d` | **Reset_Handler** address with Thumb bit set. Strip bit 0 -> real address `0x1000015c`. Bootrom jumps here. | +| `0x10000008` | `1b 01 00 10` | `0x1000011b` | **NMI** handler address (Thumb, -> `0x1000011a`). | +| `0x1000000c` | `1d 01 00 10` | `0x1000011d` | **HardFault** handler address (Thumb, -> `0x1000011c`). | + +#### 0x10000010-0x1000010f - Vector Table, IRQ Slots (all 52 external IRQs) + +``` + 10000010 11010010 11010010 ...(repeats through 0x1000010f)... +``` + +Every 4-byte word here is `11 01 00 10` = pointer `0x10000111`. +That is the **default IRQ handler** address with Thumb bit set (-> `0x10000110`). +The RP2350 Cortex-M33 has 16 system vectors (offsets 0x00-0x3f) plus up to 52 +external IRQ vectors (offsets 0x40-0xff = addresses `0x10000040`-`0x1000010f`). +Every IRQ the application does not register gets this default handler pointer. +This block is 240 bytes (`0x10000010` to `0x1000010f`) of nothing but that one +repeated pointer. + +#### 0x10000110-0x10000127 - Default IRQ Handler Code + +``` + 10000110 eff30580 103800be 00be00be 00be00be + 10000120 00be00be f2eb8871 +``` + +| Address | Bytes | ARM Thumb-2 Instruction | What it does | +|---------|-------|-------------------------|--------------| +| `0x10000110` | `ef f3 05 80` | `MRS r0, IPSR` | Read the Interrupt Program Status Register into r0. The low 9 bits = the active vector number. | +| `0x10000114` | `10 38` | `SUBS r0, #16` | Vector 16 = IRQ0, so subtract 16 to convert vector number -> IRQ index. | +| `0x10000116` | `00 be` | `BKPT #0` | Software breakpoint. If a debugger is attached, it stops here and you can inspect r0 to see which IRQ fired. If no debugger is attached, the CPU enters a fault loop and the chip hangs. | +| `0x10000118`-`0x10000127` | `00 be` *12 | `BKPT #0` repeating | Alignment padding to the next 4-byte boundary. | + +This is the **entire** default IRQ handler. It is intentionally minimal: if your +code triggers an IRQ you did not register, it crashes visibly instead of silently. + +#### 0x10000128-0x1000013b - Binary Info Pointer Table + +``` + 10000120 201b0010 4c1b0010 + 10000130 a0010010 90a31ae7 +``` + +| Address | Bytes (LE) | Decoded Value | What it is | +|---------|------------|---------------|------------| +| `0x10000128` | `20 1b 00 10` | `0x10001b20` | Pointer to **start** of `.binary_info` data section in flash. | +| `0x1000012c` | `4c 1b 00 10` | `0x10001b4c` | Pointer to **end** of `.binary_info` data section in flash. | +| `0x10000130` | `a0 01 00 10` | `0x100001a0` | Pointer to `binary_info_callback` function. | +| `0x10000134` | `90 a3 1a e7` | (magic marker) | `BINARY_INFO_MARKER_END` - marks the end of this pointer table. | + +`picotool` reads this table to extract the program name, version string, URL, +and GPIO pin map from any compiled binary without running it. + +#### 0x10000138-0x1000014c - IMAGE_DEF Block (this build) + +``` + 10000130 a0010010 90a31ae7 d3deffff 42012110 + 10000140 ff010000 b01b0000 793512ab 4ff00000 +``` + +| Address | Bytes | What it is | +|---------|-------|------------| +| `0x10000138` | `d3 de ff ff` | `PICOBIN_BLOCK_MARKER_START` - the bootrom scans flash for this exact 4-byte sequence to locate the IMAGE_DEF. | +| `0x1000013c` | `42 01 21 10` | IMAGE_DEF content (image type, flags, version). | +| `0x10000140` | `ff 01 00 00` | IMAGE_DEF content (continuation). | +| `0x10000144` | `b0 1b 00 00` | IMAGE_DEF content (continuation). | +| `0x10000148` | `79 35 12 ab` | `PICOBIN_BLOCK_MARKER_END` - bootrom stops scanning here. | + +The IMAGE_DEF sits at `0x10000138`-`0x1000014b` in this build, +well within the 4 KB scan window the bootrom uses (Datasheet 5.9.5, p. 429). + +#### Full Flash Map: 0x10000000-0x1000015c + +``` + 0x10000000-0x1000000f Vector Table: mandatory entries (SP, Reset, NMI, HardFault) + 0x10000010-0x1000010f Vector Table: 52 external IRQ slots -> all point to default handler + 0x10000110-0x10000127 Default IRQ handler code (MRS / SUBS / BKPT) + 0x10000128-0x10000137 Binary info pointer table (start / end / callback / magic end) + 0x10000138-0x1000014b IMAGE_DEF block (d3 de ff ff ... 79 35 12 ab) + 0x10000150-0x1000015b (padding / alignment) + 0x1000015c Reset_Handler (_reset_handler in crt0.S) ↠bootrom jumps here +``` + +### Step 4: Confirmed Boot Sequence (proven from ELF) + +``` ++-----------------------------------------------------------------+ +| PROVEN BOOT SEQUENCE (0x0001_hello-world) | ++-----------------------------------------------------------------+ +| 1. Bootrom reads 0x10000000 | +| -> SP = 0x20082000 (offset +0x00 of vector table) | +| -> RST = 0x1000015d (offset +0x04, Thumb -> 0x1000015c) | ++-----------------------------------------------------------------+ +| 2. Bootrom scans first 4 kB for IMAGE_DEF | +| -> Found at 0x10000138 (this build) | +| -> Start marker: d3 de ff ff | +| -> End marker: 79 35 12 ab | ++-----------------------------------------------------------------+ +| 3. Bootrom jumps to reset handler at 0x1000015c | +| -> _reset_handler (crt0.S) runs | +| -> Checks CPUID - Core 1 sent back to bootrom | +| -> Core 0: .data copied, .bss zeroed, platform_entry called | ++-----------------------------------------------------------------+ +| 4. platform_entry calls runtime_init -> main -> exit | ++-----------------------------------------------------------------+ +``` + +> **Datasheet References:** +> - 5.1.5.1 (p. 357): Block markers `0xffffded3` (start) and `0xab123579` (end) +> - 5.9.5 (p. 429): IMAGE_DEF must appear within first 4 kB of flash image +> - 5.9.5.1 (p. 429): Bootrom enters via reset handler at vector table offset +4 + +--- + +**Remember:** Understanding the boot process is fundamental to embedded systems work. Whether you're debugging a system that won't start, reverse engineering firmware, or building secure boot chains, this knowledge is essential! + +Happy exploring! + + + diff --git a/WEEK03/slides/WEEK03-IMG00.svg b/WEEK03/slides/WEEK03-IMG00.svg new file mode 100644 index 0000000..d40fbfd --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 03 + + +Embedded System Analysis: +Understanding the RP2350 Architecture +w/ Comprehensive Firmware Analysis + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK03/slides/WEEK03-IMG01.svg b/WEEK03/slides/WEEK03-IMG01.svg new file mode 100644 index 0000000..0b8b8bd --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG01.svg @@ -0,0 +1,70 @@ + + + + + +RP2350 Boot Sequence +Power-On to main() — 5 Steps + + + +STEP 1 +Power On +Cortex-M33 wakes, execution at 0x00000000 (Bootrom) + + +▼ + + + +STEP 2 +Bootrom Executes +32KB on-chip ROM — finds IMAGE_DEF at 0x10000000 + + +▼ + + + +STEP 3 +Flash XIP Setup (bootrom-managed) +Bootrom configures flash interface & XIP (no boot2 on RP2350) + + +▼ + + + +STEP 4 +Vector Table & Reset Handler +Reads SP from offset 0x00 -> 0x20082000 +Reads Reset Handler from 0x04 -> 0x1000015d + + +▼ + + + +STEP 5 +C Runtime Startup (crt0.S) +Copy .data from flash -> RAM +Zero .bss section +Call runtime_init() -> main() + + + +Key Insight +Your main() is the LAST thing to run. +All 5 steps must complete first! + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG02.svg b/WEEK03/slides/WEEK03-IMG02.svg new file mode 100644 index 0000000..b7c0025 --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG02.svg @@ -0,0 +1,84 @@ + + + + + +The Bootrom +32KB Factory-Programmed ROM — Where It All Begins + + + +Bootrom Properties + + +Size +32 KB + + +Location +0x00000000 + + +Modifiable? +NO — mask ROM + + +Purpose +Boot the chip + +Burned into silicon at factory +Like BIOS in your computer + + + +What It Does + + +1. +Initialize hardware + + +2. +Check boot sources + + +3. +Validate IMAGE_DEF + + +4. +Configure flash + + +5. +Jump to your code + + + +IMAGE_DEF — Magic Markers +Bootrom looks for these to validate firmware + + +Start Marker +0xFFFFDED3 +"I'm a valid Pico binary!" + + +End Marker +0xAB123579 +"End of header block" + +Bootrom reads flash at 0x10000000, +finds these markers, then boots. + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG03.svg b/WEEK03/slides/WEEK03-IMG03.svg new file mode 100644 index 0000000..4db555b --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG03.svg @@ -0,0 +1,74 @@ + + + + + +XIP — Execute In Place +Run Code Directly from Flash — No Copy Needed + + + +Book Analogy + + +Without XIP +Photocopy every page, read copy + + +With XIP +Read directly from the book! + + + +Why Use XIP? + + +Saves RAM +Code stays in flash + + +Faster Boot +No bulk copy needed + + +Simpler +Less memory mgmt + + + +XIP Flash Region at 0x10000000 + + + +Vector Table +SP at offset 0x00 | Reset Handler at offset 0x04 | Exception handlers... + + + +Your Code +_reset_handler | main() | other functions + + + +Read-Only Data +Strings like "hello, world" | constant values + + +0x10000000 +0x100001xx +0x10001xxx + +CPU fetches instructions directly +from flash via XIP cache. + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG04.svg b/WEEK03/slides/WEEK03-IMG04.svg new file mode 100644 index 0000000..22034ba --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG04.svg @@ -0,0 +1,80 @@ + + + + + +The Vector Table +CPU's Instruction Manual at 0x10000000 + + + +Vector Table Layout + + + +Offset +Address +Value +Meaning + + + +0x00 +0x10000000 +0x20082000 +Initial SP + + + +0x04 +0x10000004 +0x1000015D +Reset Handler + + + +0x08 +0x10000008 +0x1000011B +NMI Handler + + + +0x0C +0x1000000C +0x1000011D +HardFault Handler + + + +GDB: +x/4x 0x10000000 + + + +On Power-On +1. CPU reads SP from 0x00 +2. Sets SP = 0x20082000 +3. Reads Reset from 0x04 +4. Jumps to 0x1000015C + + + +Default Handlers +NMI, HardFault, SVCall, +PendSV, SysTick all use: + +bkpt 0x0000 +<- stops debugger + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG05.svg b/WEEK03/slides/WEEK03-IMG05.svg new file mode 100644 index 0000000..c3d03a0 --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG05.svg @@ -0,0 +1,70 @@ + + + + + +Thumb Mode Addressing +Why Addresses End in Odd Numbers + + + +The LSB Rule +ARM Cortex-M uses the Least Significant +Bit (LSB) to indicate instruction mode: + + + +LSB = 1 (odd) +Thumb mode + + + +LSB = 0 (even) +ARM mode + + + +Reset Handler Example + +Vector table stores: +0x1000015D + +Actual code address: +0x1000015C + +The +1 means: +"Use Thumb mode" + + + +GDB Shows + +0x1000015D +with Thumb bit + + +Vector table raw value + + + +Ghidra Shows + +0x1000015C +actual address + + +Real instruction location + +Both are correct — just displayed differently! + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG06.svg b/WEEK03/slides/WEEK03-IMG06.svg new file mode 100644 index 0000000..f7d8b17 --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG06.svg @@ -0,0 +1,69 @@ + + + + + +Linker Script Memory Map +memmap_default.ld — Where Everything Lives + + + +Memory Regions + + + +Flash (XIP) +0x10000000 +varies +Your code (read-only) + + + +RAM +0x20000000 +512 KB +Main RAM (r/w) + + + +SCRATCH_X +0x20080000 +4 KB +Core 0 scratch (HW: SRAM8) + + + +SCRATCH_Y +0x20081000 +4 KB +Core 0 stack! (HW: SRAM9) + + + +Stack Pointer Calculation + +__StackTop = ORIGIN(SCRATCH_Y) + + LENGTH(SCRATCH_Y) + + +ORIGIN +0x20081000 + ++ LENGTH +0x1000 +(4 KB) + += __StackTop = 0x20082000 +<- matches vector table! + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG07.svg b/WEEK03/slides/WEEK03-IMG07.svg new file mode 100644 index 0000000..262aac0 --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG07.svg @@ -0,0 +1,87 @@ + + + + + +Reset Handler — 4 Phases +_reset_handler at 0x1000015C + + + +Phase 1: Core Check +0x1000015C — 0x10000168 + +mov r0, #0xD0000000 +Read CPUID -> Core 0 continues + + + +Phase 2: Data Copy +0x1000016A — 0x10000176 + +ldmia r4!, {r1,r2,r3} +Copy .data from flash -> RAM + + + +Phase 3: BSS Clear +0x10000178 — 0x10000184 + +stmia r1!, {r0} +r0 = 0 +Zero all uninitialized globals + + + +Phase 4: Platform Entry +0x10000186+ + +blx r1 +-> main() +runtime_init -> main -> exit + + + +Execution Flow + + + +Core Check +CPUID == 0? + +-> + + +Data Copy +flash -> RAM + +-> + + +BSS Clear +zero globals + +-> + + +Platform Entry +-> main()! + + + +Why check cores? +RP2350 has 2 cores. +Only Core 0 runs startup. +Core 1 returns to bootrom and waits. + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG08.svg b/WEEK03/slides/WEEK03-IMG08.svg new file mode 100644 index 0000000..b85e7ce --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG08.svg @@ -0,0 +1,93 @@ + + + + + +Data Copy & BSS Clear +Initializing RAM Before main() Can Run + + + +Phase 2: Data Copy +Copy initialized variables flash -> RAM + +C code: + +int counter = 42; + +Value 42 stored in flash +but variables live in RAM! + + + +Flash + +-> + + +RAM + + +data_cpy_table has entries: + +src: 0x10001B4C (flash) +dst: 0x20000110 (RAM) + + + +Phase 3: BSS Clear +Zero uninitialized global variables + +C code: + +int my_counter; + +C standard requires +this to start at zero. + + + +r1 = BSS start +r2 = BSS end +r0 = 0 + +Loop: store 0, advance r1 +Until r1 == r2 -> done! + + + +Key Assembly Instructions + + + +ldmia r4!, {r1,r2,r3} + +Load source, dest, end from table + + +bl data_cpy + +Copy word-by-word until done + + + +movs r0, #0 + +Load zero into r0 + + +stmia r1!, {r0} + +Store zero, advance pointer + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG09.svg b/WEEK03/slides/WEEK03-IMG09.svg new file mode 100644 index 0000000..8f0373c --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG09.svg @@ -0,0 +1,82 @@ + + + + + +Platform Entry -> main() +The Final Step — 3 Function Calls at 0x10000186 + + + +platform_entry Assembly + + +0x10000186 +ldr r1, [DAT] +-> load runtime_init addr + + +0x10000188 +blx r1 +-> call runtime_init() + + +0x1000018C +blx r1 +-> call main() + + +0x10000190 +blx r1 +-> call exit() + + + +Call Sequence + + +runtime_init() +SDK setup + +-> + + +main() +YOUR CODE + +-> + + +exit() +cleanup + + + +runtime_init() +Initializes SDK systems: +• Clock configuration +• GPIO setup +• C++ constructor calls +• Peripheral initialization + + +After main() Returns +exit() handles cleanup. +Then: + +bkpt 0x0000 +<- infinite halt + +Should never be reached! + \ No newline at end of file diff --git a/WEEK03/slides/WEEK03-IMG10.svg b/WEEK03/slides/WEEK03-IMG10.svg new file mode 100644 index 0000000..73b2c7c --- /dev/null +++ b/WEEK03/slides/WEEK03-IMG10.svg @@ -0,0 +1,92 @@ + + + + + +Secure Boot & Attack Vectors +Why Boot Sequence Knowledge Matters for Security + + + +Attack Scenarios + + +Firmware Replacement +Replace flash with malicious code + + +Vector Table Hijack +Modify reset handler address + + +Debug Port Attack +SWD/JTAG to dump or inject code + + +Startup Code Modification +Change crt0 data copy / BSS init + +Physical access = game over + + + +Defense Strategies + + +1. Secure Boot + + +2. Debug Port Lock + + +3. Flash Read Protect + + +4. MPU Configuration + + +5. Integrity Checks + +Defense in depth! + + + +Secure Boot Chain + + +Bootrom +immutable + +-> + + +Verify Sig +IMAGE_DEF + +-> + + +Verify App +signature + +-> + + +Boot! +or refuse + +Each stage cryptographically verifies +the next before handing off control. +Bootrom = trust anchor (can't be changed) + \ No newline at end of file diff --git a/WEEK04/WEEK04-SLIDES.pdf b/WEEK04/WEEK04-SLIDES.pdf new file mode 100644 index 0000000..9846259 Binary files /dev/null and b/WEEK04/WEEK04-SLIDES.pdf differ diff --git a/WEEK04/WEEK04.md b/WEEK04/WEEK04.md new file mode 100644 index 0000000..ae5b177 --- /dev/null +++ b/WEEK04/WEEK04.md @@ -0,0 +1,910 @@ +# Week 4: Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Understand what variables are and how they're stored in memory +- Know the difference between initialized, uninitialized, and constant variables +- Use Ghidra to analyze binaries without debug symbols +- Patch binary files to change program behavior permanently +- Control GPIO pins to blink LEDs on the Pico 2 +- Convert patched binaries to UF2 format for flashing +- Understand the `.data`, `.bss`, and `.rodata` memory sections + +--- + +## Part 1: Understanding Variables + +### What is a Variable? + +A **variable** is like a labeled box where you can store information. Imagine you have a row of boxes numbered 0 to 9. Each box can hold one item. In programming: + +- The **boxes** are memory locations (addresses in SRAM) +- The **items** are the values you store +- The **labels** are the variable names you choose + +``` ++-----------------------------------------------------------------+ +| Memory (SRAM) - Like a row of numbered boxes | +| | +| Box 0 Box 1 Box 2 Box 3 Box 4 ... | +| +----+ +----+ +----+ +----+ +----+ | +| | 42 | | 17 | | 0 | |255 | | 99 | | +| +----+ +----+ +----+ +----+ +----+ | +| age score count max temp | +| | ++-----------------------------------------------------------------+ +``` + +### Declaration vs Definition + +When working with variables, there are two important concepts: + +| Concept | What It Does | Example | +| ------------------ | ------------------------------------ | -------------------------- | +| **Declaration** | Tells the compiler the name and type | `uint8_t age;` | +| **Definition** | Allocates memory for the variable | (happens with declaration) | +| **Initialization** | Assigns an initial value | `uint8_t age = 42;` | + +**Important Rule:** You must declare a variable BEFORE you use it! + +### Understanding Data Types + +The **data type** tells the compiler how much memory to allocate: + +| Type | Size | Range | Description | +| ---------- | ------- | ------------------------------- | ----------------------- | +| `uint8_t` | 1 byte | 0 to 255 | Unsigned 8-bit integer | +| `int8_t` | 1 byte | -128 to 127 | Signed 8-bit integer | +| `uint16_t` | 2 bytes | 0 to 65,535 | Unsigned 16-bit integer | +| `int16_t` | 2 bytes | -32,768 to 32,767 | Signed 16-bit integer | +| `uint32_t` | 4 bytes | 0 to 4,294,967,295 | Unsigned 32-bit integer | +| `int32_t` | 4 bytes | -2,147,483,648 to 2,147,483,647 | Signed 32-bit integer | + +### Anatomy of a Variable Declaration + +Let's break down this line of code: + +```c +uint8_t age = 42; +``` + +| Part | Meaning | +| --------- | ----------------------------------------------------- | +| `uint8_t` | Data type - unsigned 8-bit integer (1 byte) | +| `age` | Variable name - how we refer to this storage location | +| `=` | Assignment operator - puts a value into the variable | +| `42` | The initial value | +| `;` | Semicolon - tells compiler the statement is complete | + +--- + +## Part 2: Memory Sections - Where Variables Live + +### The Three Main Sections + +When your program is compiled, variables go to different places depending on how they're declared: + +``` ++-----------------------------------------------------------------+ +| .data Section (Flash -> copied to RAM at startup) | +| Contains: Initialized global/static variables | +| Example: int counter = 42; | ++-----------------------------------------------------------------+ +| .bss Section (RAM - zeroed at startup) | +| Contains: Uninitialized global/static variables | +| Example: int counter; (will be 0) | ++-----------------------------------------------------------------+ +| .rodata Section (Flash - read only) | +| Contains: Constants, string literals | +| Example: const int MAX = 100; | +| Example: "hello, world" | ++-----------------------------------------------------------------+ +``` + +### What Happens to Uninitialized Variables? + +In older C compilers, uninitialized variables could contain "garbage" - random leftover data. But modern compilers (including the Pico SDK) are smarter: + +1. Uninitialized global variables go into the `.bss` section +2. The `.bss` section is **NOT stored in the binary** (saves space!) +3. At boot, the startup code uses `memset` to **zero out** all of `.bss` +4. So uninitialized variables are always `0`! + +This is why in our code: +```c +uint8_t age; // This will be 0, not garbage! +``` + +--- + +## Part 3: Understanding GPIO (General Purpose Input/Output) + +### What is GPIO? + +**GPIO** stands for **General Purpose Input/Output**. These are pins on the microcontroller that you can control with software. Think of them as tiny switches you can turn on and off. + +``` ++-----------------------------------------------------------------+ +| Raspberry Pi Pico 2 | +| | +| GPIO 16 -------â–º Red LED | +| GPIO 17 -------â–º Green LED | +| GPIO 18 -------â–º Blue LED | +| ... | +| GPIO 25 -------â–º Onboard LED | ++-----------------------------------------------------------------+ +``` + +### GPIO Functions in the Pico SDK + +The Pico SDK provides simple functions to control GPIO pins: + +| Function | Purpose | +| ------------------------------ | ------------------------------- | +| `gpio_init(pin)` | Initialize a GPIO pin for use | +| `gpio_set_dir(pin, direction)` | Set pin as INPUT or OUTPUT | +| `gpio_put(pin, value)` | Set pin HIGH (1) or LOW (0) | +| `sleep_ms(ms)` | Wait for specified milliseconds | + +### What Happens Behind the Scenes? + +Each high-level function calls lower-level code. Let's trace `gpio_init()`: + +``` +gpio_init(LED_PIN) + ↓ +gpio_set_dir(LED_PIN, GPIO_IN) // Initially set as input + ↓ +gpio_put(LED_PIN, 0) // Set output value to 0 + ↓ +gpio_set_function(LED_PIN, GPIO_FUNC_SIO) // Connect to SIO block +``` + +The SIO (Single-cycle I/O) block is a special hardware unit in the RP2350 that provides fast GPIO control! + +--- + +## Part 4: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board +2. Ghidra installed (for static analysis) +3. Python installed (for UF2 conversion) +4. The sample projects: + - `0x0005_intro-to-variables` + - `0x0008_uninitialized-variables` +5. A serial monitor (PuTTY, minicom, or screen) + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x0005_intro-to-variables/ +| +-- build/ +| | +-- 0x0005_intro-to-variables.uf2 +| | +-- 0x0005_intro-to-variables.bin +| +-- 0x0005_intro-to-variables.c ++-- 0x0008_uninitialized-variables/ +| +-- build/ +| | +-- 0x0008_uninitialized-variables.uf2 +| | +-- 0x0008_uninitialized-variables.bin +| +-- 0x0008_uninitialized-variables.c ++-- uf2conv.py +``` + +--- + +## Part 5: Hands-On Tutorial - Analyzing Variables in Ghidra + +### Step 1: Review the Source Code + +First, let's look at the code we'll be analyzing: + +**File: `0x0005_intro-to-variables.c`** + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + uint8_t age = 42; + + age = 43; + + stdio_init_all(); + + while (true) + printf("age: %d\r\n", age); +} +``` + +**What this code does:** +1. Declares a variable `age` and initializes it to `42` +2. Changes `age` to `43` +3. Initializes the serial output +4. Prints `age` forever in a loop + +### Step 2: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0005_intro-to-variables.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 3: Verify It's Working + +Open your serial monitor (PuTTY, minicom, or screen) and you should see: + +``` +age: 43 +age: 43 +age: 43 +... +``` + +The program is printing `43` because that's what we assigned after the initial `42`. + +--- + +## Part 6: Setting Up Ghidra for Binary Analysis + +### Step 4: Start Ghidra + +**Open a terminal and type:** + +```cmd +ghidraRun +``` + +Ghidra will open. Now we need to create a new project. + +### Step 5: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x0005_intro-to-variables` +5. Click **Finish** + +### Step 6: Import the Binary + +1. Open your file explorer +2. Navigate to the `Embedded-Hacking` folder +3. Find `0x0005_intro-to-variables.bin` +4. Select Cortex M Little Endian 32 +5. Select Options and set up the .text and offset 10000000 +6. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 7: Configure the Binary Format + +A dialog appears. The file is identified as a "BIN" (raw binary without debug symbols). + +**Click the three dots (...) next to "Language" and:** +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` (the XIP address!) +3. Click **OK** + +### Step 8: Open and Analyze + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete (watch the progress bar in the bottom right). + +--- + +## Part 7: Navigating and Resolving Functions + +### Step 9: Find the Functions + +Look at the **Symbol Tree** panel on the left. Expand **Functions**. + +You'll see function names like: +- `FUN_1000019a` +- `FUN_10000210` +- `FUN_10000234` + +These are auto-generated names because we imported a raw binary without symbols! + +### Step 10: Resolve Known Functions + +From our previous chapters, we know what some of these functions are: + +| Ghidra Name | Actual Name | How We Know | +| -------------- | ------------- | -------------------------- | +| `FUN_1000019a` | `data_cpy` | From Week 3 boot analysis | +| `FUN_10000210` | `frame_dummy` | From Week 3 boot analysis | +| `FUN_10000234` | `main` | This is where our code is! | + +### Step 11: Update Main's Signature + +For `main`, let's also fix the return type: + +1. Right-click on `main` in the Decompile window +2. Select **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +--- + +## Part 8: Analyzing the Main Function + +### Step 12: Examine Main in Ghidra + +Click on `main` (or `FUN_10000234`). Look at the **Decompile** window: + +You'll see something like: + +```c +void FUN_10000234(void) + +{ + FUN_10002f54(); + do { + FUN_100030e4(DAT_10000244,0x2b); + } while( true ); +} +``` + +### Step 13: Resolve stdio_init_all + +1. Click on `FUN_10002f54` +2. Right-click -> **Edit Function Signature** +3. Change to: `bool stdio_init_all(void)` +4. Click **OK** + +### Step 14: Resolve printf + +1. Click on `FUN_100030e4` +2. Right-click -> **Edit Function Signature** +3. Change the name to `void printf (undefined4 param_1, ...)` +4. Check the **Varargs** checkbox (printf takes variable arguments!) +5. Click **OK** + +### Step 15: Understand the Optimization + +Look at the updated decompiled code. This will look different if you resolved your functions however do you notice something interesting? + +```c +int main(void) + +{ + stdio_init_all(); + do { + printf(DAT_10000244,0x2b); + } while( true ); +} +``` + +**Where's `uint8_t age = 42`?** It's gone! + +The compiler **optimized it out**! Here's what happened: + +1. Original code: `age = 42`, then `age = 43` +2. Compiler sees: "The `42` is never used, only `43` matters" +3. Compiler removes the unused `42` and just uses `43` directly + +**What is `0x2b`?** Let's check: +- `0x2b` in hexadecimal = `43` in decimal + +The compiler replaced our variable with the constant value! + +--- + +## Part 9: Patching the Binary - Changing the Value + +### Step 16: Find the Value to Patch + +Look at the **Listing** window (assembly view). Find the instruction that loads `0x2b`: + +```assembly +1000023a 2b 21 movs r1,#0x2b +``` + +This instruction loads the value `0x2b` (43) into register `r1` before calling `printf`. + +### Step 17: Patch the Instruction + +We're going to change `0x2b` (43) to `0x46` (70)! + +1. At address `1000023a`, click the instruction `movs r1,#0x2b` +2. Right-click and select **Patch Instruction** +3. Replace immediate `0x2b` with `0x46` +4. Press Enter and verify the instruction bytes change from `2b 21` to `46 21` + +The instruction now reads: +```assembly +1000023a 46 21 movs r1,#0x46 +``` + +### Step 18: Export the Patched Binary + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. Navigate to your build directory +4. Name the file `0x0005_intro-to-variables-h.bin` +5. Click **OK** + +--- + +## Part 10: Converting and Flashing the Hacked Binary + +### Step 19: Convert to UF2 Format + +The Pico 2 expects UF2 files, not raw BIN files. We need to convert it! + +**Open a terminal and navigate to your project directory:** + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0005_intro-to-variables +``` + +**Run the conversion command:** + +```cmd +python ..\uf2conv.py build\0x0005_intro-to-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +**What this command means:** +- `uf2conv.py` = the conversion script +- `--base 0x10000000` = the XIP base address +- `--family 0xe48bff59` = the RP2350 family ID +- `--output build\hacked.uf2` = the output filename + +### Step 20: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Open your serial monitor + +**You should see:** + +``` +age: 70 +age: 70 +age: 70 +... +``` + + **BOOM! We hacked it!** The value changed from 43 to 70! + +--- + +## Part 11: Uninitialized Variables and GPIO + +Now let's work with a more complex example that includes GPIO control. + +### Step 21: Review the Uninitialized Variables Code + +**File: `0x0008_uninitialized-variables.c`** + +```c +#include +#include "pico/stdlib.h" + +#define LED_PIN 16 + +int main(void) { + uint8_t age; // Uninitialized! + + stdio_init_all(); + + gpio_init(LED_PIN); + gpio_set_dir(LED_PIN, GPIO_OUT); + + while (true) { + printf("age: %d\r\n", age); + + gpio_put(LED_PIN, 1); + sleep_ms(500); + + gpio_put(LED_PIN, 0); + sleep_ms(500); + } +} +``` + +**What this code does:** +1. Declares `age` without initializing it (will be 0 due to BSS zeroing) +2. Initializes GPIO 16 as an output +3. In a loop: prints age, blinks the LED + +### Step 22: Flash and Verify + +1. Flash `0x0008_uninitialized-variables.uf2` to your Pico 2 +2. Open your serial monitor + +**You should see:** + +``` +age: 0 +age: 0 +age: 0 +... +``` + +And the **red LED on GPIO 16 should be blinking**! + +The value is `0` because uninitialized variables in the `.bss` section are zeroed at startup. + +--- + +## Part 12: Analyzing GPIO Code in Ghidra + +### Step 23: Set Up Ghidra for the New Binary + +1. Create a new project: `0x0008_uninitialized-variables` +2. Import `0x0008_uninitialized-variables.bin` +3. Set Language to **ARM Cortex 32 little endian** +4. Set Base Address to `.text` and `10000000` +5. Auto-analyze + +### Step 24: Resolve the Functions + +Find and rename these functions: + +| Ghidra Name | Actual Name | +| -------------- | ---------------- | +| `FUN_10000234` | `main` | +| `FUN_100030cc` | `stdio_init_all` | +| `FUN_100002b4` | `gpio_init` | +| `FUN_1000325c` | `printf` | + +For `gpio_init`, set the signature to: +```c +void gpio_init(uint gpio) +``` + +### Step 25: Examine the Main Function + +The decompiled main should look something like: + +```c +void FUN_10000234(void) + +{ + undefined4 extraout_r1; + undefined4 extraout_r2; + undefined4 in_cr0; + undefined4 in_cr4; + + FUN_100030cc(); + FUN_100002b4(0x10); + coprocessor_moveto2(0,4,0x10,1,in_cr4); + do { + FUN_1000325c(DAT_10000274,0); + coprocessor_moveto2(0,4,0x10,1,in_cr0); + FUN_10000d10(500); + coprocessor_moveto2(0,4,0x10,0,in_cr0); + FUN_10000d10(500,extraout_r1,extraout_r2,0); + } while( true ); +} +``` + +--- + +## Part 13: Hacking GPIO - Changing the LED Pin + +### Step 26: Find the GPIO Pin Value + +Look in the assembly for instructions that use `0x10` (which is 16 in decimal - our LED pin): + +```assembly +1000023a 10 20 movs r0,#0x10 +``` + +This is where `gpio_init(LED_PIN)` is called with GPIO 16. + +### Step 27: Patch GPIO 16 to GPIO 17 + +We'll change the red LED (GPIO 16) to the green LED (GPIO 17)! + +1. At address `1000023a`, select `movs r0,#0x10` +2. Right-click -> **Patch Instruction** +3. Replace immediate `0x10` with `0x11` (17 decimal) +4. Click **OK** and verify bytes change from `10 20` to `11 20` + +### Step 28: Find All GPIO 16 References + +There are more places that use GPIO 16. Look for: + +```assembly +10000244 10 23 movs r3,#0x10 +``` + +This is used in `gpio_set_dir`. Patch this to `0x11` as well. + +```assembly +10000252 10 24 movs r4,#0x10 +``` + +This is inside the loop for `gpio_put`. Patch this to `0x11` as well. +Patch each one with **Patch Instruction**, then verify: +- `10000244`: `10 23` -> `11 23` +- `10000252`: `10 24` -> `11 24` + +### Step 29: Bonus - Change the Printed Value + +Let's also change the printed value from `0` to `0x42` (66 in decimal): + +```assembly +1000024a 00 21 movs r1,#0x0 +``` + +1. Right-click -> **Patch Instruction** +2. Replace immediate `0x0` with `0x42` +3. Click **OK** and verify bytes change from `00 21` to `42 21` + +--- + +## Part 14: Export and Test the Hacked GPIO + +### Step 30: Export the Patched Binary + +1. Click **File** -> **Export Program** +2. Format: **Raw Bytes** +3. Filename: `0x0008_uninitialized-variables-h.bin` +4. Click **OK** + +### Step 31: Convert to UF2 + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0008_uninitialized-variables +python ..\uf2conv.py build\0x0008_uninitialized-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 32: Flash and Verify + +1. Flash `hacked.uf2` to your Pico 2 +2. Check your serial monitor + +**You should see:** + +``` +age: 66 +age: 66 +age: 66 +... +``` + +And now the **GREEN LED on GPIO 17** should be blinking instead of the red one! + + **We successfully:** +1. Changed the printed value from 0 to 66 +2. Changed which LED blinks from red (GPIO 16) to green (GPIO 17) + +--- + +## Part 15: Deep Dive - GPIO at the Assembly Level + +### Understanding the GPIO Coprocessor + +The RP2350 has a special **GPIO coprocessor** that provides fast, single-cycle GPIO control. This is different from the RP2040! + +The coprocessor is accessed using special ARM instructions: + +```assembly +mcrr p0, #4, r4, r5, c0 ; GPIO output control +mcrr p0, #4, r4, r5, c4 ; GPIO direction control +``` + +**What this means:** +- `mcrr` = Move to Coprocessor from two ARM Registers +- `p0` = Coprocessor 0 (the GPIO coprocessor) +- `r4` = Contains the GPIO pin number +- `r5` = Contains the value (0 or 1) +- `c0` = Output value register +- `c4` = Output enable register + +### The Full GPIO Initialization Sequence + +When you call `gpio_init(16)`, here's what actually happens: + +``` +Step 1: Configure pad (address 0x40038044) ++-----------------------------------------------------------------+ +| - Clear OD bit (output disable) | +| - Set IE bit (input enable) | +| - Clear ISO bit (isolation) | ++-----------------------------------------------------------------+ + +Step 2: Set function (address 0x40028084) ++-----------------------------------------------------------------+ +| - Set FUNCSEL to 5 (SIO - Software I/O) | ++-----------------------------------------------------------------+ + +Step 3: Enable output (via coprocessor) ++-----------------------------------------------------------------+ +| - mcrr p0, #4, r4, r5, c4 (where r4=16, r5=1) | ++-----------------------------------------------------------------+ +``` + +### Raw Assembly LED Blink + +Here's what a completely hand-written assembly LED blink looks like: + +```assembly +; Initialize GPIO 16 as output +movs r4, #0x10 ; GPIO 16 +movs r5, #0x01 ; Enable +mcrr p0, #4, r4, r5, c4 ; Set as output + +; Configure pad registers +ldr r3, =0x40038044 ; Pad control for GPIO 16 +ldr r2, [r3] ; Load current config +bic r2, r2, #0x80 ; Clear OD (output disable) +orr r2, r2, #0x40 ; Set IE (input enable) +str r2, [r3] ; Store config + +; Set GPIO function to SIO +ldr r3, =0x40028084 ; IO bank control for GPIO 16 +movs r2, #5 ; FUNCSEL = SIO +str r2, [r3] ; Set function + +; Main loop +loop: + ; LED ON + movs r4, #0x10 ; GPIO 16 + movs r5, #0x01 ; High + mcrr p0, #4, r4, r5, c0 + + ; Delay + ldr r2, =0x17D7840 ; ~25 million iterations +delay1: + subs r2, r2, #1 + bne delay1 + + ; LED OFF + movs r4, #0x10 ; GPIO 16 + movs r5, #0x00 ; Low + mcrr p0, #4, r4, r5, c0 + + ; Delay + ldr r2, =0x17D7840 +delay2: + subs r2, r2, #1 + bne delay2 + + b loop ; Repeat forever +``` + +--- + +## Part 16: Summary and Review + +### What We Accomplished + +1. **Learned about variables** - How they're declared, initialized, and stored +2. **Understood memory sections** - `.data`, `.bss`, and `.rodata` +3. **Analyzed binaries in Ghidra** - Without debug symbols! +4. **Patched binaries** - Changed values directly in the binary +5. **Controlled GPIO** - Made LEDs blink +6. **Changed program behavior** - Different LED, different value + +### The Binary Patching Workflow + +``` ++-----------------------------------------------------------------+ +| 1. Import .bin file into Ghidra | +| - Set language to ARM Cortex | +| - Set base address to 0x10000000 | ++-----------------------------------------------------------------+ +| 2. Analyze and resolve functions | +| - Rename functions to meaningful names | +| - Fix function signatures | ++-----------------------------------------------------------------+ +| 3. Find the values/instructions to patch | +| - Look in the assembly listing | +| - Patch Instruction, then verify old bytes -> new bytes | ++-----------------------------------------------------------------+ +| 4. Export the patched binary | +| - File -> Export Program | +| - Format: Raw Bytes | ++-----------------------------------------------------------------+ +| 5. Convert to UF2 | +| - python uf2conv.py file.bin --base 0x10000000 | +| --family 0xe48bff59 --output hacked.uf2 | ++-----------------------------------------------------------------+ +| 6. Flash and verify | +| - Hold BOOTSEL, plug in, drag UF2 | +| - Check serial output and LED behavior | ++-----------------------------------------------------------------+ +``` + +### Key Memory Sections + +| Section | Location | Contains | Writable? | +| --------- | -------- | ------------------------------ | --------- | +| `.text` | Flash | Code | No | +| `.rodata` | Flash | Constants, strings | No | +| `.data` | RAM | Initialized globals | Yes | +| `.bss` | RAM | Uninitialized globals (zeroed) | Yes | + +### Important Ghidra Commands + +| Action | How To Do It | +| ----------------- | ------------------------------------- | +| Rename function | Right-click -> Edit Function Signature | +| Patch instruction | Right-click -> Patch Instruction, then verify old bytes -> new bytes | +| Export binary | File -> Export Program -> Raw Bytes | +| Go to address | Press 'G' and enter address | + +--- + +--- + +## Key Takeaways + +1. **Variables are just memory locations** - The compiler assigns them addresses in SRAM. + +2. **Compilers optimize aggressively** - Unused code and values may be removed entirely. + +3. **Uninitialized doesn't mean random** - Modern compilers zero out the `.bss` section. + +4. **Ghidra works without symbols** - You can analyze any binary, even stripped ones. + +5. **Binary patching is powerful** - You can change behavior without source code. + +6. **UF2 conversion is required** - The Pico 2 needs UF2 format, not raw binaries. + +7. **GPIO is just memory-mapped I/O** - Writing to specific addresses controls hardware. + +--- + +## Glossary + +| Term | Definition | +| ------------------ | --------------------------------------------------------------------- | +| **BSS** | Block Started by Symbol - section for uninitialized global variables | +| **Declaration** | Telling the compiler a variable's name and type | +| **Definition** | Allocating memory for a variable | +| **GPIO** | General Purpose Input/Output - controllable pins on a microcontroller | +| **Initialization** | Assigning an initial value to a variable | +| **Linker** | Tool that combines compiled code and assigns memory addresses | +| **Optimization** | Compiler removing or simplifying code for efficiency | +| **Patching** | Modifying bytes directly in a binary file | +| **rodata** | Read-only data section for constants and string literals | +| **SIO** | Single-cycle I/O - fast GPIO control block in RP2350 | +| **UF2** | USB Flashing Format - file format for Pico 2 firmware | +| **Variable** | A named storage location in memory | + +--- + +## Additional Resources + +### GPIO Coprocessor Reference + +The RP2350 GPIO coprocessor instructions: + +| Instruction | Description | +| -------------------------- | ---------------------------- | +| `mcrr p0, #4, Rt, Rt2, c0` | Set/clear GPIO output | +| `mcrr p0, #4, Rt, Rt2, c4` | Set/clear GPIO output enable | + +### RP2350 Memory Map Quick Reference + +| Address | Description | +| ------------ | ------------------------ | +| `0x10000000` | XIP Flash (code) | +| `0x20000000` | SRAM (data) | +| `0x40028000` | IO_BANK0 (GPIO control) | +| `0x40038000` | PADS_BANK0 (pad control) | +| `0xd0000000` | SIO (single-cycle I/O) | + +--- + +**Remember:** Every binary you encounter in the real world can be analyzed and understood using these same techniques. Practice makes perfect! + +Happy hacking! + + diff --git a/WEEK04/slides/WEEK04-IMG00.svg b/WEEK04/slides/WEEK04-IMG00.svg new file mode 100644 index 0000000..2b2aae1 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 04 + + +Variables in Embedded Systems: +Debugging and Hacking Variables +w/ GPIO Output Basics + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK04/slides/WEEK04-IMG01.svg b/WEEK04/slides/WEEK04-IMG01.svg new file mode 100644 index 0000000..5331adf --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG01.svg @@ -0,0 +1,96 @@ + + + + + +What is a Variable? +Labeled Boxes in Memory (SRAM) + + + +Memory — A Row of Numbered Boxes + + + +42 +age +Box 0 + + + +17 +score +Box 1 + + + +0 +count +Box 2 + + + +255 +max +Box 3 + + + +99 +temp +Box 4 + + + +Anatomy of a Declaration + + +uint8_t age = 42; + +uint8_t +Data type (1 byte) + +age +Variable name (label) + += 42 +Initial value + +; +End of statement + + + +Key Concepts + + +Declaration +name + type + + +Definition +allocates memory + + +Initialization +assigns value + + + +Important Rule +You MUST declare a +variable BEFORE you +use it! +Compiler needs to know the type + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG02.svg b/WEEK04/slides/WEEK04-IMG02.svg new file mode 100644 index 0000000..8933406 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG02.svg @@ -0,0 +1,86 @@ + + + + + +Data Types & Sizes +How Much Memory Each Type Uses + + + + + + +Type +Size +Range +Description + + + +uint8_t +1 byte +0 — 255 +Unsigned 8-bit + + + +int8_t +1 byte +-128 — 127 +Signed 8-bit + + + +uint16_t +2 bytes +0 — 65,535 +Unsigned 16-bit + + + +int16_t +2 bytes +-32,768 — 32,767 +Signed 16-bit + + + +uint32_t +4 bytes +0 — 4,294,967,295 +Unsigned 32-bit + + + +int32_t +4 bytes +-2.1B — 2.1B +Signed 32-bit + + +Size Comparison + + +1B +uint8_t + + +2B +uint16_t + + +4 Bytes +uint32_t + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG03.svg b/WEEK04/slides/WEEK04-IMG03.svg new file mode 100644 index 0000000..0cbcf5a --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG03.svg @@ -0,0 +1,63 @@ + + + + + +Memory Sections +Where Variables Live After Compilation + + + + +.data +Flash -> copied to RAM at startup +Contains: Initialized global/static variables +int counter = 42; +Initial value stored in flash, copied to SRAM by data_cpy + + + + +.bss +RAM — zeroed at startup +Contains: Uninitialized global/static variables +int counter; +NOT stored in binary (saves space!) — memset to 0 at boot + + + + +.rodata +Flash — read only +Contains: Constants and string literals +const int MAX = 100; +Lives in flash permanently — cannot be modified at runtime + + + +.data +RAM +Writable +Initialized globals + +.bss +RAM +Writable +Uninitialized globals (zeroed) + +.rodata +Flash +Read-only +Constants & strings + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG04.svg b/WEEK04/slides/WEEK04-IMG04.svg new file mode 100644 index 0000000..82437d2 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG04.svg @@ -0,0 +1,79 @@ + + + + + +GPIO Basics +General Purpose Input/Output on RP2350 + + + +Pico 2 GPIO Pins + +GPIO 16 + +Red LED + +GPIO 17 + +Green LED + +GPIO 18 + +Blue LED + +GPIO 25 + +Onboard LED + +Software-controlled switches + + + +Pico SDK Functions + + +gpio_init(pin) +Init pin + + +gpio_set_dir(pin,d) +I/O dir + + +gpio_put(pin,val) +Set H/L + + +sleep_ms(ms) +Delay + + + +Basic LED Blink Code + + +#define LED_PIN 16 +int main(void) { +gpio_init(LED_PIN); +gpio_set_dir(LED_PIN, GPIO_OUT); +while (true) { +gpio_put(LED_PIN, 1); +// ON +sleep_ms(500); +gpio_put(LED_PIN, 0); +// OFF +sleep_ms(500); +}} + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG05.svg b/WEEK04/slides/WEEK04-IMG05.svg new file mode 100644 index 0000000..c6a0ef6 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG05.svg @@ -0,0 +1,79 @@ + + + + + +Ghidra Binary Analysis +Analyzing a Raw .bin Without Symbols + + + +1. Import + +File -> Import +Language: +ARM Cortex 32 LE +Block: +.text +Base: +10000000 +XIP address for RP2350 + + + +2. Analyze + +Auto-Analyze: Yes +Ghidra finds: +FUN_1000019a +FUN_10000210 +FUN_10000234 +Auto-generated names + + + +3. Resolve + +Edit Function Sig +Rename to: +data_cpy +frame_dummy +main +Fix signatures + + + +Decompiled main() in Ghidra + + +Before Resolving: +void FUN_10000234(void){ +FUN_10002f54(); +do { +FUN_100030e4( +DAT_10000244,0x2b); +} while(true); +} + + +After Resolving: +int main(void) { +stdio_init_all(); +do { +printf( +"age: %d\r\n" +, 0x2b); +} while(true); +} + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG06.svg b/WEEK04/slides/WEEK04-IMG06.svg new file mode 100644 index 0000000..156aeeb --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG06.svg @@ -0,0 +1,77 @@ + + + + + +Compiler Optimization +Why Your Variable Disappeared + + + +Source Code + + +int main(void) { +uint8_t age = 42; +age = 43; +stdio_init_all(); +while (true) +printf("age: %d", age); + + + +Compiler Thinks... + + +age = 42 is NEVER read + + +Dead store -> REMOVED + + +age = 43 -> constant fold + +Replaces variable with literal + + + +Resulting Assembly + + +1000023a +2b 21 +movs r1, #0x2b +; 0x2b = 43 +No age=42 instruction — compiler removed it + + + +Key Takeaway + + +Source Code +age = 42 +age = 43 + +-> + + +Binary +movs r1, #0x2b + + +Compiler +Optimizes dead +stores away! + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG07.svg b/WEEK04/slides/WEEK04-IMG07.svg new file mode 100644 index 0000000..2041444 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG07.svg @@ -0,0 +1,86 @@ + + + + + +Binary Patching +Changing Values in the Binary + + + +Before Patch + + +1000023a +2b 21 +movs r1,#0x2b + +0x2b = 43 decimal +Output: +age: 43 +Compiler-optimized constant + + + +After Patch + + +1000023a +46 21 +movs r1,#0x46 + +0x46 = 70 decimal +Output: +age: 70 +Changed program behavior! + + + +How to Patch in Ghidra + + +1. Find Instr + +-> + + +2. Rt-Click + +-> + + +3. Patch Val + +-> + + +Done! + +Patch Instruction: change operand + + + +Export Patched Binary + + +File: Export + + +Format: Raw Bytes + + +Save as *-h.bin + +Exported binary has your patches + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG08.svg b/WEEK04/slides/WEEK04-IMG08.svg new file mode 100644 index 0000000..eada6e7 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG08.svg @@ -0,0 +1,99 @@ + + + + + +GPIO Hacking +Patching GPIO 16 to GPIO 17 + + + +Original: GPIO 16 +Red LED on pin 16 + + +1000023a +10 20 +movs r0,#0x10 + + +10000244 +10 23 +movs r3,#0x10 + + +10000252 +10 24 +movs r4,#0x10 + +0x10 = 16, three locations + + + +Patched: GPIO 17 +Green LED on pin 17 + + +1000023a +11 20 +movs r0,#0x11 + + +10000244 +11 23 +movs r3,#0x11 + + +10000252 +11 24 +movs r4,#0x11 + +0x11 = 17, all patched! + + + +What Each Patch Controls + + +gpio_init +r0 + + +gpio_set_dir +r3 + + +gpio_put +r4 + +ALL pin refs must be patched + + + +Bonus: Change Print Value + + +00 21 +movs r1,#0x0 +age: 0 + +-> + + +42 21 +movs r1,#0x42 +age: 66 + +Changed value: 0 to 66 (0x42) + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG09.svg b/WEEK04/slides/WEEK04-IMG09.svg new file mode 100644 index 0000000..6743432 --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG09.svg @@ -0,0 +1,78 @@ + + + + + +GPIO Coprocessor +RP2350 Single-Cycle I/O via mcrr + + + +mcrr Instruction Breakdown + + +mcrr p0, #4, r4, r5, c0 + +mcrr +Move to Coprocessor (2 regs) +p0 +Coprocessor 0 (GPIO) +r4 +GPIO pin number +r5 +Value (0=LOW, 1=HIGH) + + + +Output Value (c0) + + +mcrr p0,#4,r4,r5,c0 + +r4 = pin number +r5 = 0 or 1 +Controls GPIO output state + + +Output Enable (c4) + + +mcrr p0,#4,r4,r5,c4 + +r4 = pin number +r5 = 1 (enable output) +Sets pin direction to OUTPUT + + + +gpio_init(16) Sequence + + +Step 1: Config Pad +addr 0x40038044 + + +Step 2: Set Func +FUNCSEL = 5 (SIO) + + +Step 3: Enable Out +mcrr p0,#4,r4,r5,c4 + + + + +Pad: clear OD, set IE, clear ISO +SIO = fast single-cycle GPIO access + \ No newline at end of file diff --git a/WEEK04/slides/WEEK04-IMG10.svg b/WEEK04/slides/WEEK04-IMG10.svg new file mode 100644 index 0000000..23741fe --- /dev/null +++ b/WEEK04/slides/WEEK04-IMG10.svg @@ -0,0 +1,119 @@ + + + + + +Full Patching Pipeline +End-to-End Binary Hacking Workflow + + + + +1 +Import .bin +Ghidra: Import +ARM Cortex 32 LE +Base: 0x10000000 + + + + + + + +2 +Analyze +Auto-analyze +Rename functions +Fix signatures + + + + + + + +3 +Find Target +Listing window +Find movs rN,#val +Identify bytes to change + + + + +4 +Patch +Right-click: +Patch Instruction +Change operand value + + + + + + + +5 +Export +File: Export +Format: Raw Bytes +Save as *-h.bin + + + + + + + +6 +Convert UF2 +uf2conv.py +--family 0xe48bff59 +RP2350 family ID + + + +UF2 Command + +python uf2conv.py file.bin --base 0x10000000 -o hacked.uf2 + + + +Flash to Pico 2 +1. Hold BOOTSEL + USB +2. Drop hacked.uf2 +3. Pico reboots hacked +RPI-RP2 drive in BOOTSEL + + + +Key Sections + +.text +Flash +Code + +.rodata +Flash +Constants + +.data +RAM +Init globals + +.bss +RAM +Zeroed globals + \ No newline at end of file diff --git a/WEEK05/WEEK05-SLIDES.pdf b/WEEK05/WEEK05-SLIDES.pdf new file mode 100644 index 0000000..68bb942 Binary files /dev/null and b/WEEK05/WEEK05-SLIDES.pdf differ diff --git a/WEEK05/WEEK05.md b/WEEK05/WEEK05.md new file mode 100644 index 0000000..887ed40 --- /dev/null +++ b/WEEK05/WEEK05.md @@ -0,0 +1,1353 @@ +# Week 5: Integers and Floats in Embedded Systems: Debugging and Hacking Integers and Floats w/ Intermediate GPIO Output Assembler Analysis + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Understand how integers and floating-point numbers are stored in memory +- Know the difference between signed and unsigned integers (`uint8_t` vs `int8_t`) +- Understand how floats and doubles are represented using IEEE 754 encoding +- Use inline assembly to control GPIO pins directly at the hardware level +- Debug numeric data types using GDB and the OpenOCD debugger +- Hack integer values by modifying registers at runtime +- Hack floating-point values by understanding and manipulating their binary representation +- Reconstruct 64-bit doubles from two 32-bit registers +--- + +## Part 1: Understanding Integer Data Types + +### What is an Integer? + +An **integer** is a whole number without any decimal point. Think of it like counting apples: you can have 0 apples, 1 apple, 42 apples, but you can't have 3.5 apples (that would be a fraction!). + +In C programming for embedded systems, we have special integer types that tell the compiler exactly how much memory to use: + +``` ++-----------------------------------------------------------------+ +| Integer Types - Different Sizes for Different Needs | +| | +| uint8_t: 1 byte (0 to 255) - like a small box | +| int8_t: 1 byte (-128 to 127) - can hold negatives! | +| uint16_t: 2 bytes (0 to 65,535) - medium box | +| uint32_t: 4 bytes (0 to 4 billion) - big box | +| | ++-----------------------------------------------------------------+ +``` + +### Signed vs Unsigned Integers + +The difference between `uint8_t` and `int8_t` is whether the number can be **negative**: + +| Type | Prefix | Range | Use Case | +| --------- | ------ | ----------- | ----------------------------- | +| `uint8_t` | `u` | 0 to 255 | Ages, counts, always positive | +| `int8_t` | none | -128 to 127 | Temperature, can be negative | + +#### The Integer Variables + +Let's say a program declares two integer variables that demonstrate the difference between **signed** and **unsigned** types: + +```c +uint8_t age = 43; +int8_t range = -42; +``` + +The variable `age` is a `uint8_t` - an **unsigned** 8-bit integer that can only hold values from `0` to `255`. Since age is always a positive number, unsigned is the right choice. The variable `range` is an `int8_t` - a **signed** 8-bit integer that can hold values from `-128` to `127`. The signed type allows it to represent negative numbers like `-42`. Under the hood, negative values are stored using **two's complement** encoding: the CPU flips all the bits of `42` (`0x2A`) and adds `1`, producing `0xD6`, which is how `-42` lives in a single byte of memory. + +--- + +## Part 2: Understanding Floating-Point Data Types + +### What is a Float? + +A **float** is a number that can have a decimal point. Unlike integers which can only hold whole numbers like `42`, a float can hold values like `42.5`, `3.14`, or `-0.001`. In C, the `float` type uses **32 bits (4 bytes)** to store a number using the **IEEE 754** standard. + +``` ++-----------------------------------------------------------------+ +| IEEE 754 Single-Precision (32-bit float) | +| | +| +------+----------+---------------------------+ | +| | Sign | Exponent | Mantissa (Fraction) | | +| | 1bit | 8 bits | 23 bits | | +| +------+----------+---------------------------+ | +| | +| Value = (-1)^sign * 2^(exponent-127) * 1.mantissa | +| | +| Example: 42.5 | +| Sign: 0 (positive) | +| Exponent: 10000100 (132 - 127 = 5) | +| Mantissa: 01010100000000000000000 | +| Full: 0 10000100 01010100000000000000000 | +| Hex: 0x422A0000 | +| | ++-----------------------------------------------------------------+ +``` + +### How to Compute This by Hand (42.5 -> IEEE 754) + +Use this exact process any time you need to encode a decimal float manually. + +1. Determine the sign bit. + - `42.5` is positive, so `sign = 0`. + +2. Convert the number to binary. + - Integer part: `42 = 101010 (base 2)` + - Fractional part: use repeated multiply-by-2 on the fraction. + - Start with `0.5` + - `0.5 * 2 = 1.0` -> integer part is `1` (this is the first binary fractional bit) + - Remaining fractional part is now `0.0`, so we stop. + - Therefore `0.5 = 0.1 (base 2)`. + - Combined: `42.5 = 101010.1 (base 2)` + +3. Normalize to the form `1.xxxxx * 2^n`. + - `101010.1 (base 2) = 1.010101 (base 2) * 2^5` + - So the true exponent is `n = 5`. + +4. Compute the stored exponent (bias 127 for float). + - `stored exponent = n + 127 = 5 + 127 = 132` + - `132` in binary is `10000100` (8 bits). + + > Tip: **Why 127?** The exponent field is 8 bits wide, giving $2^8 = 256$ total values. Half of that range should represent negative exponents and half positive. The midpoint is $(2^8 / 2) - 1 = 127$. So a stored exponent of `127` means a real exponent of **0**, values below `127` are negative exponents, and values above `127` are positive exponents. Doubles use an 11-bit exponent field so their midpoint (bias) is $( 2^{11} / 2) - 1 = 1023$ instead. + +5. Build the mantissa (fraction bits). + - Take bits after the leading `1.` from `1.010101` -> `010101` + - Pad with zeros to 23 bits: + - `01010100000000000000000` + +6. Assemble all fields. + - `sign | exponent | mantissa` + - `0 | 10000100 | 01010100000000000000000` + - Full 32-bit pattern: + - `01000010001010100000000000000000` + +7. Convert the 32-bit binary to hex (group by 4 bits). + - `0100 0010 0010 1010 0000 0000 0000 0000` + - `4 2 2 A 0 0 0 0` + - Final result: `0x422A0000` + +Quick decode check (reverse direction, fully expanded): + +Given the 32-bit pattern: +- `0 | 10000100 | 01010100000000000000000` + +Decode it field by field: + +1. Sign bit + - Sign bit is `0` -> number is positive. + - So the sign multiplier is `(+1)`. + +2. Exponent field + - Exponent bits are `10000100`. + - Convert to decimal: `10000100 (base 2) = 132`. + - Float bias is `127`, so true exponent is: + - `132 - 127 = 5`. + +3. Mantissa field + - Stored mantissa bits are `01010100000000000000000`. + - IEEE 754 normal numbers use an implicit leading `1`, so significand becomes: + - `1.010101 (base 2)`. + +4. Rebuild the value + - Formula: `value = (+1) * 1.010101 (base 2) * 2^5`. + - Shift binary point right by 5: + - `1.010101 * 2^5 = 101010.1 (base 2)`. + +5. Convert `101010.1 (base 2)` to decimal + - Integer part: `101010 = 32 + 8 + 2 = 42` + - Fraction part: `.1 = 1/2 = 0.5` + - Total: `42 + 0.5 = 42.5` + +So the decoded value is exactly `42.5`. + +### Float vs Integer - Key Differences + +| Property | Integer (`uint8_t`) | Float (`float`) | +| -------------- | ---------------------- | --------------------------- | +| **Size** | 1 byte | 4 bytes | +| **Precision** | Exact | ~7 decimal digits | +| **Range** | 0 to 255 | 3.4 10^38 | +| **Encoding** | Direct binary | IEEE 754 (sign/exp/mantissa)| +| **printf** | `%d` | `%f` | + +### Our Floating-Point Program + +Let's look at a simple program that uses a `float` variable: + +**File: `0x000e_floating-point-data-type.c`** + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + float fav_num = 42.5; + + stdio_init_all(); + + while (true) + printf("fav_num: %f\r\n", fav_num); +} +``` + +> Note: `fav_num` is declared inside `main`, so by C rules it is an automatic (stack) variable. In optimized embedded builds, the compiler may avoid creating a real stack slot and instead materialize the value from read-only constant storage (typically `.rodata` and/or an ARM literal pool). +> +> An ARM **literal pool** is a small table of constants that the assembler places near code in memory. Instead of encoding a large immediate value directly in an instruction, the CPU executes a load instruction (such as `ldr`) that reads the constant from that nearby table. That is why Ghidra can show constant loads rather than a classic stack local. + +**What this code does:** +1. Declares a `float` variable `fav_num` and initializes it to `42.5` +2. Initializes the serial output +3. Prints `fav_num` forever in a loop using the `%f` format specifier + +> Tip: **Why `%f` instead of `%d`?** The `%d` format specifier tells `printf` to expect an integer. The `%f` specifier tells it to expect a floating-point number. Using the wrong one would print garbage! + +### Step 1: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x000e_floating-point-data-type.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 2: Verify It's Working + +Open your serial monitor (PuTTY) and you should see: + +**You should see:** + +``` +fav_num: 42.500000 +fav_num: 42.500000 +fav_num: 42.500000 +... +``` + +The program is printing `42.500000` because `printf` with `%f` defaults to 6 decimal places. + +--- + +## Part 2.5: Setting Up Ghidra for Float Analysis + +### Step 3: Start Ghidra + +**Open a terminal and type:** + +```cmd +ghidraRun +``` + +Ghidra will open. Now we need to create a new project. + +### Step 4: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x000e_floating-point-data-type` +5. Click **Finish** + +### Step 5: Import the Binary + +1. Open your file explorer +2. Navigate to the `Embedded-Hacking` folder +3. Find `0x000e_floating-point-data-type.bin` +4. Select Cortex M Little Endian 32 +5. Select Options and set up the .text and offset 10000000 +6. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 6: Configure the Binary Format + +A dialog appears. The file is identified as a "BIN" (raw binary without debug symbols). + +**Click the three dots (...) next to "Language" and:** +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` (the XIP address!) +3. Click **OK** + +### Step 7: Open and Analyze + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete (watch the progress bar in the bottom right). + +--- + +## Part 2.6: Navigating and Resolving Functions + +### Step 8: Find the Functions + +Look at the **Symbol Tree** panel on the left. Expand **Functions**. + +You'll see function names like: +- `FUN_1000019a` +- `FUN_10000210` +- `FUN_10000234` + +These are auto-generated names because we imported a raw binary without symbols! + +### Step 9: Resolve Known Functions + +From our previous chapters, we know what some of these functions are: + +| Ghidra Name | Actual Name | How We Know | +| -------------- | ------------- | -------------------------- | +| `FUN_1000019a` | `data_cpy` | From Week 3 boot analysis | +| `FUN_10000210` | `frame_dummy` | From Week 3 boot analysis | +| `FUN_10000234` | `main` | This is where our code is! | + +### Step 10: Update Main's Signature + +For `main`, let's also fix the return type: + +1. Right-click on `main` in the Decompile window +2. Select **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +--- + +## Part 2.7: Analyzing the Main Function + +### Step 11: Examine Main in Ghidra + +Click on `main` (or `FUN_10000234`). Look at the **Decompile** window: + +You'll see something like: + +```c +int main(void) + +{ + undefined4 uVar1; + undefined4 extraout_r1; + undefined4 uVar2; + undefined4 extraout_r1_00; + + FUN_10002f5c(); + uVar1 = DAT_1000024c; + uVar2 = extraout_r1; + do { + FUN_100030ec(DAT_10000250,uVar2,0,uVar1); + uVar2 = extraout_r1_00; + } while( true ); +} +``` + +### Step 12: Resolve stdio_init_all + +1. Click on `FUN_10002f5c` +2. Right-click -> **Edit Function Signature** +3. Change to: `bool stdio_init_all(void)` +4. Click **OK** + +### Step 13: Resolve printf + +1. Click on `FUN_100030ec` +2. Right-click -> **Edit Function Signature** +3. Change to: `int printf(char *format,...)` +4. Check the **Varargs** checkbox (printf takes variable arguments!) +5. Click **OK** + +### Step 14: Understand the Float Encoding + +Look at the decompiled code after resolving functions: + +```c +int main(void) + +{ + undefined4 uVar1; + undefined4 extraout_r1; + undefined4 uVar2; + undefined4 extraout_r1_00; + + stdio_init_all(); + uVar1 = DAT_1000024c; + uVar2 = extraout_r1; + do { + printf(DAT_10000250,uVar2,0,uVar1); + uVar2 = extraout_r1_00; + } while( true ); +} +``` + +**Where's `float fav_num = 42.5`?** It's been optimized into an immediate value! + +The compiler replaced our float variable with constants passed directly to `printf`. But wait - we see **two** values: `0x0`, in `r2` and `DAT_1000024c` or `0x40454000`, in `r3`. That's because `printf` with `%f` always receives a **double** (64-bit), not a `float` (32-bit). The C standard requires that `float` arguments to variadic functions like `printf` are **promoted to `double`**. + +A 64-bit double is passed in two 32-bit registers: + +| Register | Value | Role | +| -------- | ------------ | ------------ | +| `r2` | `0x00000000` | Low 32 bits | +| `r3` | `0x40454000` | High 32 bits | + +Together they form `0x40454000_00000000` - the IEEE 754 **double-precision** encoding of `42.5`. + +### Step 15: Verify the Double Encoding + +We need to decode `0x4045400000000000` field by field. The two registers give us the full 64-bit value: + +``` +r3 (high 32 bits): 0x40454000 = 0100 0000 0100 0101 0100 0000 0000 0000 +r2 (low 32 bits): 0x00000000 = 0000 0000 0000 0000 0000 0000 0000 0000 +``` + +Laid out as a single 64-bit value with every bit numbered: + +``` +Bit: 63 62-52 (11 bits) 51-32 (20 bits) 31-0 (32 bits) + +---+-----------------------+------------------------------------------+----------------------------------+ + | 0 | 1 0 0 0 0 0 0 0 1 0 0 | 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 00000000000000000000000000000000 | + +---+-----------------------+------------------------------------------+----------------------------------+ + Sign Exponent (11) Mantissa high 20 bits Mantissa low 32 bits + (from r3 bits 19-0) (from r2, all zero) +``` + +**Step-by-step field extraction:** + +**1. Sign bit** + +In IEEE 754, the **sign bit** is the very first (leftmost) bit of the 64-bit double. In the full 64-bit layout we call it **bit 63**: + +``` +64-bit double: [bit 63] [bit 62 ... bit 0] + ^ + sign bit +``` + +But we don't have a single 64-bit register - we have **two** 32-bit registers. The high register `r3` holds bits 63-32 of the double. So bit 63 of the double is the same physical bit as **bit 31 of r3** (the topmost bit of r3): + +``` +r3 holds bits 63-32 of the double +r2 holds bits 31-0 of the double +``` + +Now let's check it. IEEE 754 uses a simple rule for the sign bit: + +| Sign bit | Meaning | +|----------|----------| +| `0` | Positive | +| `1` | Negative | + +``` +r3 = 0x40454000 = 0100 0000 0100 0101 0100 0000 0000 0000 + ^ + r3 bit 31 = 0 -> sign = 0 -> Positive number +``` + +The topmost bit of r3 is `0`, so the number is **positive**. If that bit were `1` instead (e.g. `0xC0454000`), the number would be negative (`-42.5`). + +**2. Exponent - bits 62-52 of the 64-bit value = bits 30-20 of r3** + +Extract bits 30-20 from `0x40454000`: + +``` +0x40454000 in binary: 0 10000000100 01010100000000000000 + sign exponent mantissa (top 20 bits) +``` + +Exponent bits: `10000000100` + +Convert to decimal: $2^{10} + 2^{2} = 1024 + 4 = 1028$ + +But `1028` is **not** the actual power of 2 yet. IEEE 754 stores exponents with a **bias** - a fixed number that gets added during encoding so that the stored value is always positive (no sign bit needed for the exponent). For doubles, the bias is **1023**. + +> Tip: **Why 1023?** The exponent field is 11 bits wide, giving $2^{11} = 2048$ total values. Half of that range should represent negative exponents and half positive. The midpoint is $(2^{11} / 2) - 1 = 1023$. So a stored exponent of `1023` means a real exponent of **0**, values below `1023` are negative exponents, and values above `1023` are positive exponents. + +To recover the real exponent, we subtract the bias: + +$$\text{real exponent} = \text{stored exponent} - \text{bias}$$ + +$$\text{real exponent} = 1028 - 1023 = \mathbf{5}$$ + +This means the number is scaled by $2^5 = 32$. In other words, the mantissa gets shifted left by 5 binary places. + +**3. Mantissa - bits 51-0 of the 64-bit value** + +- **High 20 bits of mantissa** (bits 51-32) = bits 19-0 of r3: + +``` +r3 bits 19-0: 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +``` + +- **Low 32 bits of mantissa** (bits 31-0) = all of r2: + +``` +r2 = 0x00000000 -> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +``` + +Full 52-bit mantissa: + +``` +0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + <- top 20 bits from r3 -> <- bottom 32 bits from r2 (all zero) -> +``` + +IEEE 754 always prepends an **implied leading `1`**, so the actual value represented is: + +``` +1.010101 00000... (the 1. is implicit, not stored) +``` + +**4. Reconstruct the value** + +$$1.010101\text{ (base 2)} \times 2^5$$ + +Shift the binary point 5 places right: + +$$101010.1\text{ (base 2)}$$ + +Now convert each bit position to decimal: + +| Bit position | Power of 2 | Value | +|---|---|---| +| `1` (bit 5) | $2^5$ | 32 | +| `0` (bit 4) | $2^4$ | 0 | +| `1` (bit 3) | $2^3$ | 8 | +| `0` (bit 2) | $2^2$ | 0 | +| `1` (bit 1) | $2^1$ | 2 | +| `0` (bit 0) | $2^0$ | 0 | +| `1` (bit -1) | $2^{-1}$ | 0.5 | + +$$32 + 8 + 2 + 0.5 = \mathbf{42.5}$$ + +### Step 16: Examine the Assembly + +Look at the **Listing** window (assembly view). Find the main function: + +``` + ************************************************************* + * FUNCTION + ************************************************************* + int __stdcall main (void ) + int r0:4 + main+1 XREF[1,1]: 1000018c (c) , 1000018a (*) + main + 10000234 38 b5 push {r3,r4,r5,lr} + 10000236 02 f0 91 fe bl stdio_init_all bool stdio_init_all(void) + 1000023a 00 24 movs r4,#0x0 + 1000023c 03 4d ldr r5,[DAT_1000024c ] = 40454000h + LAB_1000023e XREF[1]: 10000248 (j) + 1000023e 22 46 mov r2,r4 + 10000240 2b 46 mov r3,r5 + 10000242 03 48 ldr r0=>s_fav_num:_%f_100034a8 ,[DAT_10000250 ] = "fav_num: %f\r\n" + = 100034A8h + 10000244 02 f0 52 ff bl printf int printf(char * format, ...) + 10000248 f9 e7 b LAB_1000023e + 1000024a 00 ?? 00h + 1000024b bf ?? BFh + DAT_1000024c XREF[1]: main:1000023c (R) + 1000024c 00 40 45 40 undefine 40454000h + DAT_10000250 XREF[1]: main:10000242 (R) + 10000250 a8 34 00 10 undefine 100034A8h * -> 100034a8 +``` + +> **Key Insight:** The `mov.w r2, #0x0` loads the low 32 bits (all zeros) and `ldr r3, [DAT_...]` loads the high 32 bits (`0x40454000`) of the double. Together, `r2:r3` = `0x40454000_00000000` = `42.5` as a double. + +### Step 17: Find the Format String + +In the Listing view, click on the data reference to find the format string: + +``` + s_fav_num:_%f_100034a8 XREF[1]: main:10000242 (*) + 100034a8 66 61 76 ds "fav_num: %f\r\n" + 5f 6e 75 + 6d 3a 20 +``` + +This confirms `printf` is called with the format string `"fav_num: %f\r\n"` and the double-precision value of `42.5`. + +--- + +## Part 2.8: Patching the Float - Changing 42.5 to 99.0 + +### Step 18: Calculate the New IEEE 754 Encoding + +We want to change `42.5` to `99.0`. First, we need to figure out the double-precision encoding of `99.0`: + +**Step A - Convert the integer part (99) to binary:** + +| Division | Quotient | Remainder | +|---------------|----------|-----------| +| 99 2 | 49 | **1** | +| 49 2 | 24 | **1** | +| 24 2 | 12 | **0** | +| 12 2 | 6 | **0** | +| 6 2 | 3 | **0** | +| 3 2 | 1 | **1** | +| 1 2 | 0 | **1** | + +Read remainders bottom-to-top: 99 (base 10) = 1100011 (base 2) + +**Step B - Convert the fractional part (.0) to binary:** + +There is no fractional part - `.0` is exactly zero, so the fractional binary is just `0`. + +**Step C - Combine:** + +$$99.0\text{ (base 10)} = 1100011.0\text{ (base 2)}$$ + +**Step D - Normalize to IEEE 754 form** (move the binary point so there's exactly one `1` before it): + +$$1100011.0\text{ (base 2)} = 1.100011\text{ (base 2)} \times 2^6$$ + +We shifted the binary point 6 places left, so the exponent is **6**. + +**Step E - Extract the IEEE 754 fields:** + +1. **Sign:** `0` (positive) +2. **Exponent:** $6 + 1023 = 1029 = 10000000101\text{ (base 2)}$ +3. **Mantissa:** `1000110000000000...` (everything after the `1.`, padded with zeros to 52 bits) +4. **Full double:** `0x4058C00000000000` + +| Register | Old Value | New Value | +| -------- | ------------ | ------------ | +| `r2` | `0x00000000` | `0x00000000` | +| `r3` | `0x40454000` | `0x4058C000` | + +Since `r2` stays `0x00000000`, we only need to patch the high word loaded into `r3`. + +### Step 19: Find the Value to Patch + +Look in the Listing view for the data that loads the high word of the double: + +``` + 1000024c 00 40 45 40 undefined4 40454000h +``` + +This is the 32-bit constant that gets loaded into `r3` - the high word of our double `42.5`. + +### Step 20: Patch the Constant + +1. Click on Window -> Bytes +2. Click on the Pencil icon to enable byte editing +3. At address `1000024c`, overwrite `00 40 45 40` with `00 C0 58 40` (little-endian for `0x40454000 -> 0x4058C000`) +4. Press Enter + +This changes the high word from `0x40454000` (42.5 as double) to `0x4058C000` (99.0 as double). + +--- + +## Part 2.9: Export and Test the Hacked Binary + +### Step 21: Export the Patched Binary + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. Navigate to your build directory +4. Name the file `0x000e_floating-point-data-type-h.bin` +5. Click **OK** + +### Step 22: Convert to UF2 Format + +**Open a terminal and navigate to your project directory:** + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x000e_floating-point-data-type +``` + +**Run the conversion command:** + +```cmd +python ..\uf2conv.py build\0x000e_floating-point-data-type-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 23: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Open your serial monitor + +**You should see:** + +``` +fav_num: 99.000000 +fav_num: 99.000000 +fav_num: 99.000000 +... +``` + + **BOOM! We hacked the float!** The value changed from `42.5` to `99.0`! + +--- + +## Part 3: Understanding Double-Precision Floating-Point Data Types + +### What is a Double? + +A **double** (short for "double-precision floating-point") is like a `float` but with **twice the precision**. While a `float` uses 32 bits, a `double` uses **64 bits (8 bytes)**, giving it roughly **15-16 significant decimal digits** of precision compared to a float's ~7. + +``` ++-----------------------------------------------------------------+ +| IEEE 754 Double-Precision (64-bit double) | +| | +| +------+-----------+--------------------------------------+ | +| | Sign | Exponent | Mantissa (Fraction) | | +| | 1bit | 11 bits | 52 bits | | +| +------+-----------+--------------------------------------+ | +| | +| Value = (-1)^sign * 2^(exponent-1023) * 1.mantissa | +| | +| Example: 42.52525 | +| Sign: 0 (positive) | +| Exponent: 10000000100 (1028 - 1023 = 5) | +| Mantissa: 0101010000110011101101100100010110100001110010101100 | +| Hex: 0x4045433B645A1CAC | +| | ++-----------------------------------------------------------------+ +``` + +### Float vs Double - Key Differences + +| Property | Float (`float`) | Double (`double`) | +| --------------- | ---------------------- | --------------------------- | +| **Size** | 4 bytes (32 bits) | 8 bytes (64 bits) | +| **Precision** | ~7 decimal digits | ~15 decimal digits | +| **Exponent** | 8 bits (bias 127) | 11 bits (bias 1023) | +| **Mantissa** | 23 bits | 52 bits | +| **Range** | 3.4 10^38 | 1.8 10^308 | +| **printf** | `%f` | `%lf` | +| **ARM passing** | Promoted to double | Native in `r2:r3` | + +> Tip: **Why does precision matter?** With a `float`, the value `42.52525` might be stored as `42.525249` due to rounding. A `double` can represent it as `42.525250` with much higher fidelity. For scientific or financial applications, that extra precision is critical! + +### Our Double-Precision Program + +Let's look at a program that uses a `double` variable: + +**File: `0x0011_double-floating-point-data-type.c`** + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + double fav_num = 42.52525; + + stdio_init_all(); + + while (true) + printf("fav_num: %lf\r\n", fav_num); +} +``` + +**What this code does:** +1. Declares a `double` variable `fav_num` and initializes it to `42.52525` +2. Initializes the serial output +3. Prints `fav_num` forever in a loop using the `%lf` format specifier + +> Tip: **`%lf` vs `%f`:** While `printf` actually treats `%f` and `%lf` identically (both expect a `double`), using `%lf` makes your intent clear - you're explicitly working with a `double`, not a `float`. It's good practice to match the format specifier to your variable type. + +### Step 1: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x000A_intro-to-doubles.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 2: Verify It's Working + +Open your serial monitor (PuTTY) and you should see: + +**You should see:** + +``` +fav_num: 42.525250 +fav_num: 42.525250 +fav_num: 42.525250 +... +``` + +The program is printing `42.525250` because `printf` with `%lf` defaults to 6 decimal places. + +--- + +## Part 3.5: Setting Up Ghidra for Double Analysis + +### Step 3: Start Ghidra + +**Open a terminal and type:** + +```cmd +ghidraRun +``` + +Ghidra will open. Now we need to create a new project. + +### Step 4: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x000A_intro-to-doubles` +5. Click **Finish** + +### Step 5: Import the Binary + +1. Open your file explorer +2. Navigate to the `Embedded-Hacking` folder +3. Find `0x0011_double-floating-point-data-type.bin` +4. Select Cortex M Little Endian 32 +5. Select Options and set up the .text and offset 10000000 +6. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 6: Configure the Binary Format + +A dialog appears. The file is identified as a "BIN" (raw binary without debug symbols). + +**Click the three dots (...) next to "Language" and:** +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` (the XIP address!) +3. Click **OK** + +### Step 7: Open and Analyze + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete (watch the progress bar in the bottom right). + +--- + +## Part 3.6: Navigating and Resolving Functions + +### Step 8: Find the Functions + +Look at the **Symbol Tree** panel on the left. Expand **Functions**. + +You'll see function names like: +- `FUN_1000019a` +- `FUN_10000210` +- `FUN_10000238` + +These are auto-generated names because we imported a raw binary without symbols! + +### Step 9: Resolve Known Functions + +From our previous chapters, we know what some of these functions are: + +| Ghidra Name | Actual Name | How We Know | +| -------------- | ------------- | -------------------------- | +| `FUN_1000019a` | `data_cpy` | From Week 3 boot analysis | +| `FUN_10000210` | `frame_dummy` | From Week 3 boot analysis | +| `FUN_10000238` | `main` | This is where our code is! | + +### Step 10: Update Main's Signature + +For `main`, let's also fix the return type: + +1. Right-click on `main` in the Decompile window +2. Select **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +--- + +## Part 3.7: Analyzing the Main Function + +### Step 11: Examine Main in Ghidra + +Click on `main` (or `FUN_10000234`). Look at the **Decompile** window: + +You'll see something like: + +```c +int main(void) + +{ + undefined4 uVar1; + undefined4 uVar2; + undefined4 extraout_r1; + undefined4 uVar3; + undefined4 extraout_r1_00; + + uVar2 = DAT_10000258; + uVar1 = DAT_10000254; + FUN_10002f64(); + uVar3 = extraout_r1; + do { + FUN_100030f4(DAT_10000250,uVar3,uVar1,uVar2); + uVar3 = extraout_r1_00; + } while( true ); +} +``` + +### Step 12: Resolve stdio_init_all + +1. Click on `FUN_10002f64` +2. Right-click -> **Edit Function Signature** +3. Change to: `bool stdio_init_all(void)` +4. Click **OK** + +### Step 13: Resolve printf + +1. Click on `FUN_100030f4` +2. Right-click -> **Edit Function Signature** +3. Change to: `int printf(char *format,...)` +4. Check the **Varargs** checkbox (printf takes variable arguments!) +5. Click **OK** + +### Step 14: Understand the Double Encoding + +Look at the decompiled code after resolving functions: + +```c +int main(void) + +{ + undefined4 uVar1; + undefined4 uVar2; + undefined4 extraout_r1; + undefined4 uVar3; + undefined4 extraout_r1_00; + + uVar2 = DAT_10000258; + uVar1 = DAT_10000254; + stdio_init_all(); + uVar3 = extraout_r1; + do { + printf(DAT_10000250,uVar3,uVar1,uVar2); + uVar3 = extraout_r1_00; + } while( true ); +} +``` + +**Where's `double fav_num = 42.52525`?** It's been optimized into immediate values! + +This time we see **two** non-zero values: `0x645a1cac` and `0x4045433b`. Unlike the float example where the low word was `0x0`, a double with a fractional part like `42.52525` needs **all 52 mantissa bits** - so both halves carry data. + +A 64-bit double is passed in two 32-bit registers: + +| Register | Value | Role | +| -------- | ------------ | ------------ | +| `r2` | `0x645A1CAC` | Low 32 bits | +| `r3` | `0x4045433B` | High 32 bits | + +Together they form `0x4045433B645A1CAC` - the IEEE 754 **double-precision** encoding of `42.52525`. + +> **Key Difference from Float:** In the float example, `r2` was `0x00000000` because `42.5` has a clean fractional part. But `42.52525` has a repeating binary fraction, so the low 32 bits are non-zero (`0x645A1CAC`). This means **both** registers matter when patching doubles with complex fractional values! + +### Step 15: Verify the Double Encoding + +We need to decode `0x4045433B645A1CAC` field by field. The two registers give us the full 64-bit value: + +``` +r3 (high 32 bits): 0x4045433B = 0100 0000 0100 0101 0100 0011 0011 1011 +r2 (low 32 bits): 0x645A1CAC = 0110 0100 0101 1010 0001 1100 1010 1100 +``` + +Laid out as a single 64-bit value with every bit numbered: + +``` +Bit: 63 62-52 (11 bits) 51-32 (20 bits) 31-0 (32 bits) + +---+-----------------------+------------------------------------------+------------------------------------------+ + | 0 | 1 0 0 0 0 0 0 0 1 0 0 | 0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1 | 01100100010110100001110010101100 | + +---+-----------------------+------------------------------------------+------------------------------------------+ + Sign Exponent (11) Mantissa high 20 bits Mantissa low 32 bits + (from r3 bits 19-0) (from r2) +``` + +**Step-by-step field extraction:** + +**1. Sign bit** + +The sign bit is bit 63 of the 64-bit double, which is bit 31 of r3 (the high register holds bits 63-32): + +``` +r3 = 0x4045433B = 0100 0000 0100 0101 0100 0011 0011 1011 + ^ + r3 bit 31 = 0 -> sign = 0 -> Positive number ✓ +``` + +**2. Exponent - bits 62-52 = bits 30-20 of r3** + +Extract bits 30-20 from `0x4045433B`: + +``` +0x4045433B in binary: 0 10000000100 01010100001100111011 + sign exponent mantissa (top 20 bits) +``` + +Exponent bits: `10000000100` + +Convert to decimal: $2^{10} + 2^{2} = 1024 + 4 = 1028$ + +Subtract the bias (same formula as Part 2 - the bias is 1023 for all doubles): + +$$\text{real exponent} = 1028 - 1023 = \mathbf{5}$$ + +This means the mantissa gets shifted left by 5 binary places (i.e. multiplied by $2^5 = 32$). + +**3. Mantissa - bits 51-0** + +Unlike the `42.5` example where r2 was all zeros, **both registers contribute non-zero bits** here: + +- **High 20 bits of mantissa** (bits 51-32) = bits 19-0 of r3: + +``` +r3 bits 19-0: 0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1 +``` + +- **Low 32 bits of mantissa** (bits 31-0) = all of r2: + +``` +r2 = 0x645A1CAC -> 0 1 1 0 0 1 0 0 0 1 0 1 1 0 1 0 0 0 0 1 1 1 0 0 1 0 1 0 1 1 0 0 +``` + +Full 52-bit mantissa: + +``` +0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1 | 0 1 1 0 0 1 0 0 0 1 0 1 1 0 1 0 0 0 0 1 1 1 0 0 1 0 1 0 1 1 0 0 + <- top 20 bits from r3 -> <- bottom 32 bits from r2 -> +``` + +IEEE 754 always prepends an **implied leading `1`**, so the actual value represented is: + +``` +1.0101010000110011101101100100010110100001110010101100 (the 1. is implicit, not stored) +``` + +**4. Reconstruct the value** + +$$1.0101010000110011101101100100...\text{ (base 2)} \times 2^5$$ + +Shift the binary point 5 places right: + +$$101010.10000110011101101100100010110100001110010101100\text{ (base 2)}$$ + +**Integer part** (`101010`): + +| Bit position | Power of 2 | Value | +|---|---|---| +| `1` (bit 5) | $2^5$ | 32 | +| `0` (bit 4) | $2^4$ | 0 | +| `1` (bit 3) | $2^3$ | 8 | +| `0` (bit 2) | $2^2$ | 0 | +| `1` (bit 1) | $2^1$ | 2 | +| `0` (bit 0) | $2^0$ | 0 | + +$$32 + 8 + 2 = \mathbf{42}$$ + +**Fractional part** (`.10000110011101101...`): + +| Bit position | Power of 2 | Decimal value | +|---|---|---| +| `1` (bit -1) | $2^{-1}$ | 0.5 | +| `0` (bit -2) | $2^{-2}$ | 0 | +| `0` (bit -3) | $2^{-3}$ | 0 | +| `0` (bit -4) | $2^{-4}$ | 0 | +| `0` (bit -5) | $2^{-5}$ | 0 | +| `1` (bit -6) | $2^{-6}$ | 0.015625 | +| `1` (bit -7) | $2^{-7}$ | 0.0078125 | +| `0` (bit -8) | $2^{-8}$ | 0 | +| `0` (bit -9) | $2^{-9}$ | 0 | +| `1` (bit -10) | $2^{-10}$ | 0.0009765625 | +| `1` (bit -11) | $2^{-11}$ | 0.00048828125 | +| `1` (bit -12) | $2^{-12}$ | 0.000244140625 | +| ... | ... | *(remaining 35 bits add smaller and smaller fractions)* | + +First 12 fractional bits sum: $0.5 + 0.015625 + 0.0078125 + 0.0009765625 + 0.00048828125 + 0.000244140625 \approx 0.5251$ + +The remaining 35 fractional bits refine this to $\approx 0.52525$. This is because `0.52525` is a **repeating fraction** in binary - it can never be represented with a finite number of bits, so double precision stores the closest possible 52-bit approximation. + +$$42 + 0.52525 = \mathbf{42.52525} \checkmark$$ + +### Step 16: Examine the Assembly + +Look at the **Listing** window (assembly view). Find the main function: + +``` + ************************************************************* + * FUNCTION + ************************************************************* + int __stdcall main (void ) + int r0:4 + main+1 XREF[1,1]: 1000018c (c) , 1000018a (*) + main + 10000238 38 b5 push {r3,r4,r5,lr} + 1000023a 06 a5 adr r5,[0x10000254 ] + 1000023c d5 e9 00 45 ldrd r4,r5,[r5,#0x0 ]=>DAT_10000254 = 645A1CACh + = 4045433Bh + 10000240 02 f0 90 fe bl stdio_init_all bool stdio_init_all(void) + LAB_10000244 XREF[1]: 1000024e (j) + 10000244 22 46 mov r2,r4 + 10000246 2b 46 mov r3,r5 + 10000248 01 48 ldr r0=>s_fav_num:_%lf_100034b0 ,[DAT_10000250 ] = "fav_num: %lf\r\n" + = 100034B0h + 1000024a 02 f0 53 ff bl printf int printf(char * format, ...) + 1000024e f9 e7 b LAB_10000244 + DAT_10000250 XREF[1]: main:10000248 (R) + 10000250 b0 34 00 10 undefine 100034B0h ? -> 100034b0 + DAT_10000254 XREF[1]: main:1000023c (R) + 10000254 ac 1c 5a 64 undefine 645A1CACh + DAT_10000258 XREF[1]: main:1000023c (R) + 10000258 3b 43 45 40 undefine 4045433Bh +``` + +> **Key Insight:** Notice that **both** `r2` and `r3` are loaded from data constants using `ldr`. Compare this to the float example where `r2` was loaded with `mov.w r2, #0x0`. Because `42.52525` requires all 52 mantissa bits, neither word can be zero - the compiler must store both halves as separate data constants. + +### Step 17: Find the Format String + +In the Listing view, click on the data reference to find the format string: + +``` + s_fav_num:_%lf_100034b0 XREF[1]: main:10000248 (*) + 100034b0 66 61 76 ds "fav_num: %lf\r\n" + 5f 6e 75 + 6d 3a 20 +``` + +This confirms `printf` is called with the format string `"fav_num: %lf\r\n"` and the double-precision value of `42.52525`. + +--- + +## Part 3.8: Patching the Double - Changing 42.52525 to 99.99 + +### Step 18: Calculate the New IEEE 754 Encoding + +We want to change `42.52525` to `99.99`. First, we need to figure out the double-precision encoding of `99.99`: + +1. $99.99 = 1.5623... \times 2^6 = 1.100011111111...\text{ (base 2)} \times 2^6$ +2. **Sign:** `0` (positive) +3. **Exponent:** $6 + 1023 = 1029 = 10000000101\text{ (base 2)}$ +4. **Mantissa:** `1000111111010111000010100011110101110000101000111... (base 2)` +5. **Full double:** `0x4058FF5C28F5C28F` + +| Register | Old Value | New Value | +| -------- | ------------ | ------------ | +| `r2` | `0x645A1CAC` | `0x28F5C28F` | +| `r3` | `0x4045433B` | `0x4058FF5C` | + +Unlike the float example, **both** registers change! The value `99.99` has a repeating binary fraction, so both the high and low words are different. + +### Step 19: Find the Values to Patch + +Look in the Listing view for the two data constants: + +**Low word (loaded into `r2`):** +``` + 10000254 ac 1c 5a 64 undefined4 645A1CACh +``` + +**High word (loaded into `r3`):** +``` + 10000258 3b 43 45 40 undefined4 4045433Bh +``` + +### Step 20: Patch Both Constants + +**Patch the low word:** +1. Click on the data at address `10000254` containing `645A1CAC` +2. Open the Bytes window and enable byte editing (Pencil icon) +3. Overwrite bytes `ac 1c 5a 64` with `8f c2 f5 28` (little-endian for `0x645A1CAC -> 0x28F5C28F`) +4. Press Enter + +**Patch the high word:** +1. Click on the data at address `10000258` containing `4045433B` +2. Keep byte editing enabled in the Bytes window +3. Overwrite bytes `3b 43 45 40` with `5c ff 58 40` (little-endian for `0x4045433B -> 0x4058FF5C`) +4. Press Enter + +This changes the full 64-bit double from `0x4045433B645A1CAC` (42.52525) to `0x4058FF5C28F5C28F` (99.99). + +> **Key Difference from Float Patching:** When we patched the float `42.5`, we only needed to change one word (the high word in `r3`) because the low word was all zeros. With `42.52525 -> 99.99`, **both** words change. Always check whether the low word is non-zero before patching! + +--- + +## Part 3.9: Export and Test the Hacked Binary + +### Step 21: Export the Patched Binary + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. Navigate to your build directory +4. Name the file `0x0011_double-floating-point-data-type-h.bin` +5. Click **OK** + +### Step 22: Convert to UF2 Format + +**Open a terminal and navigate to your project directory:** + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0011_double-floating-point-data-type +``` + +**Run the conversion command:** + +```cmd +python ..\uf2conv.py build\0x0011_double-floating-point-data-type-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 23: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Open your serial monitor + +**You should see:** + +``` +fav_num: 99.990000 +fav_num: 99.990000 +fav_num: 99.990000 +... +``` + + **BOOM! We hacked the double!** The value changed from `42.52525` to `99.99`! + +--- + +## Part 3.95: Summary - Float and Double Analysis + +### What We Accomplished + +1. **Learned about IEEE 754** - How floating-point numbers are encoded in 32-bit (float) and 64-bit (double) formats +2. **Discovered float-to-double promotion** - `printf` with `%f` always receives a `double`, even when you pass a `float` +3. **Decoded register pairs** - 64-bit doubles are split across `r2` (low) and `r3` (high) +4. **Patched a float value** - Changed `42.5` to `99.0` by modifying only the high word +5. **Patched a double value** - Changed `42.52525` to `99.99` by modifying **both** words +6. **Understood the key difference** - Clean fractions (like `42.5`) have a zero low word; complex fractions (like `42.52525`) require patching both words + +### IEEE 754 Quick Reference for Common Values + +| Value | Double Hex | High Word (r3) | Low Word (r2) | +| -------- | ------------------------ | --------------- | -------------- | +| 42.0 | `0x4045000000000000` | `0x40450000` | `0x00000000` | +| 42.5 | `0x4045400000000000` | `0x40454000` | `0x00000000` | +| 42.52525 | `0x4045433B645A1CAC` | `0x4045433B` | `0x645A1CAC` | +| 43.0 | `0x4045800000000000` | `0x40458000` | `0x00000000` | +| 99.0 | `0x4058C00000000000` | `0x4058C000` | `0x00000000` | +| 99.99 | `0x4058FF5C28F5C28F` | `0x4058FF5C` | `0x28F5C28F` | +| 100.0 | `0x4059000000000000` | `0x40590000` | `0x00000000` | +| 3.14 | `0x40091EB851EB851F` | `0x40091EB8` | `0x51EB851F` | + +### The Float/Double Patching Workflow + +``` ++-----------------------------------------------------------------+ +| 1. Identify the float/double value in the decompiled view | +| - Look for hex constants like 0x40454000 or 0x4045433B | ++-----------------------------------------------------------------+ +| 2. Determine if it's float (32-bit) or double (64-bit) | +| - printf promotes floats to doubles! | +| - Check if value spans r2:r3 (double) or just r0 (float) | ++-----------------------------------------------------------------+ +| 3. Check if the low word (r2) is zero or non-zero | +| - Zero low word = only patch the high word | +| - Non-zero low word = patch BOTH words | ++-----------------------------------------------------------------+ +| 4. Calculate the new IEEE 754 encoding | +| - Convert your desired value to IEEE 754 | +| - Split into high/low words | ++-----------------------------------------------------------------+ +| 5. Patch the constant(s) in Ghidra | +| - Edit bytes in the Bytes window (Pencil mode) | +| - Replace the old encoding with the new one | ++-----------------------------------------------------------------+ +| 6. Export -> Convert to UF2 -> Flash -> Verify | +| - Same workflow as integer patching | ++-----------------------------------------------------------------+ +``` + +> Tip: **Key takeaway:** Hacking doubles is the same process as hacking floats - find the IEEE 754 constant, calculate the new encoding, patch it. The only extra step is checking whether the **low word** (`r2`) is also non-zero. Clean values like `42.5` only need one patch; messy fractions like `42.52525` need two! + +--- + +--- + +## Key Takeaways + +1. **Integers have fixed sizes** - `uint8_t` is 1 byte (0-255), `int8_t` is 1 byte (-128 to 127). The `u` prefix means unsigned. + +2. **IEEE 754 encodes floats in binary** - Sign bit, exponent (with bias), and mantissa form the encoding for both 32-bit floats and 64-bit doubles. + +3. **printf promotes floats to doubles** - Even when you pass a `float`, `printf` receives a 64-bit `double` due to C's variadic function rules. + +4. **64-bit values span two registers** - On ARM Cortex-M33, doubles use `r2` (low 32 bits) and `r3` (high 32 bits). + +5. **Clean fractions have zero low words** - Values like `42.5` have `0x00000000` in the low word; complex fractions like `42.52525` have non-zero low words. + +6. **Inline assembly controls hardware directly** - The `mcrr` coprocessor instruction talks to the GPIO block without any SDK overhead. + +7. **Binary patching works on any data type** - Integers, floats, and doubles can all be patched in Ghidra using the same workflow. + +--- + +## Glossary + +| Term | Definition | +| ----------------------- | ------------------------------------------------------------------------------ | +| **Bias** | Constant added to the exponent in IEEE 754 (127 for float, 1023 for double) | +| **Double** | 64-bit floating-point type following IEEE 754 double-precision format | +| **Exponent** | Part of IEEE 754 encoding that determines the magnitude of the number | +| **Float** | 32-bit floating-point type following IEEE 754 single-precision format | +| **FUNCSEL** | Function Select - register field that assigns a GPIO pin's function (e.g., SIO)| +| **GPIO** | General Purpose Input/Output - controllable pins on a microcontroller | +| **IEEE 754** | International standard for floating-point arithmetic and binary encoding | +| **Inline Assembly** | Assembly code embedded directly within C source using `__asm volatile` | +| **int8_t** | Signed 8-bit integer type (-128 to 127) | +| **IO_BANK0** | Register block at `0x40028000` that controls GPIO pin function selection | +| **Mantissa** | Fractional part of IEEE 754 encoding (23 bits for float, 52 bits for double) | +| **mcrr** | ARM coprocessor register transfer instruction used for GPIO control | +| **PADS_BANK0** | Register block at `0x40038000` that controls GPIO pad electrical properties | +| **Promotion** | Automatic conversion of a smaller type to a larger type (float -> double) | +| **Register Pair** | Two 32-bit registers (r2:r3) used together to hold a 64-bit value | +| **UF2** | USB Flashing Format - file format for Pico 2 firmware | +| **uint8_t** | Unsigned 8-bit integer type (0 to 255) | + +--- + +## Additional Resources + +### GPIO Coprocessor Reference + +The RP2350 GPIO coprocessor instructions: + +| Instruction | Description | +| --------------------------- | ---------------------------- | +| `mcrr p0, #4, Rt, Rt2, c0` | Set/clear GPIO output | +| `mcrr p0, #4, Rt, Rt2, c4` | Set/clear GPIO output enable | + +### RP2350 Memory Map Quick Reference + +| Address | Description | +| ------------ | ------------------------ | +| `0x10000000` | XIP Flash (code) | +| `0x20000000` | SRAM (data) | +| `0x40028000` | IO_BANK0 (GPIO control) | +| `0x40038000` | PADS_BANK0 (pad control) | +| `0xd0000000` | SIO (single-cycle I/O) | + +### IEEE 754 Encoding Formula + +``` ++-----------------------------------------------------------------+ +| Float (32-bit): [1 sign] [8 exponent] [23 mantissa] | +| Double (64-bit): [1 sign] [11 exponent] [52 mantissa] | +| | +| Value = (-1)^sign * 2^(exponent - bias) * (1 + mantissa) | +| | +| Float bias: 127 | +| Double bias: 1023 | ++-----------------------------------------------------------------+ +``` + +--- + +**Remember:** Every binary you encounter in the real world can be analyzed and understood using these same techniques. Whether it's an integer, a float, or a double - it's all just bits waiting to be decoded. Practice makes perfect! + +Happy hacking! + diff --git a/WEEK05/slides/WEEK05-IMG00.svg b/WEEK05/slides/WEEK05-IMG00.svg new file mode 100644 index 0000000..77db88a --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 05 + + +Integers and Floats in Embedded Systems: +Debugging and Hacking Integers and Floats +w/ Intermediate GPIO Output Analysis + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK05/slides/WEEK05-IMG01.svg b/WEEK05/slides/WEEK05-IMG01.svg new file mode 100644 index 0000000..1659bfa --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG01.svg @@ -0,0 +1,77 @@ + + + + + +Integer Data Types +Fixed-Size Types for Embedded Systems + + + +uint8_t +Unsigned 8-bit +1 byte +Range: +0 to 255 +Ages, counts, always positive + + + +int8_t +Signed 8-bit +1 byte +Range: +-128 to 127 +Temperature, can be negative + + + +uint16_t +Unsigned 16-bit +2 bytes +Range: +0 to 65,535 +Sensor readings, medium values + + + +uint32_t +Unsigned 32-bit +4 bytes +Range: +0 to ~4 billion +Addresses, timestamps + + + +Code Example + +uint8_t age = 43; +// unsigned, 0-255 +int8_t range = -42; +// signed, -128 to 127 + + + +Key Insight +The +u +prefix means +unsigned +(no negatives) +Without +u += signed (allows negatives) +Choose the smallest type that fits your data + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG02.svg b/WEEK05/slides/WEEK05-IMG02.svg new file mode 100644 index 0000000..229f5e9 --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG02.svg @@ -0,0 +1,79 @@ + + + + + +Two's Complement +How Negative Numbers are Stored + + + +Encoding -42 as int8_t + + +Step 1: Start +42 = 0x2A + + +Step 2: Flip +~0x2A = 0xD5 + + +Step 3: Add 1 +0xD5+1 = 0xD6 + +Binary: +00101010 +-> +11010101 +-> +11010110 + +Result: -42 stored as 0xD6 in memory +Top bit = 1 means negative + + + +Signed vs Unsigned: Same Bits! + +Hex +Binary +uint8_t +int8_t + + + +0x2A +00101010 +42 +42 + +0xD6 +11010110 +214 +-42 + +Same byte 0xD6 = 214 unsigned, -42 signed + + + +GDB Verification + + +(gdb) +x/1xb &range +0x200003e7: +0xd6 +// -42 in memory + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG03.svg b/WEEK05/slides/WEEK05-IMG03.svg new file mode 100644 index 0000000..497a13d --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG03.svg @@ -0,0 +1,75 @@ + + + + + +Inline Assembly GPIO +Direct Hardware Control via ASM + + + +GPIO Init Loop (pins 16-19) + + +1. Config Pad +PADS_BANK0 +Clear OD+ISO, set IE +0x40038000 + + +2. Set Function +IO_BANK0 +FUNCSEL = 5 (SIO) +0x40028004 + + +3. Enable Out +GPIO Coprocessor +mcrr p0,#4,r4,r5,c4 +Output Enable + +Loop: r0 = 16 to 19 +Red, Green, Blue, Yellow LEDs +Each pin: pad config + function select + OE + + + +Blink Loop + + +mcrr p0,#4,r4,r5,c0 + +r4 = pin, r5 = value +c0 = output value register +r5=1 ON, r5=0 OFF + + + +Pin Cycling + + +pin++; +if (pin > 18) pin=16; + +Cycles: 16 -> 17 -> 18 -> 16 +Red -> Green -> Blue -> repeat + + + +Why Inline Assembly? +gpio_put(16,1) calls +mcrr +underneath +Inline ASM shows what the SDK does at hardware level + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG04.svg b/WEEK05/slides/WEEK05-IMG04.svg new file mode 100644 index 0000000..ecfa467 --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG04.svg @@ -0,0 +1,83 @@ + + + + + +IEEE 754 Float +32-bit Single Precision Encoding + + + +Float Bit Layout (32 bits) + + +S + + +Exponent (8) + + +Mantissa (23) + +1 bit +sign + +8 bits +bias=127 + +23 bits ++implicit 1 + + + +Decode Formula +Value = (-1)^sign × 2^(exp-127) × (1 + mantissa) +Sign determines +/- Exponent scales with bias 127 Mantissa adds precision +Special cases: exp=0 or 255 (denorm, inf, NaN) + + + +Decode Example: 42.5 + +Sign Bit: 0 +Positive number + +Exponent: 10000100 += 132 decimal + +Bias subtraction: 132 - 127 = 5 + +Mantissa: 01010100...0 += 1.010101 + +Combine: 1.010101 × 2^5 = 42.5 + +Hex: 0x422A0000 + +Binary: 01000010001010100000000000000000 + + + +32-bit Storage Comparison + +Integer: +Exact values +Range: -2.1B to +2.1B +No decimals + +Float: +Approximate values +Range: ±10^38 +~7 sig digits + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG05.svg b/WEEK05/slides/WEEK05-IMG05.svg new file mode 100644 index 0000000..90e591f --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG05.svg @@ -0,0 +1,76 @@ + + + + + +Float in Ghidra +Analyzing 42.5 in the Binary + + + +Decompiled main() + + +int main(void) { +stdio_init_all(); +uVar1 = DAT_1000024c; +do { +printf(fmt,0,uVar1); +} while(true); + + + +Key Discovery + +printf with %f always +receives a +double +(64-bit) + +C promotes float to double +for variadic functions! + + +42.5 sent as double + + + +Register Pair r2:r3 + + +r2 (low): +0x00000000 + + +r3 (high): +0x40454000 + +Combined: 0x4045400000000000 += 42.5 + + + +Assembly View + + +1000023a +00 24 +movs r4, #0x0 +// r2 = 0 + +1000023c +03 4d +ldr r5,[DAT...] +// r3 = 0x40454000 + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG06.svg b/WEEK05/slides/WEEK05-IMG06.svg new file mode 100644 index 0000000..3c82e9e --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG06.svg @@ -0,0 +1,87 @@ + + + + + +Patching Float +Changing 42.5 to 99.0 in Ghidra + + + +Calculate 99.0 as Double + +99 decimal = +1100011 +binary + +Normalize: +1.100011 x 2^6 + +Sign: +0 +Exp: +6+1023 = 1029 + +Mantissa: +100011000...0 + +Full double: 0x4058C00000000000 + + + +Before (42.5) + + +r2: +0x00000000 + + +r3: +0x40454000 + +Output: fav_num: 42.500000 + + +After (99.0) + + +r2: +0x00000000 +same! + + +r3: +0x4058C000 +changed + +Output: fav_num: 99.000000 + + + +Patch in Ghidra + + +1. Window: Bytes +Open byte editor + + +2. Find 00404540 +High word of 42.5 + + +3. Patch 00C05840 +High word of 99.0 + +Only one word to patch (low word is 0) + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG07.svg b/WEEK05/slides/WEEK05-IMG07.svg new file mode 100644 index 0000000..d67d52e --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG07.svg @@ -0,0 +1,75 @@ + + + + + +Double Precision +IEEE 754 64-bit Floating Point + + + +64-Bit Layout + + +Sign +1 bit + + +Exponent +11 bits (bias 1023) + + +Mantissa (Fraction) +52 bits + +Formula: (-1)^S x 1.Mantissa x 2^(Exp-1023) + + + +Encoding 42.52525 + +Sign: +0 (positive) + +Exponent: +5 + 1023 = 1028 += 0x404 (hex) + +Mantissa: +0101010000110011... + +Full 64-bit hex: +0x4045433B645A1CAC + + + +Float (32-bit) + +Size: 4 bytes +Exp: 8 bits +Mantissa: 23 bits +Precision: ~7 digits +Bias: 127 +1 register (ARM) + + +Double (64-bit) + +Size: 8 bytes +Exp: 11 bits +Mantissa: 52 bits +Precision: ~15 digits +Bias: 1023 +2 registers (r2:r3) + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG08.svg b/WEEK05/slides/WEEK05-IMG08.svg new file mode 100644 index 0000000..ad7749c --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG08.svg @@ -0,0 +1,71 @@ + + + + + +Double in Ghidra +Analyzing 42.52525 in memory + + + +Decompiled main() + + +int main(void) { +double fav_num + = 42.52525; +stdio_init_all(); +} + + + +Key Difference + +Float (42.5): +r2 = 0x00000000 (zero!) +r3 = 0x40454000 + +Double (42.52525): +r2 = 0x645A1CAC (non-zero!) + + + +Register Pair r2:r3 + + +r3 (HIGH): +0x4045433B + + +r2 (LOW): +0x645A1CAC + +Combined: 0x4045433B645A1CAC = 42.52525 + + + +Assembly (ldrd) + + +ldrd r2,r3,[PC,#0x10] +; Loads BOTH words at once +; r2 gets low word +; r3 gets high word + + +ldrd = Load +Register +Doubleword +ARM Cortex-M33 + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG09.svg b/WEEK05/slides/WEEK05-IMG09.svg new file mode 100644 index 0000000..fcf359f --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG09.svg @@ -0,0 +1,88 @@ + + + + + +Patching Double +Changing 42.52525 to 99.99 + + + +99.99 as IEEE 754 Double + +Sign: +0 +Exp: +6 + 1023 = 1029 + +Result: +0x4058FF5C28F5C28F + +r3 (HIGH) = 0x4058FF5C +r2 (LOW) = 0x28F5C28F + + + +Before (42.52525) + + +r3: +0x4045433B + + +r2: +0x645A1CAC + +printf: 42.525250 +Both words non-zero + + +After (99.99) + + +r3: +0x4058FF5C +changed + + +r2: +0x28F5C28F +changed + +printf: 99.990000 +BOTH words changed! + + + +Float Patch + +Words changed: +1 +r2 (low): +stays 0x0 +r3 (high): +patched +Easier to patch + + +Double Patch + +Words changed: +2 +r2 (low): +patched +r3 (high): +patched +Both words must change + \ No newline at end of file diff --git a/WEEK05/slides/WEEK05-IMG10.svg b/WEEK05/slides/WEEK05-IMG10.svg new file mode 100644 index 0000000..ce71556 --- /dev/null +++ b/WEEK05/slides/WEEK05-IMG10.svg @@ -0,0 +1,101 @@ + + + + + +IEEE 754 & Data Types +Data Types and IEEE 754 Reference + + + +IEEE 754 Hex Values + + +Value +Float (32b) +Double (64b) + + + + +42.0 +0x42280000 +0x4045000000000000 + + +42.5 +0x422A0000 +0x4045400000000000 + + +99.0 +0x42C60000 +0x4058C00000000000 + + +99.99 +0x42C7F5C3 +0x4058FF5C28F5C28F + + +3.14 +0x4048F5C3 +0x40091EB851EB851F + + +100.0 +0x42C80000 +0x4059000000000000 + +Tip: float low word often 0x0; double low word usually non-zero + + + +Patching Workflow + + +1. Identify +float / double + + +2. Check r2 +zero = float + + +3. Calculate +new hex value + + +4. Patch +in byte editor + + +5. Export +UF2 + test + + + +Integer Types + +uint8_t: 0-255 +int8_t: -128 to 127 +Two's complement for signed + + +Key Insight + +Float: patch 1 word +Double: patch 2 words +Check r2 to detect type + \ No newline at end of file diff --git a/WEEK06/WEEK06-SLIDES.pdf b/WEEK06/WEEK06-SLIDES.pdf new file mode 100644 index 0000000..54ad3ac Binary files /dev/null and b/WEEK06/WEEK06-SLIDES.pdf differ diff --git a/WEEK06/WEEK06.md b/WEEK06/WEEK06.md new file mode 100644 index 0000000..0cadd8c --- /dev/null +++ b/WEEK06/WEEK06.md @@ -0,0 +1,1269 @@ +# Week 6: Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Understand the difference between regular (automatic) variables and static variables +- Know where different types of variables are stored (stack vs static storage) +- Configure GPIO pins as inputs and use internal pull-up resistors +- Read button states using `gpio_get()` and control LEDs based on input +- Use GDB to examine how the compiler handles static vs automatic variables +- Identify compiler optimizations by stepping through assembly +- Hack variable values and invert GPIO input/output logic using a hex editor +- Convert patched binaries to UF2 format for flashing + +--- + +## Part 1: Understanding Static Variables + +### What is a Static Variable? + +A **static variable** is a special kind of variable that "remembers" its value between function calls or loop iterations. Unlike regular variables that get created and destroyed each time, static variables **persist** for the entire lifetime of your program. + +Think of it like this: +- **Regular variable:** Like writing on a whiteboard that gets erased after each class +- **Static variable:** Like writing in a notebook that you keep forever + +``` ++-----------------------------------------------------------------+ +| Regular vs Static Variables | +| | +| REGULAR (automatic): | +| +------------------------------------------------------------+ | +| | Loop 1: Create -> Set to 42 -> Increment to 43 -> Destroy | +| | Loop 2: Create -> Set to 42 -> Increment to 43 -> Destroy | +| | Loop 3: Create -> Set to 42 -> Increment to 43 -> Destroy | +| | Result: Always appears as 42! | +| +------------------------------------------------------------+ | +| | +| STATIC: | +| +------------------------------------------------------------+ | +| | Loop 1: Already exists -> Read 42 -> Increment -> Store 43 | +| | Loop 2: Already exists -> Read 43 -> Increment -> Store 44 | +| | Loop 3: Already exists -> Read 44 -> Increment -> Store 45 | +| | Result: Keeps incrementing! | +| +------------------------------------------------------------+ | +| | ++-----------------------------------------------------------------+ +``` + +### The `static` Keyword + +In C, you declare a static variable by adding the `static` keyword: + +```c +uint8_t regular_fav_num = 42; // Regular - recreated each time +static uint8_t static_fav_num = 42; // Static - persists forever +``` + +### Where Do Variables Live in Memory? + +Different types of variables are stored in different memory locations: + +| Variable Type | Storage Location | Lifetime | Example | +| ----------------- | ---------------- | ------------------------- | ------------------------------------ | +| Automatic (local) | Stack | Until function/block ends | `uint8_t x = 5;` | +| Static | Static Storage | Entire program lifetime | `static uint8_t x = 5;` | +| Global | Static Storage | Entire program lifetime | `uint8_t x = 5;` (outside functions) | +| Dynamic (heap) | Heap | Until `free()` is called | `malloc(sizeof(int))` | + +### Stack vs Static Storage vs Heap + +``` ++-----------------------------------------------------------------+ +| Memory Layout | +| | +| +-------------------+ High Address (0x20082000) | +| | STACK | ?? Automatic/local variables | +| | (grows down) | Created/destroyed per function | +| +-------------------+ | +| | | | +| | (free space) | | +| | | | +| +-------------------+ | +| | HEAP | ?? Dynamic allocation (malloc/free) | +| | (grows up) | | +| +-------------------+ | +| | .bss section | ?? Uninitialized static/global vars | +| +-------------------+ | +| | .data section | ?? Initialized static/global vars | +| +-------------------+ Low Address (0x20000000) | +| | ++-----------------------------------------------------------------+ +``` + +**Key Point:** Static variables are NOT on the heap! They live in a fixed location in the `.data` section (if initialized) or `.bss` section (if uninitialized). This is different from heap memory which is dynamically allocated at runtime. + +### What Happens with Overflow? + +Since `static_fav_num` is a `uint8_t` (unsigned 8-bit), it can only hold values 0-255. What happens when it reaches 255 and we add 1? + +``` +255 + 1 = 256... but that doesn't fit in 8 bits! +Binary: 11111111 + 1 = 100000000 (9 bits) +The 9th bit is lost, so we get: 00000000 = 0 +``` + +This is called **overflow** or **wrap-around**. The value "wraps" back to 0 and starts counting again! + +--- + +## Part 2: Understanding GPIO Inputs + +### Input vs Output + +So far, we've used GPIO pins as **outputs** to control LEDs. Now we'll learn to use them as **inputs** to read button states! + +``` ++-----------------------------------------------------------------+ +| GPIO Direction | +| | +| OUTPUT (what we've done before): | +| +---------+ | +| | Pico | -------? LED | +| | GPIO 16 | (We control the LED) | +| +---------+ | +| | +| INPUT (new this week): | +| +---------+ | +| | Pico | ?------- Button | +| | GPIO 15 | (We read the button state) | +| +---------+ | +| | ++-----------------------------------------------------------------+ +``` + +### The Floating Input Problem + +When a GPIO pin is set as an input but nothing is connected, it's called a **floating input**. The voltage on the pin is undefined and can randomly read as HIGH (1) or LOW (0) due to electrical noise. + +``` ++-----------------------------------------------------------------+ +| Floating Input = Random Values! | +| | +| GPIO Pin (no connection): | +| Reading 1: HIGH | +| Reading 2: LOW | +| Reading 3: HIGH | +| Reading 4: HIGH | +| Reading 5: LOW | +| (Completely unpredictable!) | +| | ++-----------------------------------------------------------------+ +``` + +### Pull-Up and Pull-Down Resistors + +To solve the floating input problem, we use **pull resistors**: + +| Resistor Type | Default State | When Button Pressed | +| ------------- | ------------- | ------------------- | +| **Pull-Up** | HIGH (1) | LOW (0) | +| **Pull-Down** | LOW (0) | HIGH (1) | + +The Pico 2 has **internal** pull resistors that you can enable with software - no external components needed! + +``` ++-----------------------------------------------------------------+ +| Pull-Up Resistor (what we're using) | +| | +| 3.3V | +| | | +| + (internal pull-up resistor) | +| | | +| +------? GPIO 15 (reads HIGH normally) | +| | | +| +-+-+ | +| |BTN| ?? Button connects GPIO to GND when pressed | +| +-+-+ | +| | | +| GND | +| | +| Button NOT pressed: GPIO reads 1 (HIGH) | +| Button PRESSED: GPIO reads 0 (LOW) | +| | ++-----------------------------------------------------------------+ +``` + +### GPIO Input Functions + +| Function | Purpose | +| ---------------------------- | --------------------------------------- | +| `gpio_init(pin)` | Initialize a GPIO pin for use | +| `gpio_set_dir(pin, GPIO_IN)` | Set pin as INPUT | +| `gpio_pull_up(pin)` | Enable internal pull-up resistor | +| `gpio_pull_down(pin)` | Enable internal pull-down resistor | +| `gpio_get(pin)` | Read the current state (returns 0 or 1) | + +### The Ternary Operator + +The code uses a **ternary operator** to control the LED based on button state: + +```c +gpio_put(LED_GPIO, pressed ? 0 : 1); +``` + +This is a compact if-else statement: +- If `pressed` is **true (1)**: output `0` (LED OFF... wait, that seems backwards!) +- If `pressed` is **false (0)**: output `1` (LED ON) + +**Why is it inverted?** Because of the pull-up resistor! +- Button **released** -> GPIO reads `1` -> `pressed = 1` -> output `0` -> LED OFF +- Button **pressed** -> GPIO reads `0` -> `pressed = 0` -> output `1` -> LED ON + +A clearer way to write this: +```c +gpio_put(LED_GPIO, !gpio_get(BUTTON_GPIO)); +``` + +--- + +## Part 3: Understanding Compiler Optimizations + +### Why Does Code Disappear? + +When you compile code, the compiler tries to make it faster and smaller. This is called **optimization**. Sometimes the compiler removes code that it thinks has no effect! + +**Example from our code:** +```c +while (true) { + uint8_t regular_fav_num = 42; // Created + regular_fav_num++; // Incremented to 43 + // But then it's destroyed and recreated as 42 next loop! +} +``` + +The compiler sees that incrementing `regular_fav_num` has no lasting effect (because it's recreated as 42 each loop), so it may **optimize away** the increment operation entirely! + +### Function Inlining + +Sometimes the compiler **inlines** functions, meaning it replaces a function call with the function's code directly. + +**Original code:** +```c +gpio_pull_up(BUTTON_GPIO); +``` + +**What the compiler might do:** +```c +// Instead of calling gpio_pull_up, it calls the underlying function: +gpio_set_pulls(BUTTON_GPIO, true, false); +``` + +This is why when you look for `gpio_pull_up` in the binary, you might find `gpio_set_pulls` instead! + +--- + +## Part 4: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. OpenOCD installed and configured +4. GDB (`arm-none-eabi-gdb`) installed +5. Python installed (for UF2 conversion) +6. A serial monitor (PuTTY, minicom, or screen) +7. A push button connected to GPIO 15 +8. An LED connected to GPIO 16 (or use the breadboard LED) +9. A hex editor (HxD, ImHex, or similar) +10. The sample project: `0x0014_static-variables` + +### Hardware Setup + +Connect your button like this: +- One side of button -> GPIO 15 +- Other side of button -> GND + +The internal pull-up resistor provides the 3.3V connection, so you only need to connect to GND! + +``` ++-----------------------------------------------------------------+ +| Breadboard Wiring | +| | +| Pico 2 | +| +----------+ | +| | | | +| | GPIO 15 |--------+ | +| | | | | +| | GPIO 16 |--------+---? LED (with resistor to GND) | +| | | | | +| | GND |--------+---+ | +| | | | | | +| +----------+ +-+-+ | | +| |BTN|-+ | +| +---+ | +| | ++-----------------------------------------------------------------+ +``` + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x0014_static-variables/ +| +-- build/ +| | +-- 0x0014_static-variables.uf2 +| | +-- 0x0014_static-variables.elf +| +-- 0x0014_static-variables.c ++-- uf2conv.py +``` + +--- + +## Part 5: Hands-On Tutorial - Static Variables and GPIO Input + +### Step 1: Review the Source Code + +Let's examine the static variables code: + +**File: `0x0014_static-variables.c`** + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + + const uint BUTTON_GPIO = 15; + const uint LED_GPIO = 16; + bool pressed = 0; + + gpio_init(BUTTON_GPIO); + gpio_set_dir(BUTTON_GPIO, GPIO_IN); + gpio_pull_up(BUTTON_GPIO); + + gpio_init(LED_GPIO); + gpio_set_dir(LED_GPIO, GPIO_OUT); + + while (true) { + uint8_t regular_fav_num = 42; + static uint8_t static_fav_num = 42; + + printf("regular_fav_num: %d\r\n", regular_fav_num); + printf("static_fav_num: %d\r\n", static_fav_num); + + regular_fav_num++; + static_fav_num++; + + pressed = gpio_get(BUTTON_GPIO); + gpio_put(LED_GPIO, pressed ? 0 : 1); + } +} +``` + +**What this code does:** + +1. **Line 6-8:** Defines constants for button (GPIO 15) and LED (GPIO 16) pins +2. **Line 10-12:** Sets up GPIO 15 as input with internal pull-up resistor +3. **Line 14-15:** Sets up GPIO 16 as output for the LED +4. **Line 18-19:** Creates two variables: + - `regular_fav_num` - a normal local variable (recreated each loop) + - `static_fav_num` - a static variable (persists across loops) +5. **Line 21-22:** Prints both values to the serial terminal +6. **Line 24-25:** Increments both values +7. **Line 27-28:** Reads button and controls LED accordingly + +### Step 2: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0014_static-variables.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 3: Open Your Serial Monitor + +Open PuTTY, minicom, or screen and connect to your Pico's serial port. + +**You should see output like this:** + +``` +... +regular_fav_num: 42 +static_fav_num: 42 +regular_fav_num: 42 +static_fav_num: 43 +regular_fav_num: 42 +static_fav_num: 44 +regular_fav_num: 42 +static_fav_num: 45 +... +``` + +**Notice the difference:** +- `regular_fav_num` stays at 42 every time (it's recreated each loop) +- `static_fav_num` increases each time (it persists and remembers its value) + +### Step 4: Test the Button + +Now test the button behavior: +- **Button NOT pressed:** LED should be OFF +- **Button PRESSED:** LED should turn ON + +That may feel backwards at first glance, but it matches the pull-up + ternary logic in the code. We'll hack this later to make the behavior more intuitive. + +### Step 5: Watch for Overflow + +Keep the program running and watch `static_fav_num`. After 255, you'll see: + +``` +static_fav_num: 254 +static_fav_num: 255 +static_fav_num: 0 ?? Wrapped around! +static_fav_num: 1 +static_fav_num: 2 +... +``` + +This demonstrates unsigned integer overflow! + +--- + +## Part 6: Debugging with GDB (Dynamic Analysis) + +> ? **REVIEW:** This setup is identical to previous weeks. If you need a refresher on OpenOCD and GDB connection, refer back to Week 3 Part 6. + +### Starting the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +**Terminal 2 - Start GDB:** + +```cmd +arm-none-eabi-gdb build\0x0014_static-variables.elf +``` + +**Connect to target:** + +```gdb +(gdb) target extended-remote :3333 +(gdb) monitor reset halt +``` + +### Step 6: Examine Main Function + +Let's examine the main function at its entry point. First, disassemble from the start: + +``` +x/38i 0x10000234 +``` + +You should see output like: + +``` +(gdb) x/38i 0x10000234 + 0x10000234
: push {r4, lr} + 0x10000236 : bl 0x10003014 + 0x1000023a : movs r0, #15 + 0x1000023c : bl 0x10000300 + 0x10000240 : movs r0, #15 + 0x10000242 : mov.w r3, #0 + 0x10000246 : mcrr 0, 4, r0, r3, cr4 + 0x1000024a : movs r2, #0 + 0x1000024c : movs r1, #1 + 0x1000024e : bl 0x100002d8 + 0x10000252 : movs r0, #16 + 0x10000254 : bl 0x10000300 + 0x10000258 : movs r3, #16 + 0x1000025a : mov.w r2, #1 + 0x1000025e : mcrr 0, 4, r3, r2, cr4 + 0x10000262 : ldr r4, [pc, #44] @ (0x10000290 ) + 0x10000264 : movs r1, #42 @ 0x2a + 0x10000266 : ldr r0, [pc, #44] @ (0x10000294 ) + 0x10000268 : bl 0x100031a4 <__wrap_printf> + 0x1000026c : ldrb r1, [r4, #0] + 0x1000026e : ldr r0, [pc, #40] @ (0x10000298 ) + 0x10000270 : bl 0x100031a4 <__wrap_printf> + 0x10000274 : mov.w r1, #3489660928 @ 0xd0000000 + 0x10000278 : ldrb r3, [r4, #0] + 0x1000027a : movs r2, #16 + 0x1000027c : adds r3, #1 + 0x1000027e : strb r3, [r4, #0] + 0x10000280 : ldr r3, [r1, #4] + 0x10000282 : ubfx r3, r3, #15, #1 + 0x10000286 : eor.w r3, r3, #1 + 0x1000028a : mcrr 0, 4, r2, r3, cr0 + 0x1000028e : b.n 0x10000264 + 0x10000290 : lsls r0, r5, #22 + 0x10000292 : movs r0, #0 + 0x10000294 : adds r5, #96 @ 0x60 + 0x10000296 : asrs r0, r0, #32 + 0x10000298 : adds r5, #120 @ 0x78 + 0x1000029a : asrs r0, r0, #32 +``` + +### Step 7: Set a Breakpoint at Main + +``` +b *0x10000234 +c +``` + +### Step 8: Examine the Static Variable Location + +Static variables live at fixed RAM addresses. But how do we find that address? Look at `0x10000262` in the disassembly from Step 6: + +``` +(gdb) x/16i 0x10000234 +=> 0x10000234
: push {r4, lr} + 0x10000236 : bl 0x10003014 + 0x1000023a : movs r0, #15 + 0x1000023c : bl 0x10000300 + 0x10000240 : movs r0, #15 + 0x10000242 : mov.w r3, #0 + 0x10000246 : mcrr 0, 4, r0, r3, cr4 + 0x1000024a : movs r2, #0 + 0x1000024c : movs r1, #1 + 0x1000024e : bl 0x100002d8 + 0x10000252 : movs r0, #16 + 0x10000254 : bl 0x10000300 + 0x10000258 : movs r3, #16 + 0x1000025a : mov.w r2, #1 + 0x1000025e : mcrr 0, 4, r3, r2, cr4 + 0x10000262 : ldr r4, [pc, #44] @ (0x10000290 ) +``` + +This loads `r4` from the **literal pool** at address `0x10000290`. The literal pool stores constants that are too large for immediate encoding - in this case, a 32-bit RAM address. Let's examine what's stored there: + +```gdb +(gdb) x/1wx 0x10000290 +0x10000290 : 0x200005a8 +``` + +That's `0x200005a8` - the RAM address of `static_fav_num`! The compiler placed this address in the literal pool because it can't encode a full 32-bit address in a single Thumb instruction. + +> **Common confusion three things that trip people up here:** +> +> **1. GDB shows `` instead of ``.** +> `static_fav_num` is a function-local static, so it does not appear in GDB's global symbol table. GDB labels the address with the nearest *named* global symbol it can find in this case an SDK-internal spin-lock variable that happens to be nearby. The address `0x200005a8` is still correct; the label is just GDB's best guess at a name. +```gdb +(gdb) x/x 0x200005a8 +0x200005a8 : 0x0000122a +``` +> +> **2. `x/x 0x200005a8` shows `0x0000122a`, not `0x0000002a` (42).** +> `x/x` reads a **4-byte word** (32 bits). `static_fav_num` is a `uint8_t` only **1 byte**. The low byte of `0x0000122a` is `0x2a` = 42. That *is* the variable. The upper bytes belong to whatever happens to be in adjacent RAM. To read exactly one byte and see `42`, use: +```gdb +(gdb) x/1ub 0x200005a8 +0x200005a8 : 42 +``` +> +> **3. `x/x *0x200005a8` shows junk at address `0x122a`.** +> The `*` is a pointer dereference. GDB first reads the 4-byte word at `0x200005a8` (= `0x0000122a`), then treats that *value* as a new address and reads from `0x0000122a`. You are asking GDB to follow a pointer that was never meant to be a pointer the result is meaningless. Drop the `*`: use `x/1ub 0x200005a8` to read the variable's value directly. + +> Tip: **Why did the disassembly at `0x10000290` show `lsls r0, r5, #22` instead?** Because `x/i` (disassemble) interprets raw data as instructions. The bytes `A8 05 00 20` at that address are the little-endian encoding of `0x200005A8`, but GDB's disassembler doesn't know it's data - it tries to decode it as a Thumb instruction. Using `x/wx` (examine as word) shows the actual value. + +### Step 9: Step Through the Loop + +The loop body starts at `0x10000264`. Set a breakpoint there and continue: + +```gdb +(gdb) b *0x10000264 +(gdb) c +(gdb) x/i $pc +=> 0x10000264 : movs r1, #42 @ 0x2a +``` + +GDB will stop at `movs r1, #42` that is the compiler's constant for `regular_fav_num`. Let's set a breakpoint and continue at `0x10000278`. + +```gdb +(gdb) b *0x10000278 +(gdb) c +(gdb) x/i $pc +=> 0x10000278 : ldrb r3, [r4, #0] +``` + +This is where the static variable is actually manipulated: + +```gdb +(gdb) x/4i $pc +=> 0x10000278 : ldrb r3, [r4, #0] <- load static_fav_num from RAM into r3 + 0x1000027a : movs r2, #16 <- load LED pin number (16) into r2 for later + 0x1000027c : adds r3, #1 <- increment r3 by 1 + 0x1000027e : strb r3, [r4, #0] <- store incremented value back to RAM +``` + +Confirm what `r4` actually contains right now: + +```gdb +(gdb) x/x $r4 +0x200005a8 : 0x2a +``` + +`r4` is `0x200005a8`, a RAM address. The value at that address is `0x2a` (42 in decimal), which is the current value of `static_fav_num`. After `adds r3, #1` and `strb r3, [r4, #0]` execute, examining `r4` again will show `0x0000002b` (43). + +**What is the literal pool?** + +A Thumb `ldr` instruction is only 16 or 32 bits wide. There is no room inside those bits to encode a full 32-bit constant like `0x200005a8`. To solve this, the compiler collects all such large constants and writes them into a small block of **raw data** embedded directly in the flash image this block is called a **literal pool** (also called a constant pool). + +**There is not one literal pool per flash, there is (at least) one per function.** Every function that references large constants gets its own pool appended to its code. A large function can have several pools scattered through it wherever the compiler decides to emit them. Each pool is purely data; the CPU must never try to execute it. + +**Where exactly is a literal pool placed?** Always **after an unconditional branch**, never before or in the middle of a straight-line code path. This is by design: the CPU falls through code sequentially, so the pool must be unreachable by fall-through. In our case the pool sits at `0x10000290`, directly after the `b.n 0x10000264` branch at `0x1000028e` that closes the `while (true)` loop: + +``` +0x1000028e: b.n 0x10000264 <- unconditional branch back to loop top +0x10000290: [literal pool] <- CPU never reaches here by fall-through +``` + +**Why does the pool have to be nearby?** The `ldr rN, [pc, #offset]` instruction encodes the offset in a limited number of bits. In standard Thumb the maximum reach is **1020 bytes** from the instruction. If the function is long enough that the end is out of range, the assembler emits an intermediate pool mid-function after the next unconditional branch it can find. This is in the ARM v8-M Architecture Reference Manual p. 672. + +**How the load works in our function:** At `0x10000262` the compiler emits: + +``` +ldr r4, [pc, #44] @ (0x10000290) +``` + +On ARM, `PC` reads as the address of the instruction **plus 4** during execution. So the effective address is `0x10000262 + 4 + 44 = 0x10000290`. That word in the pool contains `0x200005a8`, the RAM address of `static_fav_num`. The pool is also where the `printf` format-string pointers live (the other `ldr` instructions you saw in the disassembly). + +``` +Flash (read-only) ++-------------------------------------------+ RAM +| 0x10000262: ldr r4, [pc, #44] | +------------------+ +| ...loop body... | | 0x200005a8: 42 | <- static_fav_num +| 0x1000028e: b.n 0x10000264 (end of loop) | +------------------+ ++------- literal pool (data, not code) -----+ ^ +| 0x10000290: 0x200005a8 --------------------+----------------+ +| 0x10000294: ptr to "regular_fav_num: %d" | r4 holds this address +| 0x10000298: ptr to "static_fav_num: %d" | ++------- next function ---------------------+ +| 0x1000029c: push {r4} | ++-------------------------------------------+ +``` + +You can see this directly. Run `x/10i 0x1000028e` in GDB and you will get exactly this: + +```gdb +(gdb) x/10i 0x1000028e + (gdb) x/10i 0x1000028e + 0x1000028e : b.n 0x10000264 <- unconditional branch, loop top + 0x10000290 : lsls r0, r5, #22 <-+ + 0x10000292 : movs r0, #0 <-+-- word 1: 0x200005a8 (fixed RAM addr static_fav_num) + 0x10000294 : adds r5, #96 @ 0x60 <-+ + 0x10000296 : asrs r0, r0, #32 <-+-- word 2: 0x10004b60 (flash addr regular_fav_num text) + 0x10000298 : adds r5, #120 <-+ + 0x1000029a : asrs r0, r0, #32 <-+-- word 3: 0x10004b78 (flash addr static_fav_num text) +``` + +`0x10000290` - `0x1000029b` is the literal pool 12 bytes, 3 32-bit words. GDB's `x/i` (disassemble) has no idea those bytes are data, so it blindly decodes them as Thumb instructions and produces nonsense (`lsls r0, r5, #22`, etc.). The giveaway that you have entered pool territory is always the same: **garbled, implausible instructions immediately after an unconditional branch**. Use `x/wx` to read those addresses as data and you get the real values: + +```gdb +(gdb) x/3wx 0x10000290 +0x10000290 : 0x200005a8 0x10003560 0x10003578 +``` + +```gdb +(gdb) x/1ub 0x200005a8 +0x200005a8 : 42 +``` +- `0x200005a8` RAM address of `static_fav_num`, lives in the **`.data` section** (initialized static RAM, `0x20000000``0x200xxxxx`). It is `.data` and not `.bss` because the variable has an explicit initializer (`= 42`). `.bss` is for variables that are zero-initialized or have no initializer at all. + +```gdb +(gdb) x/s 0x10003560 +0x10003560: "regular_fav_num: %d\r\n" +``` +- `0x10003560` flash address of the `"regular_fav_num: %d\r\n"` string literal, lives in the **`.rodata` section** (read-only data) inside flash (`0x10000000`+). String literals are constants baked into the binary at compile time they never change and never move, so they stay in flash. + +```gdb +(gdb) x/s 0x10003578 +0x10003578: "static_fav_num: %d\r\n" +- `0x10003578` flash address of the `"static_fav_num: %d\r\n"` string literal, also in **`.rodata`** in flash for the same reason. + +So the pool contains addresses into three different regions: RAM `.data` (the static variable), and flash `.rodata` (both format strings). Only the RAM address needed to be in the pool, the flash addresses could in principle be reached other ways, but they are also too large to encode as 16-bit immediates, so they go in the pool too. + +**Why `.data` and not `.bss`?** The distinction matters: + +| Section | What goes here | Example declarations | Initial value stored in flash? | +|---------|---------------|----------------------|-------------------------------| +| `.data` | Any static-duration variable with a **non-zero initializer** | `static uint8_t x = 42;` (inside a function) | Yes the initial value lives in flash; startup copies it to RAM | +| `.data` | Same rule applies to **global** variables with non-zero initializers | `uint8_t g = 10;` (outside all functions) | Yes same startup copy | +| `.bss` | Any static-duration variable with **no initializer or zero initializer** | `static uint8_t x;` or `static uint8_t x = 0;` | No startup just zeroes the RAM range; no flash copy needed | +| `.bss` | Same rule for **global** variables that are zero/uninitialized | `uint8_t g;` or `uint8_t g = 0;` (outside functions) | No same zero-fill at startup | + +The rule is not about `static` specifically it is about **lifetime and initial value**. Any variable whose lifetime spans the whole program (static locals, globals) goes into `.data` or `.bss`. The `static` keyword inside a function just forces that lifetime. A global variable without `static` has the same lifetime and follows the same rule. + +`static_fav_num = 42` has an explicit non-zero initializer, so the linker puts it in `.data`. The startup code copies the initial value `42` from flash into RAM address `0x200005a8` before `main()` runs. If it had been `static uint8_t static_fav_num;` (no initializer), the linker would put it in `.bss` instead and the startup code would zero that RAM region no flash copy needed. + +**Why is there no pool entry for the address of `regular_fav_num`?** + +Because `regular_fav_num` is an **automatic (stack) variable** it has no fixed address. Automatic variables are allocated on the call stack at runtime, relative to the current stack pointer (`sp`). Their address changes every time the function runs and is different on every call. There is no constant 32-bit address to put in a pool. + +In this specific case the compiler went even further: it **never gave `regular_fav_num` a stack slot at all**. The compiler saw that the value is always `42` when printed, so it baked the constant `42` directly into the `movs r1, #42` instruction. The variable lives only in a register at the moment it is needed — no stack space reserved, no pool entry, no load-store cycle. + +**You can prove this with three GDB checks:** + +**Proof 1 `info locals` shows the value came from a register, not a stack slot:** + +```gdb +(gdb) b *0x10000264 +(gdb) c +(gdb) info locals +regular_fav_num = 42 '*' +static_fav_num = 42 '*' +BUTTON_GPIO = 15 +LED_GPIO = 16 +pressed = +``` + +The `'*'` suffix after `42` means GDB retrieved the value from a **register** (specifically `r1`, which holds `42` at this point), not from a stack slot. The variable has no fixed memory address — GDB can report its current value only because the right register happens to hold it right now. Notice that `pressed` is the one showing `` — the compiler removed it from tracking entirely because its value is never needed after the ternary expression. + +**Proof 2 The function prologue allocates no stack space for locals:** + +```gdb +(gdb) x/2i 0x10000234 + 0x10000234
: push {r4, lr} + 0x10000236 : bl 0x10003014 +``` + +The very first instruction is `push {r4, lr}`, that saves `r4` (a callee-saved register) and `lr` (return address). There is **no** `sub sp, #N` instruction after it. On ARM, allocating stack space for local variables requires subtracting from `sp` to reserve room. If `regular_fav_num` lived on the stack, there would be a `sub sp, #4` (or similar) here. There isn't, so no stack slot was ever created. + +**Proof 3 The loop body contains no store to the stack for `regular_fav_num`:** + +```gdb +(gdb) x/6i 0x10000264 +=> 0x10000264 : movs r1, #42 @ 0x2a + // constant 42 baked directly into instruction + 0x10000266 : ldr r0, [pc, #44] @ (0x10000294 ) + // load format string address from pool + 0x10000268 : bl 0x100031a4 <__wrap_printf> + // call printf + 0x1000026c : ldrb r1, [r4, #0] + // load static_fav_num from RAM + 0x1000026e : ldr r0, [pc, #40] @ (0x10000298 ) + // load format string address from pool + 0x10000270 : bl 0x100031a4 <__wrap_printf> + call printf +``` + +For `regular_fav_num`, the value `42` goes straight into `r1` via `movs`, it is never written to memory and never read back from memory. There is no `strb r1, [sp, #N]` storing it to the stack, and no `ldrb r1, [sp, #N]` loading it back. The variable exists only in the source code. In the binary it is just a number in an instruction encoding. + +`static_fav_num` is the exact opposite. It lives in the `.data` section of RAM, a fixed, known address (`0x200005a8`) that is set at link time and never changes for the life of the program. That fixed address is exactly the kind of value that cannot fit in a 16-bit Thumb instruction, so the linker writes it into the literal pool and the CPU fetches it with `ldr r4, [pc, #44]`. + +> **IMPORTANT:** Static variables are **not** on the heap. The heap is for dynamic memory (`malloc`/`free`), memory whose lifetime is controlled explicitly at runtime. Static and global variables live in the `.data` section (if initialized) or `.bss` section (if zero/uninitialized), a region of RAM with a fixed layout determined at link time, not at runtime. + +``` +Memory regions for our two variables: ++------------------------------------------+ +| FLASH (read-only, 0x10000000+) | +| - Code (instructions) | +| - Literal pool (our 3 words) | +| - String literals ("regular_fav_num...")| ++------------------------------------------+ +| RAM .data section (0x20000000+) | +| - static_fav_num @ 0x200005a8 <- pool | ++------------------------------------------+ +| STACK (grows down from 0x20082000) | +| - regular_fav_num (if not optimized) | +| frame-relative, no fixed address | ++------------------------------------------+ +| HEAP (grows up, between stack and .bss) | +| - malloc/free only, nothing here today | ++------------------------------------------+ +``` + +The pool ends at `0x1000029b`. At `0x1000029d` a new function begins, specifically `gpio_set_function`. The first two instructions, `push {r4}` (save caller's `r4` to the stack) and `mov.w r4, #256` (load a working constant), are a standard SDK function prologue, nothing to do with `main`. The `ldr r3, [pc, #48]`i r/. at `0x100002a3` (`gpio_set_function+6`) is that next function loading from **its own** literal pool further ahead in flash — proof that every function manages its own pool. + +### Step 10: Examine Register Values + +After breaking at `0x10000264`, check the registers: + +```gdb +(gdb) i r +``` + +What you will actually see: + +- `r4` = `0x200005a8` — the static variable's fixed RAM address. This was loaded from the literal pool in the function prologue and never changes across loop iterations. +- `r1` = leftover value from the previous `printf` call (e.g. `0x40039044`). The breakpoint is stopped **at** `movs r1, #42` — that instruction has not executed yet, so `r1` does not yet hold `42`. +- `r3` = leftover from prior operations (e.g. `0x10`). It will be used later in the loop body to load, increment, and store `static_fav_num`, but not yet. +- `pc` = `0x10000264` — the loop top, pointing at the `movs r1, #42` instruction. + +You can confirm `r4` holds the right address and value: + +```gdb +(gdb) x/1db $r4 +0x200005a8 : 42 +``` + +The `` label is just the nearest named global symbol in the SDK that GDB finds at that address — the actual variable stored there is our `static_fav_num`. + +### Step 11: Watch the Static Variable Change + +Now that we know the static variable lives at `0x200005a8`, examine it directly: + +```gdb +(gdb) x/1db 0x200005a8 +0x200005a8 : 42 +``` + +Step through a full loop iteration (back to `0x10000264`) and re-examine: + +```gdb +(gdb) c +(gdb) x/1db 0x200005a8 +0x200005a8 : 43 +``` + +The value incremented from 42 to 43! Each loop iteration, the `adds r3, #1` at `0x1000027c` bumps it by 1, and `strb r3, [r4, #0]` at `0x1000027e` writes it back to RAM. + +### Step 12: Examine GPIO State + +Read the GPIO input register to see the button state: + +```gdb +(gdb) x/1wx 0xd0000004 +0xd0000004: 0x00008003 +``` + +The SIO GPIO input register at `0xd0000004` shows the current state of all GPIO pins. Bit 15 corresponds to our button on GPIO 15. To extract just bit 15: + +```gdb +(gdb) p/x (*(unsigned int *)0xd0000004 >> 15) & 1 +$1 = 0x1 +``` + +- Returns `1` when button is **not pressed** (pull-up holds it HIGH) +- Returns `0` when button is **pressed** (connected to GND) + +TRY IT! + +--- + +## Part 7: Understanding the Assembly + +Now that we've explored the binary in GDB, let's make sense of the key patterns. + +### Step 13: Analyze the Regular Variable + +In GDB, examine the code at the start of the loop: + +```gdb +(gdb) x/5i 0x10000262 + 0x10000262 : ldr r4, [pc, #44] @ (0x10000290 ) +=> 0x10000264 : movs r1, #42 @ 0x2a + 0x10000266 : ldr r0, [pc, #44] @ (0x10000294 ) + 0x10000268 : bl 0x100031a4 <__wrap_printf> + 0x1000026c : ldrb r1, [r4, #0] +``` + +Look for this instruction: + +``` +0x10000264 : movs r1, #42 @ 0x2a +``` + +This loads the value `0x2a` (42 in decimal) directly into register `r1` for the first `printf` call. + +**Key insight:** The compiler **never allocated stack space** for `regular_fav_num`. Since it is always `42` when printed, the compiler bakes the constant directly into the `movs r1, #42` instruction. The `regular_fav_num++` after the print is also removed because it has no observable effect — the variable is recreated as `42` on the next loop iteration anyway. + +### Step 14: Analyze the Static Variable + +Examine the static variable operations in the second half of the loop body: + +```gdb +(gdb) x/10i 0x10000274 + 0x10000274 : mov.w r1, #3489660928 @ 0xd0000000 + 0x10000278 : ldrb r3, [r4, #0] + 0x1000027a : movs r2, #16 + 0x1000027c : adds r3, #1 + 0x1000027e : strb r3, [r4, #0] + 0x10000280 : ldr r3, [r1, #4] + 0x10000282 : ubfx r3, r3, #15, #1 + 0x10000286 : eor.w r3, r3, #1 + 0x1000028a : mcrr 0, 4, r2, r3, cr0 + 0x1000028e : b.n 0x10000264 +``` + +Look for the load-increment-store pattern using `r4` (which holds the static variable's RAM address): + +``` + ... + 0x10000278 : ldrb r3, [r4, #0] + 0x1000027a : movs r2, #16 + 0x1000027c : adds r3, #1 + 0x1000027e : strb r3, [r4, #0] + ... +``` + +Note that `r4` was loaded earlier at `0x10000262` via `ldr r4, [pc, #44]` - this pulled the static variable's RAM address (`0x200005a8`) from the literal pool at `0x10000290`. + +**Key insight:** The static variable lives at a **fixed RAM address** (`0x200005a8`). It's loaded, incremented, and stored back. The regular variable, by contrast, never gets a stack slot — the compiler holds it in a register and bakes the constant `42` directly into the instruction, so there is nothing to load or store. + +Verify the static variable value which should be `43`: + +```gdb +(gdb) x/1db 0x200005a8 +0x200005a8 : 43 +``` + +### Step 15: Analyze the GPIO Logic + +Examine the GPIO input/output code: + +```gdb +(gdb) x/10i 0x10000274 + 0x10000274 : mov.w r1, #3489660928 @ 0xd0000000 + 0x10000278 : ldrb r3, [r4, #0] + 0x1000027a : movs r2, #16 + 0x1000027c : adds r3, #1 + 0x1000027e : strb r3, [r4, #0] + 0x10000280 : ldr r3, [r1, #4] + 0x10000282 : ubfx r3, r3, #15, #1 + 0x10000286 : eor.w r3, r3, #1 + 0x1000028a : mcrr 0, 4, r2, r3, cr0 + 0x1000028e : b.n 0x10000264 +``` + +**Breaking this down:** + +| Address | Instruction | Purpose | +| -------------- | -------------------------- | ---------------------------------------------------- | +| `0x10000274` | `mov.w r1, #0xd0000000` | Load SIO (Single-cycle I/O) base address into `r1` | +| `0x10000278` | `ldrb r3, [r4, #0]` | Load `static_fav_num` from RAM into `r3` | +| `0x1000027a` | `movs r2, #16` | Load LED pin number (16) into `r2` for later | +| `0x1000027c` | `adds r3, #1` | Increment `static_fav_num` by 1 | +| `0x1000027e` | `strb r3, [r4, #0]` | Store incremented value back to RAM | +| `0x10000280` | `ldr r3, [r1, #4]` | Read GPIO input state (SIO_GPIO_IN at offset `0x04`) | +| `0x10000282` | `ubfx r3, r3, #15, #1` | Extract bit 15 (GPIO 15 = button) | +| `0x10000286` | `eor.w r3, r3, #1` | XOR with 1 to invert (implements `? 0 : 1`) | +| `0x1000028a` | `mcrr 0, 4, r2, r3, cr0` | Write `r3` (button) and `r2` (pin 16) to GPIO output | +| `0x1000028e` | `b.n 0x10000264` | Loop back to start (`while (true)`) | + +> Tip: **Notice how the compiler interleaves the static variable increment with the GPIO logic.** It loads the SIO base address (`r1`) *before* doing the increment, and sets up `r2 = 16` (LED pin) in between. This is called **instruction scheduling** - the compiler reorders instructions to avoid pipeline stalls while waiting for memory reads. + +### Step 16: Find the Infinite Loop + +The last instruction at `0x1000028e` is already covered in the table above: + +``` + 0x1000028e : b.n 0x10000264 +``` + +This is an **unconditional branch** back to `0x10000264` (the `movs r1, #42` at the top of the loop) - this is the `while (true)` in our code! There is no `pop` or `bx lr` to return from `main` because the loop never exits. + +--- + +## Part 8: Hacking the Binary with a Hex Editor + +Now for the fun part - we'll patch the `.bin` file directly using a hex editor! + +> Tip: **Why a hex editor?** GDB **cannot write to flash memory** - the `0x10000000+` address range where program instructions live. Trying `set *(char *)0x10000264 = 0x2b` in GDB gives `Writing to flash memory forbidden in this context`. To make **permanent** patches that survive a power cycle, we edit the `.bin` file directly with a hex editor and re-flash it. + +### Step 17: Open the Binary in a Hex Editor + +1. Open **HxD** (or your preferred hex editor: ImHex, 010 Editor, etc.) +2. Click **File** -> **Open** +3. Navigate to `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0014_static-variables\build\` +4. Open `0x0014_static-variables.bin` + +### Step 18: Calculate the File Offset + +The binary is loaded at base address `0x10000000`. To find the file offset of any address: + +``` +file_offset = address - 0x10000000 +``` + +For example: +- Address `0x10000264` -> file offset `0x264` (612 in decimal) +- Address `0x10000286` -> file offset `0x286` (646 in decimal) + +### Step 19: Hack #1 - Change regular_fav_num from 42 to 43 + +From our GDB analysis, we know the instruction at `0x10000264` is: + +``` +movs r1, #0x2a -> bytes: 2a 21 +``` + +To change the value from 42 (`0x2a`) to 43 (`0x2b`): + +1. In HxD, open `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0014_static-variables\build\0x0014_static-variables.bin` +2. Press **Ctrl+G** (Go to offset) +3. Enter offset: `264` +4. You should see the byte `2A` at this position +5. Change `2A` to `2B` +6. The instruction is now `movs r1, #0x2b` (43 in decimal) + +> ?? **How Thumb encoding works:** In `movs r1, #imm8`, the immediate value is the first byte, and the opcode `21` is the second byte. So the bytes `2a 21` encode `movs r1, #0x2a`. + +### Step 20: Hack #2 - Invert the Button Logic + +#### Understand the Encoding + +From GDB, we found the `eor.w r3, r3, #1` instruction at `0x10000286` that inverts the button value. Examine the exact bytes: + +```gdb +(gdb) x/4bx 0x10000286 +0x10000286 : 0x83 0xf0 0x01 0x03 +``` + +This is the 32-bit Thumb-2 encoding of `eor.w r3, r3, #1`. The bytes break down as: + +``` ++-----------------------------------------------------------------+ +| eor.w r3, r3, #1 -> bytes: 83 F0 01 03 | +| | +| Byte 0: 0x83 -?? | +| Byte 1: 0xF0 -+ First halfword (opcode + source register) | +| Byte 2: 0x01 ---- Immediate value (#1) ?? CHANGE THIS | +| Byte 3: 0x03 ---- Destination register (r3) | +| | ++-----------------------------------------------------------------+ +``` + +To change `eor.w r3, r3, #1` to `eor.w r3, r3, #0` (making XOR do nothing): + +The file offset is `0x10000286 - 0x10000000 = 0x286`. The immediate byte is the 3rd byte of the instruction, so: `0x286 + 2 = 0x288`. + +To change `eor.w r3, r3, #1` to `eor.w r3, r3, #0`: + +1. In HxD, press **Ctrl+G** (Go to offset) +2. Enter offset: `288` (the third byte of the 4-byte instruction) +3. You should see the byte `01` at this position +4. Change `01` to `00` + +> ?? **Why offset `0x288` and not `0x286`?** The immediate value `#1` is in the **third byte** of the 4-byte instruction. The instruction starts at file offset `0x286`, so the immediate byte is at `0x286 + 2 = 0x288`. + +Now the logic is permanently changed: +- Button released (input = 1): `1 XOR 0 = 1` -> LED **ON** +- Button pressed (input = 0): `0 XOR 0 = 0` -> LED **OFF** + +This is the **opposite** of the original behavior! + +### Step 21: Save the Patched Binary + +1. Click **File** -> **Save As** +2. Save as `0x0014_static-variables-h.bin` in the build directory +3. Close the hex editor + +--- + +## Part 9: Converting and Flashing the Hacked Binary + +### Step 22: Convert to UF2 Format + +Open a terminal and navigate to your project directory: + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0014_static-variables +``` + +Run the conversion command: + +```cmd +python ..\uf2conv.py build\0x0014_static-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +**What this command means:** +- `uf2conv.py` = the conversion script (in the parent `Embedded-Hacking` directory) +- `--base 0x10000000` = the XIP base address where code runs from +- `--family 0xe48bff59` = the RP2350 family ID +- `--output build\hacked.uf2` = the output filename + +### Step 23: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Open your serial monitor + +### Step 24: Verify the Hacks + +**Check the serial output:** +``` +regular_fav_num: 43 ?? Changed from 42! +static_fav_num: 42 +regular_fav_num: 43 +static_fav_num: 43 +... +``` + +**Check the LED behavior:** +- LED should now be **ON by default** (when button is NOT pressed) +- LED should turn **OFF** when you press the button + + **BOOM! We successfully:** +1. Changed the printed value from 42 to 43 +2. Inverted the LED/button logic + +--- + +## Part 10: Summary and Review + +### What We Accomplished + +1. **Learned about static variables** - How they persist across function calls and loop iterations +2. **Understood memory layout** - Stack vs static storage vs heap +3. **Configured GPIO inputs** - Using pull-up resistors and reading button states +4. **Analyzed compiled code in GDB** - Saw how the compiler optimizes code +5. **Discovered function inlining** - `gpio_pull_up` became `gpio_set_pulls` +6. **Hacked variable values** - Changed 42 to 43 using a hex editor +7. **Inverted GPIO logic** - Made LED behavior opposite + +### Static vs Automatic Variables + +| Aspect | Automatic (Regular) | Static | +| ------------------ | ------------------------ | --------------------------- | +| **Storage** | Stack | Static storage (.data/.bss) | +| **Lifetime** | Block/function scope | Entire program | +| **Initialization** | Every time block entered | Once at program start | +| **Persistence** | Lost when scope exits | Retained between calls | +| **Compiler view** | May be optimized away | Always has memory location | + +### GPIO Input Configuration + +``` ++-----------------------------------------------------------------+ +| GPIO Input Setup Steps | +| | +| 1. gpio_init(pin) - Initialize the pin | +| 2. gpio_set_dir(pin, GPIO_IN) - Set as input | +| 3. gpio_pull_up(pin) - Enable pull-up | +| OR gpio_pull_down(pin) - OR enable pull-down | +| 4. gpio_get(pin) - Read the state | +| | ++-----------------------------------------------------------------+ +``` + +### The Binary Hacking Workflow + +``` ++-----------------------------------------------------------------+ +| 1. Analyze the binary with GDB | +| - Disassemble functions with x/Ni | +| - Identify key instructions and addresses | ++-----------------------------------------------------------------+ +| 2. Understand compiler optimizations | +| - Some functions get inlined (gpio_pull_up -> gpio_set_pulls)| +| - Some variables are optimized away | ++-----------------------------------------------------------------+ +| 3. Calculate file offsets | +| - file_offset = address - 0x10000000 | ++-----------------------------------------------------------------+ +| 4. Patch the .bin file with a hex editor | +| - Open the .bin file in HxD / ImHex | +| - Go to the calculated offset | +| - Change the target byte(s) | ++-----------------------------------------------------------------+ +| 5. Convert to UF2 | +| python uf2conv.py file.bin --base 0x10000000 | +| --family 0xe48bff59 --output hacked.uf2 | ++-----------------------------------------------------------------+ +| 6. Flash and verify | +| - Hold BOOTSEL, plug in, drag UF2 | +| - Check serial output and button/LED behavior | ++-----------------------------------------------------------------+ +``` + +### Key Memory Addresses + +| Address | Description | +| ------------ | ----------------------------------- | +| `0x10000234` | Typical main() entry point | +| `0x10003014` | stdio_init_all() function | +| `0x200005a8` | Static variable storage (example) | +| `0xd0000000` | SIO (Single-cycle I/O) base address | + +--- + +--- + +## Key Takeaways + +1. **Static variables persist** - They keep their value between function calls and loop iterations. + +2. **Static storage ? heap** - Static variables are in a fixed location, not dynamically allocated. + +3. **Compilers optimize aggressively** - Regular variables may be optimized away if the compiler sees no effect. + +4. **Function inlining is common** - `gpio_pull_up` becomes `gpio_set_pulls` in the binary. + +5. **Pull-up resistors invert logic** - Button pressed = LOW, button released = HIGH. + +6. **XOR is useful for inverting** - `eor r3,r3,#0x1` flips a bit between 0 and 1. + +7. **Static variables have fixed addresses** - You can find them in the .data section at known RAM addresses. + +8. **Overflow wraps around** - A `uint8_t` at 255 becomes 0 when incremented. + +9. **UBFX extracts bits** - Used to read a single GPIO pin from a register. + +10. **Binary patching is powerful** - Change values and logic without source code! + +--- + +## Glossary + +| Term | Definition | +| --------------------- | ---------------------------------------------------------------- | +| **Automatic** | Variable that's created and destroyed automatically (local vars) | +| **eor/XOR** | Exclusive OR - flips bits where operands differ | +| **Floating Input** | GPIO input with undefined voltage (reads random values) | +| **Function Inlining** | Compiler replaces function call with the function's code | +| **gpio_get** | Function to read the current state of a GPIO pin | +| **Heap** | Memory area for dynamic allocation (malloc/free) | +| **Overflow** | When a value exceeds its type's maximum and wraps around | +| **Pull-Down** | Resistor that holds a pin LOW when nothing drives it | +| **Pull-Up** | Resistor that holds a pin HIGH when nothing drives it | +| **SIO** | Single-cycle I/O - fast GPIO access on RP2350 | +| **Stack** | Memory area for local variables and function call frames | +| **Static Storage** | Fixed memory area for static and global variables | +| **Static Variable** | Variable declared with `static` that persists across calls | +| **Ternary Operator** | `condition ? value_if_true : value_if_false` | +| **UBFX** | Unsigned Bit Field Extract - extracts bits from a register | +| **Varargs** | Variable arguments - functions that take unlimited parameters | + +--- + +## Additional Resources + +### GPIO Input Reference + +| Function | Purpose | +| ----------------------------- | -------------------------- | +| `gpio_init(pin)` | Initialize GPIO pin | +| `gpio_set_dir(pin, GPIO_IN)` | Set pin as input | +| `gpio_set_dir(pin, GPIO_OUT)` | Set pin as output | +| `gpio_pull_up(pin)` | Enable internal pull-up | +| `gpio_pull_down(pin)` | Enable internal pull-down | +| `gpio_disable_pulls(pin)` | Disable all pull resistors | +| `gpio_get(pin)` | Read pin state (0 or 1) | +| `gpio_put(pin, value)` | Set pin output (0 or 1) | + +### Key Assembly Instructions + +| Instruction | Description | +| ----------------------- | -------------------------------------------- | +| `movs rN, #imm` | Move immediate value to register | +| `ldrb rN, [rM, #off]` | Load byte from memory | +| `strb rN, [rM, #off]` | Store byte to memory | +| `adds rN, #imm` | Add immediate value to register | +| `eor rN, rM, #imm` | Exclusive OR (XOR) with immediate | +| `ubfx rN, rM, #lsb, #w` | Extract unsigned bit field | +| `mcrr p0, ...` | Move to coprocessor (GPIO control on RP2350) | +| `b LABEL` | Unconditional branch (jump) | + +### Memory Map Quick Reference + +| Address Range | Description | +| --------------------- | ------------------------------ | +| `0x10000000` | XIP Flash (code execution) | +| `0x20000000-200005xx` | SRAM (.data section) | +| `0x20082000` | Stack top (initial SP) | +| `0x40038000` | PADS_BANK0 (pad configuration) | +| `0xd0000000` | SIO (single-cycle I/O) | + +--- + +**Remember:** Static variables are your friends when you need to remember values across function calls. But they also make your program's behavior more complex to analyze - which is exactly why we practice reverse engineering! + +Happy hacking! ? + + + diff --git a/WEEK06/slides/WEEK06-IMG00.svg b/WEEK06/slides/WEEK06-IMG00.svg new file mode 100644 index 0000000..0feedec --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 06 + + +Static Variables in Embedded Systems: +Debugging and Hacking Static Variables +w/ GPIO Input Basics + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK06/slides/WEEK06-IMG01.svg b/WEEK06/slides/WEEK06-IMG01.svg new file mode 100644 index 0000000..45e7fee --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG01.svg @@ -0,0 +1,74 @@ + + + + + +Static vs Regular Vars +Persistence across loop iterations + + + +Regular (auto) + + +Loop 1: 42 -> 43 -> destroy + + +Loop 2: 42 -> 43 -> destroy + + +Loop 3: 42 -> 43 -> destroy + +Always prints: +42 + + + +Static + + +Loop 1: 42 -> 43 (kept!) + + +Loop 2: 43 -> 44 (kept!) + + +Loop 3: 44 -> 45 (kept!) + +Keeps incrementing: +42,43,44... + + + +Declaration Syntax + + +uint8_t reg = 42; +Recreated each iteration + + +static uint8_t s = 42; +Persists for program life + + + +uint8_t Overflow + +255 + 1 = +0 +(wraps around!) + +Binary: 11111111 + 1 = 100000000 (9 bits) +Only 8 bits kept: 00000000 = 0 + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG02.svg b/WEEK06/slides/WEEK06-IMG02.svg new file mode 100644 index 0000000..dca4829 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG02.svg @@ -0,0 +1,93 @@ + + + + + +Memory Layout +Where variables live in RAM + + + +RP2350 SRAM Map + + + +STACK (grows down) +Local/auto variables + +0x20082000 + + + +(free space) + + + +HEAP (grows up) +malloc / free + + + +.bss section +Uninit static/global + + + +.data section +Initialized static/global + +0x20000000 + +Static vars: .data (init) +Static vars: .bss (uninit) + + + +Variable Storage + +Type +Location + + + +Automatic +Stack + +Static +.data / .bss + +Global +.data / .bss + +Dynamic +Heap + +Static vars are NOT on heap! +Fixed location, set at compile +time. Lives entire program. +Example: +static_fav_num @ 0x200005A8 + + + +Key Insight + +Stack vars: +Created + destroyed +each function call + +Static vars: +Fixed RAM address +persist entire runtime + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG03.svg b/WEEK06/slides/WEEK06-IMG03.svg new file mode 100644 index 0000000..279854a --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG03.svg @@ -0,0 +1,93 @@ + + + + + +GPIO Input Basics +Reading buttons with the RP2350 + + + +OUTPUT (before) + + +Pico GPIO 16 +--> +LED + +We CONTROL the LED +gpio_put(pin, value) + + +INPUT (new!) + + +Pico GPIO 15 +<-- +BTN + +We READ button state +gpio_get(pin) + + + +Floating Input + +No connection = +RANDOM values! + +Read 1: HIGH +Read 2: LOW +Read 3: HIGH +Read 4: HIGH +??? + + + +Pull Resistors + +Type +Default +Pressed + + + +Pull-Up +HIGH(1) +LOW(0) + +Pull-Down +LOW(0) +HIGH(1) + +Pico 2 has internal pull resistors! + + + +GPIO Input Functions + +gpio_init(pin) +Initialize pin + +gpio_set_dir(pin, GPIO_IN) +Set as input + +gpio_pull_up(pin) +Enable pull-up + +gpio_get(pin) +Read state (0 or 1) + +No external resistor needed -- internal pull-up! + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG04.svg b/WEEK06/slides/WEEK06-IMG04.svg new file mode 100644 index 0000000..4ab0ae6 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG04.svg @@ -0,0 +1,99 @@ + + + + + +Pull-Up Resistor +Internal pull-up on GPIO 15 + + + +Circuit + +3.3V + + + +pull-up R + + + +GPIO 15 + + + + +BTN + + +GND + + + +Button Logic + +State +GPIO +LED + + + +Released +HIGH (1) +OFF + +Pressed +LOW (0) +ON + +Inverted! Pull-up = backwards + + + +Ternary Operator + + +gpio_put(LED, pressed?0:1); + +pressed=1 -> LED OFF (inverted!) + + + +Hardware Wiring + + +Pico 2 +GPIO 15 +GPIO 16 +GND +BOOTSEL + +--> +--> +--> + + +Components +Button (one leg) +LED + resistor +Button (other leg) +Hold to flash UF2 + + +Key Point +No external +resistor needed! +Internal pull-up +handles it all + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG05.svg b/WEEK06/slides/WEEK06-IMG05.svg new file mode 100644 index 0000000..b612ee1 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG05.svg @@ -0,0 +1,83 @@ + + + + + +Source Code +0x0014_static-variables.c + + + +main() + + +int main(void) { +stdio_init_all(); +// GPIO setup +gpio_init(15); +gpio_set_dir(15, GPIO_IN); +gpio_pull_up(15); +gpio_init(16); +gpio_set_dir(16, GPIO_OUT); +while (true) { +uint8_t reg = 42; +static uint8_t s = 42; +printf(... reg, s); +reg++; s++; +} +} + + + +GPIO Setup + +Pin 15: +Input +Pull-up enabled + +Pin 16: +Output +LED control + + + +Serial Output + + +reg: 42 +s: 42 +reg: 42 +s: 43 + +reg always 42, s grows + + + +Button Logic + +pressed = gpio_get(15); + + + +Key Behaviors + + +reg: always 42 + + +s: 42,43,44... + + +wraps at 255->0 + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG06.svg b/WEEK06/slides/WEEK06-IMG06.svg new file mode 100644 index 0000000..a0919c3 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG06.svg @@ -0,0 +1,65 @@ + + + + + +Compiler Optimizations +What the compiler does to your code + + + +Optimization 1: Dead Code Removal + + +Your code: +uint8_t reg = 42; +reg++; +// No lasting effect! + + +Compiler output: +movs r1, #42 +// reg++ is GONE +// Uses constant 42 directly + + + +Optimization 2: Function Inlining + + +Your code: +gpio_pull_up(15); +// Simple wrapper func + + +Compiler output: +gpio_set_pulls(15,1,0); +// Inlined to real func + + + +Optimization 3: Scheduling + +Compiler reorders instructions +to avoid pipeline stalls: + + +Load SIO base + + +Increment s + + +Read GPIO + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG07.svg b/WEEK06/slides/WEEK06-IMG07.svg new file mode 100644 index 0000000..c359080 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG07.svg @@ -0,0 +1,90 @@ + + + + + +Assembly Analysis +Key instructions in main() loop + + + +Loop Body (0x10000264) + + +movs r1, #0x2a +; reg=42 +bl __wrap_printf +; print reg +ldrb r1, [r4] +; load static +bl __wrap_printf +; print s +mov.w r1, #0xd0000000 +; SIO base +ldrb r3, [r4] +; reload s +adds r3, #1 +; s++ +strb r3, [r4] +; store s +ldr r3, [r1, #4] +; read GPIO +ubfx r3, r3, #15, #1 +; bit 15 +eor.w r3, r3, #1 +; invert +mcrr 0, 4, r2, r3, cr0 +; GPIO out +b.n 0x10000264 +; loop + + + +Key Registers + +r1: +printf arg +r3: +temp / static +r4: +0x200005a8 +(static var addr) +r2: +LED pin (16) + + + +Key Patterns + +ldrb/adds/strb +Load-increment-store +(static var update) + +ubfx #15, #1 +Extract GPIO bit 15 + +eor.w #1 +Inverts (? 0 : 1) + + + +Infinite Loop + + +b.n 0x10000264 +; while(true) + +No pop/bx lr +main() never returns + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG08.svg b/WEEK06/slides/WEEK06-IMG08.svg new file mode 100644 index 0000000..702d322 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG08.svg @@ -0,0 +1,75 @@ + + + + + +GDB: Static Variable +Finding static_fav_num in RAM + + + +Literal Pool Lookup + + +ldr r4, [pc, #44] +@ 0x10000290 + +Examine literal pool: + +x/1wx 0x10000290 = 0x200005A8 + +r4 now holds the +RAM address of +static_fav_num! + + + +Read Value + + +x/1db 0x200005a8 = 42 + +After one loop iteration: + +x/1db 0x200005a8 = 43 + +It incremented! Persists in RAM. + + + +Disasm Gotcha + +x/i 0x10000290 shows: + + +lsls r0, r5, #22 + +GDB decodes DATA as code! +Bytes A8 05 00 20 = 0x200005A8 +Use x/wx not x/i for data + + + +GPIO Input Register + +Read button state in GDB: + + +p/x (*(uint*)0xd0000004 >> 15) & 1 + +Returns 1: +not pressed (pull-up) +Returns 0: +button pressed + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG09.svg b/WEEK06/slides/WEEK06-IMG09.svg new file mode 100644 index 0000000..c603290 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG09.svg @@ -0,0 +1,63 @@ + + + + + +Hacking the Binary +Two patches with a hex editor + + + +File Offset Formula +offset = address - 0x10000000 +Example: 0x10000264 -> 0x264 + + + +Hack 1: Change 42 to 43 + +Target: movs r1, #0x2a +at address 0x10000264 (offset 0x264) + + +Before: 2A 21 +movs r1, #42 + + +After: 2B 21 +movs r1, #43 + +Thumb encoding: imm8 byte first, opcode 0x21 second + + + +Hack 2: Invert Button Logic + +Target: eor.w r3, r3, #1 +at address 0x10000286 (offset 0x286) + + +Before: 83 F0 01 03 + + +After: 83 F0 00 03 + +Byte at offset 0x288: +01 +-> +00 + +XOR with 0 = no invert +LED now ON by default, OFF when pressed + \ No newline at end of file diff --git a/WEEK06/slides/WEEK06-IMG10.svg b/WEEK06/slides/WEEK06-IMG10.svg new file mode 100644 index 0000000..f88fa45 --- /dev/null +++ b/WEEK06/slides/WEEK06-IMG10.svg @@ -0,0 +1,112 @@ + + + + + +Static Vars & GPIO Input +Static variables, GPIO input, hacking + + + +Static vs Auto + +Aspect +Auto +Static + + + +Where +Stack +.data + +Life +Scope +Forever + +Init +Every +Once + +Keeps? +No +Yes + +Optimized? +Often +In RAM + +Compiler may remove auto vars + + + +GPIO Input Setup + +1. +gpio_init(pin) + +2. +gpio_set_dir(pin, GPIO_IN) + +3. +gpio_pull_up(pin) + +4. +gpio_get(pin) + +Pull-up: released=HIGH +pressed=LOW (inverted!) +Internal R, no hardware + + + +Key Instructions + +ubfx r3,r3,#15,#1 +Extract single GPIO bit + +eor.w r3,r3,#1 +XOR to invert logic + +b.n 0x10000264 + + + +Hacking Workflow + +1. +Analyze in GDB + +2. +Calculate offset + +3. +Patch .bin in HxD + +4. +uf2conv.py + flash + + + +Takeaways + + +42 -> 43 (1 byte) + + +XOR 1->0 (invert) + + +offset = addr - base + \ No newline at end of file diff --git a/WEEK07/WEEK07-SLIDES.pdf b/WEEK07/WEEK07-SLIDES.pdf new file mode 100644 index 0000000..e4a3b56 Binary files /dev/null and b/WEEK07/WEEK07-SLIDES.pdf differ diff --git a/WEEK07/WEEK07.md b/WEEK07/WEEK07.md new file mode 100644 index 0000000..25a723e --- /dev/null +++ b/WEEK07/WEEK07.md @@ -0,0 +1,1074 @@ +# Week 7: Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Understand the difference between `#define` macros and `const` variables +- Know how constants are stored differently in memory (compile-time vs runtime) +- Understand the I2C (Inter-Integrated Circuit) communication protocol +- Configure I2C peripherals and communicate with LCD displays +- Understand C structs and how the Pico SDK uses them for hardware abstraction +- Use GDB to examine constants, structs, and string literals in memory +- Hack constant values and string literals using a hex editor +- Patch LCD display text without access to source code + +--- + +## Part 1: Understanding Constants in C + +### Two Types of Constants + +In C, there are two ways to create values that shouldn't change: + +| Type | Syntax | Where It Lives | When Resolved | +| ----------- | ----------------------- | ------------------ | ------------- | +| **#define** | `#define FAV_NUM 42` | Nowhere (replaced) | Compile time | +| **const** | `const int NUM = 1337;` | Flash (.rodata) | Runtime | + +### Preprocessor Macros (#define) + +A **preprocessor macro** is a text replacement that happens BEFORE your code is compiled: + +```c +#define FAV_NUM 42 + +printf("Value: %d", FAV_NUM); +// Becomes: printf("Value: %d", 42); +``` + +Think of it like a "find and replace" in a text editor. The compiler never sees `FAV_NUM` - it only sees `42`! + +``` ++-----------------------------------------------------------------+ +| Preprocessor Macro Flow | +| | +| Source Code Preprocessor Compiler | +| +----------+ +----------+ +----------+ | +| | #define | | Replace | | Compile | | +| | FAV_NUM | -----? | FAV_NUM | -----? | binary | | +| | 42 | | with 42 | | code | | +| +----------+ +----------+ +----------+ | +| | +| FAV_NUM doesn't exist in the final binary! | +| The value 42 is embedded directly in instructions. | +| | ++-----------------------------------------------------------------+ +``` + +### Const Variables + +A **const variable** is an actual variable stored in memory, but marked as read-only: + +```c +const int OTHER_FAV_NUM = 1337; +``` + +Unlike `#define`, this creates a real memory location in the `.rodata` (read-only data) section of flash: + +``` ++-----------------------------------------------------------------+ +| Const Variable in Memory | +| | +| Flash Memory (.rodata section) | +| +------------------------------------------------------------+ | +| | Address: 0x10001234 | | +| | Value: 0x00000539 (1337 in hex) | | +| | Name: OTHER_FAV_NUM (in debug symbols only) | | +| +------------------------------------------------------------+ | +| | +| The variable EXISTS in memory and can be read at runtime. | +| | ++-----------------------------------------------------------------+ +``` + +### Comparison: #define vs const + +| Feature | #define | const | +| -------------------- | ------------------------ | ---------------------------- | +| **Type checking** | None (just text) | Yes (compiler enforced) | +| **Memory usage** | None (inlined) | Uses flash space | +| **Debugger visible** | No | Yes (with symbols) | +| **Can take address** | No (`&FAV_NUM` fails) | Yes (`&OTHER_FAV_NUM` works) | +| **Scope** | Global (from definition) | Normal C scoping rules | + +--- + +## Part 2: Understanding I2C Communication + +### What is I2C? + +**I2C** (pronounced "I-squared-C" or "I-two-C") stands for **Inter-Integrated Circuit**. It's a way for chips to talk to each other using just TWO wires! + +``` ++-----------------------------------------------------------------+ +| I2C Bus - Two Wires, Many Devices | +| | +| 3.3V | +| | | +| + Pull-up + Pull-up | +| | | | +| SDA -+------------+--------------------------------------- | +| | | | +| SCL -+------------+--------------------------------------- | +| | | | | | +| +---+----+ +--+---+ +----+--+ +-----+---+ | +| | Pico | | LCD | |Sensor | | EEPROM | | +| |(Master)| | 0x27 | | 0x48 | | 0x50 | | +| +--------+ +------+ +-------+ +---------+ | +| | +| Each device has a unique address (0x27, 0x48, 0x50...) | +| | ++-----------------------------------------------------------------+ +``` + +### The Two I2C Wires + +| Wire | Name | Purpose | +| ------- | ------------ | ------------------------------------ | +| **SDA** | Serial Data | Carries the actual data bits | +| **SCL** | Serial Clock | Timing signal that synchronizes data | + +### Why Pull-Up Resistors? + +I2C uses **open-drain** signals, meaning devices can only pull the line LOW. They can't drive it HIGH! Pull-up resistors are needed to bring the lines back to HIGH when no device is pulling them down. + +The Pico 2 has internal pull-ups that we can enable with `gpio_pull_up()`. + +### I2C Addresses + +Every I2C device has a unique **7-bit address**. Common addresses: + +| Device Type | Typical Address | +| --------------------- | ---------------- | +| 1602 LCD with PCF8574 | `0x27` or `0x3F` | +| Temperature sensor | `0x48` | +| EEPROM | `0x50` | +| Real-time clock | `0x68` | + +### I2C Communication Flow + +``` ++-----------------------------------------------------------------+ +| I2C Transaction | +| | +| 1. Master sends START condition | +| 2. Master sends device address (7 bits) + R/W bit | +| 3. Addressed device sends ACK (acknowledge) | +| 4. Data is transferred (8 bits at a time) | +| 5. Receiver sends ACK after each byte | +| 6. Master sends STOP condition | +| | +| START --? Address --? ACK --? Data --? ACK --? STOP | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 3: Understanding C Structs + +### What is a Struct? + +A **struct** (short for "structure") is a way to group related variables together under one name. Think of it like a form with multiple fields: + +```c +// A struct definition - like a template +struct student { + char name[50]; + int age; + float gpa; +}; + +// Creating a variable of this struct type +struct student alice = {"Alice", 16, 3.8}; +``` + +### Why Use Structs? + +Instead of passing many separate variables: +```c +void print_student(char *name, int age, float gpa); // Messy! +``` + +You pass one struct: +```c +void print_student(struct student s); // Clean! +``` + +### The typedef Keyword + +Writing `struct student` everywhere is tedious. The `typedef` keyword creates an alias: + +```c +typedef struct student student_t; + +// Now you can write: +student_t alice; // Instead of: struct student alice; +``` + +### Forward Declaration + +Sometimes you need to tell the compiler "this struct exists" before defining it: + +```c +typedef struct i2c_inst i2c_inst_t; // Forward declaration + alias + +// Later, the full definition: +struct i2c_inst { + i2c_hw_t *hw; + bool restart_on_next; +}; +``` + +--- + +## Part 4: Understanding the Pico SDK's I2C Structs + +### The i2c_inst_t Struct + +The Pico SDK uses a struct to represent each I2C controller: + +```c +struct i2c_inst { + i2c_hw_t *hw; // Pointer to hardware registers + bool restart_on_next; // SDK internal flag +}; +``` + +**What each member means:** + +| Member | Type | Purpose | +| ----------------- | ------------ | ---------------------------------------- | +| `hw` | `i2c_hw_t *` | Pointer to the actual hardware registers | +| `restart_on_next` | `bool` | Tracks if next transfer needs a restart | + +### The Macro Chain + +When you write `I2C_PORT` in your code, here's what happens: + +``` ++-----------------------------------------------------------------+ +| Macro Expansion Chain | +| | +| In your code: #define I2C_PORT i2c1 | +| | | +| ? | +| In i2c.h: #define i2c1 (&i2c1_inst) | +| | | +| ? | +| In i2c.c: i2c_inst_t i2c1_inst = {i2c1_hw, false}; | +| | | +| ? | +| In i2c.h: #define i2c1_hw ((i2c_hw_t *)I2C1_BASE) | +| | | +| ? | +| In addressmap.h: #define I2C1_BASE 0x40098000 | +| | ++-----------------------------------------------------------------+ +``` + +So `I2C_PORT` eventually becomes a pointer to a struct that contains a pointer to hardware registers at address `0x40098000`! + +### The Hardware Register Pointer + +The `i2c_hw_t *hw` member points to the actual silicon: + +``` ++-----------------------------------------------------------------+ +| Memory Map | +| | +| Address 0x40098000: I2C1 Hardware Registers | +| +------------------------------------------------------------+ | +| | Offset 0x00: IC_CON (Control register) | | +| | Offset 0x04: IC_TAR (Target address register) | | +| | Offset 0x10: IC_DATA_CMD (Data command register) | | +| | ... | | +| +------------------------------------------------------------+ | +| | +| The i2c_hw_t struct maps directly to these registers! | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 5: The ARM Calling Convention (AAPCS) + +### How Arguments Are Passed + +On ARM Cortex-M, the **ARM Architecture Procedure Call Standard (AAPCS)** defines how functions receive arguments: + +| Register | Purpose | +| -------- | ---------------- | +| `r0` | First argument | +| `r1` | Second argument | +| `r2` | Third argument | +| `r3` | Fourth argument | +| Stack | Fifth+ arguments | +| `r0` | Return value | + +### Example: i2c_init(i2c1, 100000) + +```c +i2c_init(I2C_PORT, 100000); +``` + +In assembly: +```assembly +ldr r0, [address of i2c1_inst] ; r0 = pointer to struct (first arg) +ldr r1, =0x186A0 ; r1 = 100000 (second arg) +bl i2c_init ; Call the function +``` + +--- + +## Part 6: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. OpenOCD installed and configured +4. GDB (`arm-none-eabi-gdb`) installed +5. Python installed (for UF2 conversion) +6. A serial monitor (PuTTY, minicom, or screen) +7. A 1602 LCD display with I2C backpack (PCF8574) +8. A hex editor (HxD, ImHex, or similar) +9. The sample project: `0x0017_constants` + +### Hardware Setup + +Connect your LCD like this: + +| LCD Pin | Pico 2 Pin | +| ------- | ---------- | +| VCC | 3.3V or 5V | +| GND | GND | +| SDA | GPIO 2 | +| SCL | GPIO 3 | + +``` ++-----------------------------------------------------------------+ +| I2C LCD Wiring | +| | +| Pico 2 1602 LCD + I2C Backpack | +| +----------+ +----------------------+ | +| | | | | | +| | GPIO 2 |------- SDA -----?| SDA | | +| | (SDA) | | | | +| | | | +------------+ | | +| | GPIO 3 |------- SCL -----?| SCL| Reverse | | | +| | (SCL) | | |Engineering | | | +| | | | +------------+ | | +| | 3.3V |------- VCC -----?| VCC | | +| | | | | | +| | GND |------- GND -----?| GND | | +| | | | | | +| +----------+ +----------------------+ | +| | ++-----------------------------------------------------------------+ +``` + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x0017_constants/ +| +-- build/ +| | +-- 0x0017_constants.uf2 +| | +-- 0x0017_constants.bin +| +-- 0x0017_constants.c +| +-- lcd_1602.h ++-- uf2conv.py +``` + +--- + +## Part 7: Hands-On Tutorial - Constants and I2C LCD + +### Step 1: Review the Source Code + +Let's examine the constants code: + +**File: `0x0017_constants.c`** + +```c +#include +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "lcd_1602.h" + +#define FAV_NUM 42 +#define I2C_PORT i2c1 +#define I2C_SDA_PIN 2 +#define I2C_SCL_PIN 3 + +const int OTHER_FAV_NUM = 1337; + +int main(void) { + stdio_init_all(); + + i2c_init(I2C_PORT, 100000); + gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(I2C_SDA_PIN); + gpio_pull_up(I2C_SCL_PIN); + + lcd_i2c_init(I2C_PORT, 0x27, 4, 0x08); + lcd_set_cursor(0, 0); + lcd_puts("Reverse"); + lcd_set_cursor(1, 0); + lcd_puts("Engineering"); + + while (true) { + printf("FAV_NUM: %d\r\n", FAV_NUM); + printf("OTHER_FAV_NUM: %d\r\n", OTHER_FAV_NUM); + } +} +``` + +**What this code does:** + +1. **Lines 7-10:** Define preprocessor macros for constants and I2C configuration +2. **Line 12:** Define a `const` variable stored in flash +3. **Line 15:** Initialize UART for serial output +4. **Lines 17-21:** Initialize I2C1 at 100kHz, configure GPIO pins, enable pull-ups +5. **Lines 23-27:** Initialize LCD and display "Reverse" on line 0, "Engineering" on line 1 +6. **Lines 29-32:** Infinite loop printing both constant values to serial terminal + +### Step 2: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0017_constants.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 3: Verify It's Working + +**Check the LCD:** +- Line 1 should show: `Reverse` +- Line 2 should show: `Engineering` + +**Check the serial monitor (PuTTY/screen):** +``` +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +... +``` + +--- + +## Part 8: Debugging with GDB (Dynamic Analysis) + +> ? **REVIEW:** This setup is identical to previous weeks. If you need a refresher on OpenOCD and GDB connection, refer back to Week 3 Part 6. + +### Starting the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +**Terminal 2 - Start GDB:** + +```cmd +arm-none-eabi-gdb build\0x0017_constants.elf +``` + +**Connect to target:** + +```gdb +(gdb) target extended-remote :3333 +(gdb) monitor reset halt +``` + +### Step 4: Examine Main Function + +Let's examine the main function. Disassemble from the entry point: + +``` +x/54i 0x10000234 +``` + +You should see output like: + +``` +(gdb) x/54i 0x10000234 + 0x10000234
: push {r3, lr} + 0x10000236 : bl 0x100037fc + 0x1000023a : ldr r1, [pc, #104] @ (0x100002a4 ) + 0x1000023c : ldr r0, [pc, #104] @ (0x100002a8 ) + 0x1000023e : bl 0x10003cdc + 0x10000242 : movs r1, #3 + 0x10000244 : movs r0, #2 + 0x10000246 : bl 0x100008f0 + 0x1000024a : movs r1, #3 + 0x1000024c : mov r0, r1 + 0x1000024e : bl 0x100008f0 + 0x10000252 : movs r2, #0 + 0x10000254 : movs r1, #1 + 0x10000256 : movs r0, #2 + 0x10000258 : bl 0x1000092c + 0x1000025c : movs r2, #0 + 0x1000025e : movs r1, #1 + 0x10000260 : movs r0, #3 + 0x10000262 : bl 0x1000092c + 0x10000266 : movs r3, #8 + 0x10000268 : movs r2, #4 + 0x1000026a : movs r1, #39 @ 0x27 + 0x1000026c : ldr r0, [pc, #56] @ (0x100002a8 ) + 0x1000026e : bl 0x100002bc + 0x10000272 : movs r1, #0 + 0x10000274 : mov r0, r1 + 0x10000276 : bl 0x100006f4 + 0x1000027a : ldr r0, [pc, #48] @ (0x100002ac ) + 0x1000027c : bl 0x100007f0 + 0x10000280 : movs r0, #1 + 0x10000282 : movs r1, #0 + 0x10000284 : bl 0x100006f4 + 0x10000288 : ldr r0, [pc, #36] @ (0x100002b0 ) + 0x1000028a : bl 0x100007f0 + 0x1000028e : movs r1, #42 @ 0x2a + 0x10000290 : ldr r0, [pc, #32] @ (0x100002b4 ) + 0x10000292 : bl 0x1000398c <__wrap_printf> + 0x10000296 : movw r1, #1337 @ 0x539 + 0x1000029a : ldr r0, [pc, #28] @ (0x100002b8 ) + 0x1000029c : bl 0x1000398c <__wrap_printf> + 0x100002a0 : b.n 0x1000028e + 0x100002a2 : nop + 0x100002a4 : strh r0, [r4, #52] @ 0x34 + 0x100002a6 : movs r1, r0 + 0x100002a8 : lsls r4, r5, #24 + 0x100002aa : movs r0, #0 + 0x100002ac : subs r6, #232 @ 0xe8 + 0x100002ae : asrs r0, r0, #32 + 0x100002b0 : subs r6, #240 @ 0xf0 + 0x100002b2 : asrs r0, r0, #32 + 0x100002b4 : subs r6, #252 @ 0xfc + 0x100002b6 : asrs r0, r0, #32 + 0x100002b8 : subs r7, #12 + 0x100002ba : asrs r0, r0, #32 +``` + +### Step 5: Set a Breakpoint at Main + +``` +b *0x10000234 +c +``` + +### Step 6: Find the #define Constant (FAV_NUM) + +Step through to the printf call and examine the registers: + +``` +x/20i 0x1000028e +``` + +Look for: +``` +... + 0x1000028e : movs r1, #42 @ 0x2a +... +``` + +The `#define` constant is embedded directly as an immediate value in the instruction! + +### Step 7: Find the const Variable (OTHER_FAV_NUM) + +Continue examining the loop body: + +```gdb +(gdb) x/5i 0x10000296 +``` + +Look for this instruction: + +``` +... + 0x10000296 : movw r1, #1337 @ 0x539 +... +``` + +**Surprise!** The `const` variable is ALSO embedded as an immediate value - not loaded from memory! The compiler saw that `OTHER_FAV_NUM` is never address-taken (`&OTHER_FAV_NUM` is never used), so it optimized the `const` the same way as `#define` - as a constant embedded directly in the instruction. + +The difference is the instruction encoding: +- `FAV_NUM` (42): `movs r1, #0x2a` - 16-bit Thumb instruction (values 0-255) +- `OTHER_FAV_NUM` (1337): `movw r1, #0x539` - 32-bit Thumb-2 instruction (values 0-65535) + +> Tip: **Why `movw` instead of `movs`?** The value 1337 doesn't fit in 8 bits (max 255), so the compiler uses `movw` (Move Wide) which can encode any 16-bit immediate (0-65535) in a 32-bit instruction. + +### Step 8: Examine the Literal Pool + +The literal pool after the loop contains addresses and constants that are too large for regular instruction immediates. Let's examine it: + +```gdb +(gdb) x/6wx 0x100002a4 +0x100002a4 : 0x000186a0 0x2000062c 0x10003ee8 0x10003ef0 +0x100002b4 : 0x10003efc 0x10003f0c +``` + +These are the values that `ldr rN, [pc, #offset]` instructions load: + +| Literal Pool Addr | Value | Used By | +| ----------------- | -------------- | ------------------------------ | +| `0x100002a4` | `0x000186A0` | I2C baudrate (100000) | +| `0x100002a8` | `0x2000062C` | &i2c1_inst (I2C struct in RAM) | +| `0x100002ac` | `0x10003EE8` | "Reverse" string address | +| `0x100002b0` | `0x10003EF0` | "Engineering" string address | +| `0x100002b4` | `0x10003EFC` | "FAV_NUM: %d\r\n" format str | +| `0x100002b8` | `0x10003F0C` | "OTHER_FAV_NUM: %d\r\n" fmt | + +> Tip: **Why does the disassembly at `0x100002a4` show `strh r0, [r4, #52]` instead of data?** Same reason as Week 6 - GDB's `x/i` tries to decode raw data as instructions. Use `x/wx` to see the actual word values or we can also use `x/x`. + +```gdb +(gdb) x/x 0x100002a4 +0x100002a4 : 0x000186a0 +(gdb) x/x 0x100002a8 +0x100002a8 : 0x2000062c +(gdb) x/x 0x100002ac +0x100002ac : 0x10003ee8 +(gdb) x/x 0x100002b0 +0x100002b0 : 0x10003ef0 +(gdb) x/x 0x100002b4 +0x100002b4 : 0x10003efc +(gdb) x/x 0x100002b8 +0x100002b8 : 0x10003f0c +``` + +### Step 9: Examine the I2C Struct + +Find the i2c1_inst struct address loaded into r0 before i2c_init: + +``` +x/2wx 0x2000062c +``` + +You should see: +``` +0x2000062c : 0x40098000 0x00000000 +``` + +### Step 10: Examine the LCD String Literals + +Find the strings passed to lcd_puts: + +``` +x/s 0x10003ee8 +``` + +Output: +``` +0x10003ee8: "Reverse" +``` + +``` +x/s 0x10003ef0 +``` + +Output: +``` +0x10003ef0: "Engineering" +``` + +### Step 11: Step Through I2C Initialization + +Step through instructions and watch the I2C setup: + +```gdb +(gdb) b *0x1000023e +(gdb) c +(gdb) i r r0 r1 +r0 0x2000062c 536872492 +r1 0x186a0 100000 +``` + +--- + +## Part 9: Understanding the Assembly + +Now that we've explored the binary in GDB, let's make sense of the key patterns we found. + +### Step 12: Analyze #define vs const in Assembly + +From GDB, we discovered something interesting - **both constants ended up as instruction immediates!** + +**For FAV_NUM (42) - a `#define` macro:** +``` +0x1000028e <+90>: movs r1, #42 @ 0x2a +``` + +The value 42 is embedded directly in a 16-bit Thumb instruction. This is expected - `#define` is text replacement, so the compiler never sees `FAV_NUM`, only `42`. + +**For OTHER_FAV_NUM (1337) - a `const` variable:** +``` +0x10000296 <+98>: movw r1, #1337 @ 0x539 +``` + +The value 1337 is ALSO embedded directly in an instruction - but this time a 32-bit Thumb-2 `movw` because the value doesn't fit in 8 bits. + +**Why wasn't `const` stored in memory?** In theory, `const int OTHER_FAV_NUM = 1337` creates a variable in the `.rodata` section. But the compiler optimized it away because: +1. We never take the address of `OTHER_FAV_NUM` (no `&OTHER_FAV_NUM`) +2. The value fits in a 16-bit `movw` immediate +3. Loading from an immediate is faster than loading from memory + +> Tip: **Key takeaway for reverse engineering:** Don't assume `const` variables will appear as memory loads. Modern compilers aggressively inline constant values. The C keyword `const` is a **source-level** concept - the compiler may or may not honor it in the final binary. + +### Step 13: Analyze the I2C Struct Layout + +In GDB, we examined the `i2c1_inst` struct at `0x2000062c`: + +```gdb +(gdb) x/2wx 0x2000062c +0x2000062c : 0x40098000 0x00000000 +``` + +This maps to the `i2c_inst_t` struct: + +``` ++-----------------------------------------------------------------+ +| i2c_inst_t at 0x2000062c | +| | +| +------------------------------------------------------------+ | +| | Offset Type Name Value | | +| | 0x00 i2c_hw_t * hw 0x40098000 | | +| | 0x04 bool restart_on_next 0x00 (false) | | +| +------------------------------------------------------------+ | +| | ++-----------------------------------------------------------------+ +``` + +The first member (`hw`) points to `0x40098000` - the I2C1 hardware register base. This is the end of the macro chain: `I2C_PORT` -> `i2c1` -> `&i2c1_inst` -> `hw` -> `0x40098000`. + +### Step 14: Locate the String Literals + +We found the LCD strings in flash memory: + +```gdb +(gdb) x/s 0x10003ee8 +0x10003ee8: "Reverse" + +(gdb) x/s 0x10003ef0 +0x10003ef0: "Engineering" +``` + +These are stored consecutively in the `.rodata` section. Note the addresses - we'll need them for patching. + +--- + +## Part 10: Hacking the Binary with a Hex Editor + +Now for the fun part - we'll patch the `.bin` file directly using a hex editor! + +> Tip: **Why a hex editor?** GDB **cannot write to flash memory** - the `0x10000000+` address range where program instructions and read-only data live. Trying `set *(char *)0x1000028e = 0x2b` in GDB gives `Writing to flash memory forbidden in this context`. To make **permanent** patches that survive a power cycle, we edit the `.bin` file directly with a hex editor and re-flash it. + +### Step 15: Open the Binary in a Hex Editor + +1. Open **HxD** (or your preferred hex editor: ImHex, 010 Editor, etc.) +2. Click **File** -> **Open** +3. Navigate to `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants\build\` +4. Open `0x0017_constants.bin` + +### Step 16: Calculate the File Offset + +The binary is loaded at base address `0x10000000`. To find the file offset of any address: + +``` +file_offset = address - 0x10000000 +``` + +For example: +- Address `0x1000028e` -> file offset `0x28E` (654 in decimal) +- Address `0x10003ee8` -> file offset `0x3EE8` (16104 in decimal) + +### Step 17: Understand FAV_NUM Encoding (movs - 16-bit Thumb) + +From our GDB analysis, we know the instruction at `0x1000028e` is: + +``` +movs r1, #0x2a -> bytes: 2a 21 +``` + +In HxD, use **Ctrl+G** to navigate to file offset `28E` and verify you see the byte `2A` followed by `21`. + +> ?? **How Thumb encoding works:** In `movs r1, #imm8`, the immediate value is the first byte, and the opcode `21` is the second byte. So the bytes `2a 21` encode `movs r1, #0x2a` (42). If you wanted to change this to 43, you'd change `2A` to `2B`. + +### Step 18: Understand OTHER_FAV_NUM Encoding (movw - 32-bit Thumb-2) + +From GDB, we found the `movw r1, #1337` instruction at `0x10000296`. Examine the exact bytes: + +```gdb +(gdb) x/4bx 0x10000296 +0x10000296 : 0x40 0xf2 0x39 0x51 +``` + +This is the 32-bit Thumb-2 encoding of `movw r1, #0x539` (1337). The bytes break down as: + +``` ++-----------------------------------------------------------------+ +| movw r1, #0x539 -> bytes: 40 F2 39 51 | +| | +| Byte 0: 0x40 -?? | +| Byte 1: 0xF2 -+ First halfword (opcode + upper imm bits) | +| Byte 2: 0x39 ---- Lower 8 bits of immediate (imm8) ?? CHANGE | +| Byte 3: 0x51 ---- Destination register (r1) + upper imm bits | +| | +| imm16 = 0x0539 = 1337 decimal | +| imm8 field = 0x39 (lower 8 bits of the value) | +| | ++-----------------------------------------------------------------+ +``` + +The file offset is `0x10000296 - 0x10000000 = 0x296`. The imm8 byte is the 3rd byte of the instruction: `0x296 + 2 = 0x298`. + +To change `movw r1, #1337` to `movw r1, #1344`: + +1. In HxD, press **Ctrl+G** (Go to offset) +2. Enter offset: `298` (the third byte of the 4-byte instruction) +3. You should see the byte `39` at this position +4. Change `39` to `40` + +> ?? **Why offset `0x298` and not `0x296`?** The lower 8 bits of the immediate (`imm8`) are in the **third byte** of the 4-byte `movw` instruction. The instruction starts at file offset `0x296`, so imm8 is at `0x296 + 2 = 0x298`. Changing `0x39` to `0x40` changes the value from `0x539` (1337) to `0x540` (1344). + +### Step 19: Hack - Change LCD Text from "Reverse" to "Exploit" + +**IMPORTANT:** The new string must be the **same length** as the original! "Reverse" and "Exploit" are both 7 characters - perfect! + +From our GDB analysis in Step 10, we found the string at `0x10003ee8`. File offset = `0x10003ee8 - 0x10000000 = 0x3EE8`. + +1. In HxD, press **Ctrl+G** and enter offset: `3EE8` +2. You should see the bytes for "Reverse": `52 65 76 65 72 73 65 00` +3. Change the bytes to spell "Exploit": `45 78 70 6c 6f 69 74 00` + +**ASCII Reference:** + +| Character | Hex | +| --------- | ------ | +| E | `0x45` | +| x | `0x78` | +| p | `0x70` | +| l | `0x6c` | +| o | `0x6f` | +| i | `0x69` | +| t | `0x74` | + +### Step 20: Save the Patched Binary + +1. Click **File** -> **Save As** +2. Save as `0x0017_constants-h.bin` in the build directory +3. Close the hex editor + +--- + +## Part 11: Converting and Flashing the Hacked Binary + +### Step 21: Convert to UF2 Format + +Open a terminal and navigate to your project directory: + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants +``` + +Run the conversion command: + +```cmd +python ..\uf2conv.py build\0x0017_constants-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 22: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Check your LCD and serial monitor + +### Step 23: Verify the Hack + +**Check the LCD:** +- Line 1 should now show: `Exploit` (instead of "Reverse") +- Line 2 should still show: `Engineering` + +**Check the serial monitor:** +``` +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +... +``` + +The numbers are unchanged - we only patched the LCD string! + + **BOOM! We successfully changed the LCD text from "Reverse" to "Exploit" without access to the source code!** + +--- + +## Part 12: Summary and Review + +### What We Accomplished + +1. **Learned about constants** - `#define` macros vs `const` variables +2. **Understood I2C communication** - Two-wire protocol for peripheral communication +3. **Explored C structs** - How the Pico SDK abstracts hardware +4. **Mastered the macro chain** - From `I2C_PORT` to `0x40098000` +5. **Examined structs in GDB** - Inspected memory layout of `i2c_inst_t` +6. **Analyzed instruction encodings** - Both `movs` (8-bit) and `movw` (16-bit) immediates in the hex editor +7. **Patched a string literal** - Changed LCD display text from "Reverse" to "Exploit" + +### #define vs const Summary + +``` ++-----------------------------------------------------------------+ +| #define FAV_NUM 42 | +| ------------------- | +| - Text replacement at compile time | +| - No memory allocated | +| - Cannot take address (&FAV_NUM is invalid) | +| - In binary: value appears as immediate (movs r1, #0x2a) | +| - To hack: patch the instruction operand | ++-----------------------------------------------------------------+ +| const int OTHER_FAV_NUM = 1337 | +| ------------------------------ | +| - Theoretically in .rodata, but compiler optimized it away | +| - Value embedded as immediate: movw r1, #0x539 (32-bit instr) | +| - Optimization: compiler saw &OTHER_FAV_NUM is never used | +| - In binary: immediate in instruction, same as #define! | +| - To hack: patch instruction operand (imm8 byte at offset +2) | ++-----------------------------------------------------------------+ +``` + +### I2C Configuration Summary + +``` ++-----------------------------------------------------------------+ +| I2C Setup Steps | +| | +| 1. i2c_init(i2c1, 100000) - Initialize at 100kHz | +| 2. gpio_set_function(pin, I2C) - Assign pins to I2C | +| 3. gpio_pull_up(sda_pin) - Enable SDA pull-up | +| 4. gpio_pull_up(scl_pin) - Enable SCL pull-up | +| 5. lcd_i2c_init(...) - Initialize the device | +| | ++-----------------------------------------------------------------+ +``` + +### The Struct Chain + +``` ++-----------------------------------------------------------------+ +| I2C_PORT -> i2c1 -> &i2c1_inst -> i2c_inst_t | +| | | +| +-- hw -> i2c_hw_t * | +| | +-- 0x40098000 | +| | | +| +-- restart_on_next (bool) | +| | ++-----------------------------------------------------------------+ +``` + +### Key Memory Addresses + +| Address | Description | +| ------------ | ---------------------------------- | +| `0x10000234` | main() entry point | +| `0x1000028e` | FAV_NUM value in instruction | +| `0x10000296` | OTHER_FAV_NUM value in instruction | +| `0x10003ee8` | "Reverse" string literal (example) | +| `0x40098000` | I2C1 hardware registers base | +| `0x2000062C` | i2c1_inst struct in SRAM | + +--- + +--- + +## Key Takeaways + +1. **#define is text replacement** - It happens before compilation, no memory used. + +2. **const creates real variables** - Stored in .rodata, takes memory, has an address. + +3. **I2C uses two wires** - SDA for data, SCL for clock, pull-ups required. + +4. **Structs group related data** - The SDK uses them to abstract hardware. + +5. **Macros can chain** - `I2C_PORT` -> `i2c1` -> `&i2c1_inst` -> hardware pointer. + +6. **ARM passes args in registers** - r0-r3 for first four arguments. + +7. **GDB reveals struct layouts** - Examine memory to understand data organization. + +8. **String hacking requires same length** - Or you'll corrupt adjacent data! + +9. **Constants aren't constant** - With binary patching, everything can change! + +10. **Compiler optimization changes code** - `gpio_pull_up` becomes `gpio_set_pulls`. + +--- + +## Glossary + +| Term | Definition | +| ----------------------- | --------------------------------------------------- | +| **#define** | Preprocessor directive for text replacement | +| **AAPCS** | ARM Architecture Procedure Call Standard | +| **const** | Keyword marking a variable as read-only | +| **Forward Declaration** | Telling compiler a type exists before defining it | +| **I2C** | Inter-Integrated Circuit - two-wire serial protocol | +| **Immediate Value** | A constant embedded directly in an instruction | +| **Open-Drain** | Output that can only pull low, not drive high | +| **PCF8574** | Common I2C I/O expander chip used in LCD backpacks | +| **Preprocessor** | Tool that processes code before compilation | +| **Pull-Up Resistor** | Resistor that holds a line HIGH by default | +| **SCL** | Serial Clock - I2C timing signal | +| **SDA** | Serial Data - I2C data line | +| **Struct** | User-defined type grouping related variables | +| **typedef** | Creates an alias for a type | + +--- + +## Additional Resources + +### I2C Timing Reference + +| Speed Mode | Maximum Frequency | +| ---------- | ----------------- | +| Standard | 100 kHz | +| Fast | 400 kHz | +| Fast Plus | 1 MHz | + +### Common I2C Addresses + +| Device | Address | +| --------------------- | ------------- | +| PCF8574 LCD (default) | `0x27` | +| PCF8574A LCD | `0x3F` | +| DS3231 RTC | `0x68` | +| BMP280 Sensor | `0x76`/`0x77` | +| SSD1306 OLED | `0x3C`/`0x3D` | + +### Key ARM Instructions for Constants + +| Instruction | Description | +| -------------------- | ------------------------------------------- | +| `movs rN, #imm` | Load small immediate (0-255) directly | +| `ldr rN, [pc, #off]` | Load larger value from literal pool | +| `ldr rN, =value` | Pseudo-instruction for loading any constant | + +### RP2350 I2C Memory Map + +| Address | Description | +| ------------ | ----------------------- | +| `0x40090000` | I2C0 hardware registers | +| `0x40098000` | I2C1 hardware registers | + +--- + +**Remember:** When you see complex nested structures in a binary, take your time to understand the hierarchy. Use GDB to examine struct layouts in memory and trace pointer chains. And always remember - even "constants" can be hacked! + +Happy hacking! ? \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG00.svg b/WEEK07/slides/WEEK07-IMG00.svg new file mode 100644 index 0000000..427c6f7 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 07 + + +Constants in Embedded Systems: +Debugging and Hacking Constants +w/ 1602 LCD I2C Basics + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK07/slides/WEEK07-IMG01.svg b/WEEK07/slides/WEEK07-IMG01.svg new file mode 100644 index 0000000..bbe54c4 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG01.svg @@ -0,0 +1,63 @@ + + + + +#define vs const +Preprocessor Macros vs Constant Variables + + + +#define FAV_NUM 42 + +Preprocessor text replacement +Happens BEFORE compilation +No memory allocated +Cannot take address (&) + +In Binary: + +movs r1, #42 @ 0x2a + +16-bit Thumb instruction +Value embedded as immediate +Compiler sees only "42" + + + +const int OTHER_FAV_NUM=1337 + +Creates real variable +Theoretically in .rodata +Has an address (if needed) +Type-checked by compiler + +In Binary: + +movw r1, #1337 @ 0x539 + +32-bit Thumb-2 instruction +Also embedded as immediate! +Compiler optimized it away + + + +KEY INSIGHT: +Both ended up as instruction immediates! +The compiler saw &OTHER_FAV_NUM is never used, so it +optimized const the same way as #define -- no memory load needed. +Lesson: const is a source-level concept -- not guaranteed in binary + + + + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG02.svg b/WEEK07/slides/WEEK07-IMG02.svg new file mode 100644 index 0000000..d798cd5 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG02.svg @@ -0,0 +1,85 @@ + + + + +I2C Protocol +Two-Wire Serial Communication + + + +What is I2C? +Two-wire serial protocol +SDA += Serial Data +SCL += Serial Clock +Open-drain with pull-up resistors + + + +I2C Bus + +Pico + + +SDA +SCL + +LCD +GPIO 2 = SDA, GPIO 3 = SCL +Pull-ups hold lines HIGH + + + +Common I2C Addresses (7-bit) + +0x27 +LCD + +0x3F +LCD Alt + +0x48 +Sensor + +0x50 +EEPROM + + + +I2C Transaction Flow + +START +--> + +Address +--> + +ACK +--> + +Data +--> + +ACK +--> + +STOP + +Master sends START, then 7-bit address + R/W bit +Slave responds with ACK, then data bytes follow + + + + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG03.svg b/WEEK07/slides/WEEK07-IMG03.svg new file mode 100644 index 0000000..d69b142 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG03.svg @@ -0,0 +1,66 @@ + + + + +C Structs & typedef +Grouping Related Data in C + + + +Struct Definition + +typedef struct { +i2c_hw_t *hw; +bool restart_on_next; +} i2c_inst_t; +typedef creates an alias +so we can write: i2c_inst_t var; +instead of: struct { ... } var; + + + +Memory Layout +i2c_inst_t at 0x2000062C + + +Offset 0x00 +hw += 0x40098000 +i2c_hw_t* (4 bytes) + + +Offset 0x04 +restart_on_next += 0x00 (false) +bool (1 byte) + +Total struct size: 8 bytes +hw points to I2C1 registers + + + +Forward Declaration + +struct i2c_inst; +// tells compiler: this type exists, define later + + + +Why Structs Matter in RE +GDB shows raw memory -- you must recognize struct layouts +x/2wx 0x2000062c shows: 0x40098000 0x00000000 + + + + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG04.svg b/WEEK07/slides/WEEK07-IMG04.svg new file mode 100644 index 0000000..927fd8c --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG04.svg @@ -0,0 +1,80 @@ + + + + +Pico SDK Macro Chain +From I2C_PORT to Hardware Registers + + + +Macro Expansion Chain + + + +I2C_PORT +#define I2C_PORT i2c1 + + +--> + + + +i2c1 +#define i2c1 (&i2c1_inst) + + +--> + + + +&i2c1_inst +Address of global struct + + +Struct Contents at 0x2000062C + +i2c_inst_t i2c1_inst = { +.hw = (i2c_hw_t *)0x40098000, +.restart_on_next = false +}; + + +Hardware Register Access + + +i2c1_inst.hw +--> +i2c1_hw +--> +(i2c_hw_t*)0x40098000 + +I2C1_BASE = 0x40098000 +I2C0_BASE = 0x40090000 +Direct memory-mapped I/O to RP2350 peripheral + + + +FULL CHAIN: +I2C_PORT +--> +i2c1 +--> +&i2c1_inst +--> +0x40098000 +Macro --> Macro --> Struct pointer --> HW register base + + + + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG05.svg b/WEEK07/slides/WEEK07-IMG05.svg new file mode 100644 index 0000000..de2e153 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG05.svg @@ -0,0 +1,53 @@ + + + + +Source Code +0x0017_constants.c + + + + + +//--- Defines and Constants --- +#define FAV_NUM 42 +#define I2C_PORT i2c1 +#define I2C_SDA_PIN 2 +#define I2C_SCL_PIN 3 +const int OTHER_FAV_NUM = 1337; + +//--- Main Loop --- +lcd_set_cursor(0, 0); +lcd_puts("Reverse"); +lcd_set_cursor(1, 0); +lcd_puts("Engineering"); + +//--- Serial Output Loop --- +printf("FAV_NUM: %d\r\n", FAV_NUM); +printf("OTHER_FAV_NUM: %d\r\n", OTHER_FAV_NUM); + + + +LCD Output +Line 0: "Reverse" +Line 1: "Engineering" + + +Serial Output +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 + + + + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG06.svg b/WEEK07/slides/WEEK07-IMG06.svg new file mode 100644 index 0000000..738b366 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG06.svg @@ -0,0 +1,65 @@ + + + + +GDB Analysis +Disassembly of main() at 0x10000234 + + + +Key Instructions from x/54i 0x10000234 + + +push {r3, lr} +// save return addr +bl stdio_init_all +// init serial +ldr r1, [pc, #104] +// r1 = 100000 (baud) +ldr r0, [pc, #104] +// r0 = &i2c1_inst +bl i2c_init +// init I2C at 100kHz +movs r0, #2 +// GPIO 2 (SDA) +bl gpio_set_function +// set pin to I2C +movs r1, #39 +// 0x27 = LCD addr +bl lcd_i2c_init +// init LCD device + +b.n 0x1000028e +// infinite loop start +... +AAPCS: r0-r3 = first 4 args, r0 = return value + + + +Literal Pool at 0x100002A4 + + +0x000186A0 +I2C baudrate (100000) +0x2000062C +&i2c1_inst struct in RAM +0x10003EE8 +"Reverse" string in flash +0x10003EF0 +"Engineering" string in flash +0x10003EFC +"FAV_NUM: %d\r\n" +0x10003F0C +"OTHER_FAV_NUM: %d\r\n" + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG07.svg b/WEEK07/slides/WEEK07-IMG07.svg new file mode 100644 index 0000000..15a1e54 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG07.svg @@ -0,0 +1,71 @@ + + + + +Instruction Encoding +movs (16-bit Thumb) vs movw (32-bit Thumb-2) + + + +movs r1, #42 (FAV_NUM) +At address 0x1000028E + + +Bytes: 2A 21 + +2A = immediate value (42) +21 = opcode (movs r1) +16-bit Thumb instruction +Fits values 0-255 in 8 bits +File offset: 0x28E + + + +movw r1, #1337 (OTHER_FAV) +At address 0x10000296 + + +Bytes: 40 F2 39 51 + +40 F2 = opcode (first halfword) +39 = imm8 (lower 8 bits) +51 = dest reg + upper imm +32-bit Thumb-2 instruction +File offset: 0x296 + + + +movw Byte Layout (40 F2 39 51) + + +40 F2 +Opcode + upper imm + + +39 +imm8 (lower 8 bits) + + +51 +Dest reg (r1) + bits + +imm16 = 0x539 += 1337 decimal + + + +Why movw instead of movs? +1337 > 255 -- does not fit in 8-bit movs immediate +movw encodes 0-65535 in 32-bit instruction + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG08.svg b/WEEK07/slides/WEEK07-IMG08.svg new file mode 100644 index 0000000..9f8ea92 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG08.svg @@ -0,0 +1,68 @@ + + + + +I2C Struct in Memory +Examining i2c1_inst at 0x2000062C + + + +GDB Memory Dump + +x/2wx 0x2000062c: +0x40098000 +0x00000000 + + + +i2c_inst_t Struct Layout + + + +Offset 0x00 | 4 bytes +i2c_hw_t *hw += 0x40098000 + + +--> + + + +I2C1 HW Registers +Base: 0x40098000 (MMIO) + + + +Offset 0x04 | 1 byte +bool restart_on_next += false + +I2C0 base = 0x40090000 +I2C1 base = 0x40098000 + + + +String Literals in Flash (.rodata) + + +x/s 0x10003ee8: +"Reverse" + + +x/s 0x10003ef0: +"Engineering" + +Stored consecutively in .rodata (flash) +These addresses are targets for patching + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG09.svg b/WEEK07/slides/WEEK07-IMG09.svg new file mode 100644 index 0000000..5934306 --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG09.svg @@ -0,0 +1,63 @@ + + + + +Hacking the Binary +Patching LCD Text: "Reverse" --> "Exploit" + + + +File Offset Formula +file_offset = address - 0x10000000 +Binary loaded at 0x10000000 + + + +Hack: Change LCD String +Address 0x10003EE8 --> File offset 0x3EE8 + +Original: + +52 65 76 65 72 73 65 00 +"Reverse" + +Patched: + +45 78 70 6C 6F 69 74 00 +"Exploit" + +Same length (7 chars) -- null terminator stays + + + +Flash the Hacked Binary + + +python uf2conv.py build\patched.bin + +1. +Save patched .bin file +2. +Convert to .uf2 format +3. +Hold BOOTSEL, plug in Pico +4. +Drag hacked.uf2 to drive + + + +LCD now shows: "Exploit" +instead of "Reverse" +No source code needed! + \ No newline at end of file diff --git a/WEEK07/slides/WEEK07-IMG10.svg b/WEEK07/slides/WEEK07-IMG10.svg new file mode 100644 index 0000000..3fda6dc --- /dev/null +++ b/WEEK07/slides/WEEK07-IMG10.svg @@ -0,0 +1,88 @@ + + + + +I2C & Macro Exploitation +Constants, I2C, Structs, and Hacking + + + +Key Concepts + +#define +Text replacement, no memory +const +Variable in .rodata (maybe) +I2C +Two-wire: SDA + SCL +struct +Groups related data fields +typedef +Creates type alias +AAPCS +r0-r3 args, r0 return +movs +16-bit, imm 0-255 +movw +32-bit, imm 0-65535 +Literal Pool +Large consts after code + + + +Key Addresses + +0x10000234 +main() entry +0x1000028E +FAV_NUM (movs) +0x10000296 +OTHER_FAV_NUM (movw) +0x10003EE8 +"Reverse" string +0x10003EF0 +"Engineering" string +0x40098000 +I2C1 HW base +0x2000062C +i2c1_inst struct + +file_offset = addr - 0x10000000 +String patches must be same length + + + +Macro Chain +I2C_PORT +--> +i2c1 +--> +&i2c1_inst +--> +0x40098000 + + + +Binary Hack Result +LCD: "Reverse" --> +"Exploit" +Patched at 0x3EE8 +Compiler may optimize const same as #define + + + +TAKEAWAY: +const is a source-level concept. +In binary, everything can change! + \ No newline at end of file diff --git a/WEEK09/WEEK09-SLIDES.pdf b/WEEK09/WEEK09-SLIDES.pdf new file mode 100644 index 0000000..130da37 Binary files /dev/null and b/WEEK09/WEEK09-SLIDES.pdf differ diff --git a/WEEK09/WEEK09.md b/WEEK09/WEEK09.md new file mode 100644 index 0000000..ff9d82f --- /dev/null +++ b/WEEK09/WEEK09.md @@ -0,0 +1,1205 @@ +# Week 9: Operators in Embedded Systems: Debugging and Hacking Operators w/ DHT11 Temperature & Humidity Sensor Single-Wire Protocol Basics. + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Understand all six types of C operators (arithmetic, increment, relational, logical, bitwise, assignment) +- Know how the DHT11 temperature and humidity sensor communicates with the Pico 2 +- Understand how post-increment operators affect variable values +- Navigate to the Reset_Handler and main function in stripped binaries +- Identify function arguments by analyzing register values in Ghidra +- Understand IEEE-754 floating-point representation +- Hack floating-point constants to manipulate sensor readings + +--- + +## Part 1: Understanding C Operators + +### What Are Operators? + +**Operators** are symbols that tell the compiler to perform specific mathematical, logical, or data manipulation operations. Think of them as the "verbs" of programming - they describe actions to perform on data. + +### The Six Types of Operators + +| Type | Example | What It Does | +| -------------- | -------------------- | ----------------------------------- | +| **Arithmetic** | `x * y` | Math operations (+, -, *, /, %) | +| **Increment** | `x++` or `++x` | Increase/decrease by 1 | +| **Relational** | `x > y` | Compare values (returns true/false) | +| **Logical** | `(x > y) && (y > x)` | Combine conditions (AND, OR, NOT) | +| **Bitwise** | `x << 1` | Manipulate individual bits | +| **Assignment** | `x += 5` | Assign and modify values | + +--- + +## Part 2: Arithmetic Operators + +### Basic Math in C + +Arithmetic operators perform mathematical calculations: + +```c +int x = 5; +int y = 10; +int result = x * y; // result = 50 +``` + +``` ++-----------------------------------------------------------------+ +| Arithmetic Operators | +| | +| Operator Example Result Description | +| ------------------------------------------------------------- | +| + 5 + 10 15 Addition | +| - 10 - 5 5 Subtraction | +| * 5 * 10 50 Multiplication | +| / 10 / 5 2 Division | +| % 10 % 3 1 Modulus (remainder) | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 3: Increment and Decrement Operators + +### Pre-Increment vs Post-Increment + +This is where many beginners get confused! There are TWO ways to increment: + +```c +int x = 5; +int a = x++; // Post-increment: a = 5, then x becomes 6 +int b = ++x; // Pre-increment: x becomes 7, then b = 7 +``` + +**The Key Difference:** + +| Type | Syntax | When Value Changes | Example Result | +| ------------------ | ------ | --------------------- | -------------------- | +| **Post-increment** | `x++` | AFTER the expression | `a = x++` -> a=5, x=6 | +| **Pre-increment** | `++x` | BEFORE the expression | `b = ++x` -> x=7, b=7 | + +``` ++-----------------------------------------------------------------+ +| Post-Increment (x++) | +| | +| int x = 5; | +| int result = x++; | +| | +| Step 1: result = x (result gets 5) | +| Step 2: x = x + 1 (x becomes 6) | +| | +| Final: result = 5, x = 6 | +| | +| Think of it as: "Use first, THEN increment" | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 4: Relational Operators + +### Comparing Values + +Relational operators compare two values and return `true` (1) or `false` (0): + +```c +int x = 6; +int y = 10; +bool result = (x > y); // false, because 6 is NOT greater than 10 +``` + +``` ++-----------------------------------------------------------------+ +| Relational Operators | +| | +| Operator Example Result Description | +| ------------------------------------------------------------- | +| > 6 > 10 false Greater than | +| < 6 < 10 true Less than | +| >= 6 >= 6 true Greater than or equal | +| <= 6 <= 10 true Less than or equal | +| == 6 == 10 false Equal to | +| != 6 != 10 true Not equal to | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 5: Logical Operators + +### Combining Conditions + +Logical operators combine multiple conditions: + +```c +int x = 6; +int y = 10; +bool result = (x > y) && (y > x); // false AND true = false +``` + +``` ++-----------------------------------------------------------------+ +| Logical Operators | +| | +| Operator Name Example Result | +| ------------------------------------------------------------- | +| && AND true && true true | +| && AND true && false false | +| || OR true || false true | +| || OR false || false false | +| ! NOT !true false | +| | +| Truth Table for AND (&&): | +| +-------+-------+--------+ | +| | A | B | A && B | | +| +-------+-------+--------+ | +| | false | false | false | | +| | false | true | false | | +| | true | false | false | | +| | true | true | true | | +| +-------+-------+--------+ | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 6: Bitwise Operators + +### Manipulating Individual Bits + +Bitwise operators work on the binary representation of numbers: + +```c +int x = 6; // Binary: 0b00000110 +int result = x << 1; // Shift left by 1: 0b00001100 = 12 +``` + +``` ++-----------------------------------------------------------------+ +| Bitwise Left Shift (<<) | +| | +| x = 6 = 0b00000110 | +| | +| x << 1 means "shift all bits LEFT by 1 position" | +| | +| Before: 0 0 0 0 0 1 1 0 (6) | +| ? ? ? ? ? ? ? ? | +| After: 0 0 0 0 1 1 0 0 (12) | +| | +| Each left shift DOUBLES the value! | +| 6 << 1 = 12 (same as 6 * 2) | +| | ++-----------------------------------------------------------------+ +``` + +**Common Bitwise Operators:** + +| Operator | Name | Example | Result | +| -------- | ----------- | -------- | ------------------ | +| `<<` | Left shift | `6 << 1` | 12 (multiply by 2) | +| `>>` | Right shift | `6 >> 1` | 3 (divide by 2) | +| `&` | AND | `6 & 3` | 2 (bits in common) | +| `\|` | OR | `6 \| 3` | 7 (all set bits) | +| `^` | XOR | `6 ^ 3` | 5 (different bits) | +| `~` | NOT | `~6` | Inverts all bits | + +--- + +## Part 7: Assignment Operators + +### Shorthand for Math + Assign + +Assignment operators combine math with assignment: + +```c +int x = 6; +x += 5; // Same as: x = x + 5; Result: x = 11 +``` + +``` ++-----------------------------------------------------------------+ +| Compound Assignment Operators | +| | +| Operator Example Equivalent To Result (if x=6) | +| ------------------------------------------------------------- | +| += x += 5 x = x + 5 x = 11 | +| -= x -= 2 x = x - 2 x = 4 | +| *= x *= 3 x = x * 3 x = 18 | +| /= x /= 2 x = x / 2 x = 3 | +| %= x %= 4 x = x % 4 x = 2 | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 8: Understanding the DHT11 Sensor + +### What is the DHT11? + +The **DHT11** is a low-cost digital temperature and humidity sensor. It uses a single wire for communication (plus power and ground). + +``` ++-----------------------------------------------------------------+ +| DHT11 Pinout | +| | +| +-------------+ | +| | DHT11 | | +| | +-----+ | | +| | | | | | +| | | | | | +| | +-----+ | | +| | 1 2 3 4 | | +| +--+--+--+--+-+ | +| | | | | | +| VCC DATA NC GND | +| | +| Pin 1: VCC (3.3V or 5V) | +| Pin 2: DATA (connect to GPIO) | +| Pin 3: Not Connected | +| Pin 4: GND (Ground) | +| | ++-----------------------------------------------------------------+ +``` + +### DHT11 Specifications + +| Parameter | Range | Accuracy | +| --------------- | ------------ | -------- | +| **Humidity** | 20% - 90% RH | ?5% RH | +| **Temperature** | 0 deg C - 50 deg C | ?2 deg C | + +### How DHT11 Communication Works + +The DHT11 uses a custom one-wire protocol: + +1. **Host sends start signal** - Pull data line LOW for 18ms +2. **DHT11 responds** - Pulls line LOW for 80 us, then HIGH for 80 us +3. **Data transmission** - 40 bits sent (8 humidity int, 8 humidity decimal, 8 temp int, 8 temp decimal, 8 checksum) + +--- + +## Part 9: Understanding Pointers (Quick Review) + +### The & Operator (Address-Of) + +When you see `&variable`, it means "the memory address of variable": + +```c +float hum, temp; + +// Pass ADDRESSES to the function so it can modify our variables +if (dht11_read(&hum, &temp)) { + printf("Humidity: %.1f%%, Temperature: %.1f?C\n", hum, temp); +} +``` + +``` ++-----------------------------------------------------------------+ +| Passing by Reference | +| | +| Stack Memory | +| +----------------------------+ | +| | Address 0x20000008: hum |?--- &hum (passed to function) | +| | Value: 51.0 | | +| +----------------------------+ | +| | Address 0x2000000C: temp |?--- &temp (passed to function) | +| | Value: 23.8 | | +| +----------------------------+ | +| | +| dht11_read() receives the ADDRESSES, so it can write | +| new values directly into hum and temp! | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 10: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. Ghidra installed (for static analysis) +4. Python installed (for UF2 conversion) +5. A serial monitor (PuTTY, minicom, or screen) +6. A DHT11 temperature and humidity sensor +7. The sample project: `0x001a_operators` + +### Hardware Setup + +Connect your DHT11 like this: + +| DHT11 Pin | Pico 2 Pin | +| --------- | ---------- | +| VCC | 3.3V | +| DATA | GPIO 4 | +| GND | GND | + +``` ++-----------------------------------------------------------------+ +| DHT11 Wiring | +| | +| Pico 2 DHT11 Sensor | +| +----------+ +----------+ | +| | | | | | +| | GPIO 4 |---------- DATA ------?| DATA | | +| | | | | | +| | 3.3V |---------- VCC -------?| VCC | | +| | | | | | +| | GND |---------- GND -------?| GND | | +| | | | | | +| +----------+ +----------+ | +| | +| Note: Some DHT11 modules have a built-in pull-up resistor. | +| If yours doesn't, add a 10K resistor between DATA and VCC. | +| | ++-----------------------------------------------------------------+ +``` + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x001a_operators/ +| +-- build/ +| | +-- 0x001a_operators.uf2 +| | +-- 0x001a_operators.bin +| +-- main/ +| | +-- 0x001a_operators.c +| +-- dht11.h ++-- uf2conv.py +``` + +--- + +## Part 11: Hands-On Tutorial - The Operators Code + +### Step 1: Review the Source Code + +Let's examine the operators code: + +**File: `0x001a_operators.c`** + +```c +#include +#include "pico/stdlib.h" +#include "dht11.h" + +int main(void) { + stdio_init_all(); + + dht11_init(4); + + int x = 5; + int y = 10; + int arithmetic_operator = (x * y); + int increment_operator = x++; + bool relational_operator = (x > y); + bool logical_operator = (x > y) && (y > x); + int bitwise_operator = (x<<1); // x is now 6 because of x++ or 0b00000110 and (x<<1) is 0b00001100 or 12 + int assignment_operator = (x += 5); + + while (true) { + printf("arithmetic_operator: %d\r\n", arithmetic_operator); + printf("increment_operator: %d\r\n", increment_operator); + printf("relational_operator: %d\r\n", relational_operator); + printf("logical_operator: %d\r\n", logical_operator); + printf("bitwise_operator: %d\r\n", bitwise_operator); + printf("assignment_operator: %d\r\n", assignment_operator); + + float hum, temp; + if (dht11_read(&hum, &temp)) { + printf("Humidity: %.1f%%, Temperature: %.1f?C\r\n", hum, temp); + } else { + printf("DHT11 read failed\r\n"); + } + + sleep_ms(2000); + } +} +``` + +### Step 2: Understand the Variable Flow + +Let's trace through what happens to `x`: + +``` ++-----------------------------------------------------------------+ +| Variable x Through the Program | +| | +| Line | x value | Result | +| ------------------+---------+--------------------------------- | +| int x = 5; | 5 | x initialized to 5 | +| x * y | 5 | arithmetic = 5 * 10 = 50 | +| x++ | 5->6 | increment = 5 (then x becomes 6)| +| x > y | 6 | relational = (6 > 10) = false | +| (x>y) && (y>x) | 6 | logical = false && true = false | +| x << 1 | 6 | bitwise = 6 << 1 = 12 | +| x += 5 | 6->11 | assignment = 6 + 5 = 11 | +| | ++-----------------------------------------------------------------+ +``` + +### Step 3: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x001a_operators.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 4: Verify It's Working + +Open your serial monitor (PuTTY at 115200 baud) and you should see: + +``` +arithmetic_operator: 50 +increment_operator: 5 +relational_operator: 0 +logical_operator: 0 +bitwise_operator: 12 +assignment_operator: 11 +Humidity: 51.0%, Temperature: 23.8 deg C +``` + +**Understanding the Output:** + +| Variable | Value | Explanation | +| ------------------- | ------ | --------------------------------------------- | +| arithmetic_operator | 50 | 5 * 10 = 50 | +| increment_operator | 5 | Post-increment returns value BEFORE increment | +| relational_operator | 0 | 6 > 10 is false (0) | +| logical_operator | 0 | false AND true = false (0) | +| bitwise_operator | 12 | 6 (0b0110) << 1 = 12 (0b1100) | +| assignment_operator | 11 | 6 + 5 = 11 | +| Humidity | 51.0% | Real reading from DHT11 | +| Temperature | 23.8 deg C | Real reading from DHT11 | + +--- + +## Part 12: Debugging with GDB + +### Step 5: Start OpenOCD (Terminal 1) + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe. + +### Step 6: Start GDB (Terminal 2) + +Open a **new terminal** and launch GDB with the binary: + +```cmd +arm-none-eabi-gdb build\0x001a_operators.elf +``` + +### Step 7: Connect to the Remote Target + +Inside GDB, type: + +``` +target extended-remote :3333 +``` + +This connects GDB to OpenOCD. + +### Step 8: Halt the Running Binary + +``` +monitor reset halt +``` + +This stops the Pico 2 so we can examine its state. + +### Step 9: Examine Main Function + +Let's examine the main function. Disassemble from the entry point: + +``` +x/60i 0x10000234 +``` + +You should see the operator calculations and function calls: + +``` + 0x10000234
: push {r4, r5, r6, r7, lr} + 0x10000236 : sub sp, #20 + 0x10000238 : bl 0x10003384 + 0x1000023c : movs r0, #4 + 0x1000023e : bl 0x100002d4 +... +``` + +### Step 10: Set a Breakpoint at Main + +``` +b *0x10000234 +c +``` + +### Step 11: Find the Operator Calculations + +The compiler likely optimized many of these calculations at compile time. Look for immediate values: + +``` +x/32i 0x10000240 +``` + +You may see values like: +- `#0x32` (50) for arithmetic_operator +- `#0x5` (5) for increment_operator +- `#0x0` (0) for relational and logical operators +- `#0xc` (12) for bitwise_operator +- `#0xb` (11) for assignment_operator + +### Step 12: Examine Printf Arguments + +Set a breakpoint before the first printf and examine registers: + +```gdb +b *0x10000262 +c +i r r0 r1 +``` + +You should see: +- `r0` = address of format string +- `r1` = value to print + +### Step 13: Examine the Format Strings + +```gdb +x/s 0x10003978 +``` + +Find the format strings and value for print: +```gdb +(gdb) x/s 0x10003978 +0x10003978: "Humidity: %.1f%%, Temperature: %.1fA°C\r\n" +(gdb) x/x 0x4037cccc +0x4037cccc: 0x00 +(gdb) x/x $r1 +0x4037cccc: 0x00 +... +``` + +### Step 14: Examine DHT11 Function Call + +Find where dht11_read is called: + +```gdb +(gdb) x/3i 0x1000029f +``` + +You'll see stack addresses being passed as arguments: +``` + 0x1000029f : add r1, sp, #12 + 0x100002a1 : add r0, sp, #8 + 0x100002a3 : bl 0x100002f4 +``` + +### Step 15: Watch the Float Values + +After dht11_read returns, examine the float values on the stack: + +```gdb +(gdb) x/2fw $sp+8 +0x20081fe0: 62 23.7999992 +``` + +This shows the humidity and temperature as floats. + +### Step 16: Step Through the Loop + +Continue execution and watch the values: + +```gdb +c +``` + +The program will loop, printing values to serial. + +--- + +## Part 13: Setting Up Ghidra for Analysis + +### Step 17: Start Ghidra + +Open a terminal and type: + +```cmd +ghidraRun +``` + +### Step 18: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x001a_operators` +5. Click **Finish** + +### Step 19: Import the Binary + +1. Open your file explorer +2. Navigate to the `0x001a_operators/build/` folder +3. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 20: Configure the Binary Format + +**Click the three dots (...) next to "Language" and:** +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` +3. Click **OK** + +### Step 21: Analyze the Binary + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete. + +--- + +## Part 14: Finding the Reset_Handler + +### Step 22: Understand the Vector Table + +In ARM Cortex-M, the **vector table** is at the base of flash (0x10000000). The second entry (offset 4) contains the Reset_Handler address. + +``` ++-----------------------------------------------------------------+ +| ARM Vector Table at 0x10000000 | +| | +| Offset Contents Description | +| ------------------------------------------------------------- | +| 0x00 Initial SP value Stack pointer at reset | +| 0x04 Reset_Handler addr First code to execute | +| 0x08 NMI_Handler addr Non-maskable interrupt | +| 0x0C HardFault_Handler Hard fault handler | +| ... | +| | ++-----------------------------------------------------------------+ +``` + +### Step 23: Read the Reset_Handler Address + +1. Press `G` (Go to address) and type `10000004` +2. You'll see bytes like `5d 01 00 10` (your exact bytes may vary) + +**Important:** This is **little-endian**, so we need to reverse the byte order! + +``` ++-----------------------------------------------------------------+ +| Little-Endian Byte Order | +| | +| In memory: 5d 01 00 10 | +| Reversed: 10 00 01 5d | +| As hex: 0x1000015d | +| | +| But wait! ARM uses the THUMB bit! | +| The lowest bit indicates Thumb mode (always set for Cortex-M) | +| Real address: 0x1000015d - 1 = 0x1000015c | +| | ++-----------------------------------------------------------------+ +``` + +### Step 24: Navigate to Reset_Handler + +1. Press `G` and type `1000015c` (or your calculated address) +2. You might see undefined data - that's OK! + +### Step 25: Create the Reset_Handler Function + +If Ghidra didn't automatically recognize this as a function: + +1. Click on the address `0x1000015c` +2. Right-click and press `F` to create a function +3. Right-click -> **Edit Function Signature** +4. Change the name to `Reset_Handler` +5. Click **OK** + +### Step 26: Find Main from Reset_Handler + +The Reset_Handler typically calls three functions: + +``` ++-----------------------------------------------------------------+ +| Reset_Handler Flow (crt0.S) | +| | +| Reset_Handler: | +| 1. Call some_init() ?-- Initialize hardware | +| 2. Call main() ?-- THIS IS WHAT WE WANT! | +| 3. Call exit() ?-- Never returns | +| | +| The MIDDLE function is main! | +| | ++-----------------------------------------------------------------+ +``` + +Look at the end of Reset_Handler for three function calls. The middle one is `main`! + +### Step 27: Navigate to Main + +1. Double-click on the middle function call (should be around `0x10000234`) +2. Right-click -> **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +--- + +## Part 15: Resolving Functions in Ghidra + +### Step 28: Resolve stdio_init_all + +The first function call in main is `stdio_init_all`: + +1. Find the call at approximately `0x10000238` +2. Double-click to navigate to the function +3. Right-click -> **Edit Function Signature** +4. Change to: `bool stdio_init_all(void)` +5. Click **OK** + +### Step 29: Resolve dht11_init + +Look for a function call where `r0` is loaded with `0x4`: + +```assembly +movs r0, #0x4 ; GPIO pin 4 +bl FUN_xxxxx ; dht11_init +``` + +**How do we know it's dht11_init?** +- The argument `4` is the GPIO pin number +- We physically connected the DHT11 to GPIO 4! + +1. Right-click -> **Edit Function Signature** +2. Change to: `void dht11_init(uint pin)` +3. Click **OK** + +### Step 30: Resolve printf + +Look for repeated function calls with string addresses: + +1. Find a call like the one at `0x10000262` +2. Right-click -> **Edit Function Signature** +3. Change to: `int printf(char *format,...)` +4. Check the **Varargs** checkbox +5. Click **OK** + +### Step 31: Resolve sleep_ms + +Look for a function call where `r0` is loaded with `0x7d0` (2000 in decimal): + +```assembly +ldr r0, =0x7d0 ; 2000 milliseconds +bl FUN_xxxxx ; sleep_ms +``` + +1. Right-click -> **Edit Function Signature** +2. Change to: `void sleep_ms(uint ms)` +3. Click **OK** + +### Step 32: Resolve dht11_read + +This is trickier! Look for a function call with TWO address arguments: + +```assembly +add r1, sp, #0xc ; Address of temp on stack +add r0, sp, #0x8 ; Address of hum on stack +bl FUN_xxxxx ; dht11_read +``` + +**Understanding the stack offsets:** +- `sp + 0x8` = address of `hum` variable +- `sp + 0xc` = address of `temp` variable +- These are `float` pointers passed to the function + +1. Right-click -> **Edit Function Signature** +2. Change to: `bool dht11_read(float *humidity, float *temperature)` +3. Click **OK** + +### Step 33: Resolve puts + +Look for a function call after the `if` statement that takes a single string argument: + +```assembly +ldr r0, ="DHT11 read failed" +bl FUN_xxxxx ; puts +``` + +1. Right-click -> **Edit Function Signature** +2. Change to: `int puts(char *s)` +3. Click **OK** + +--- + +## Part 16: Understanding IEEE-754 Floating-Point + +### What is IEEE-754? + +IEEE-754 is the standard for representing decimal numbers in binary. A 32-bit float is divided into three parts: + +``` ++-----------------------------------------------------------------+ +| IEEE-754 Single Precision (32-bit) Float | +| | +| +-----+-------------+---------------------------------------+ | +| | S | Exponent | Mantissa (Fraction) | | +| | 1 | 8 bits | 23 bits | | +| +-----+-------------+---------------------------------------+ | +| bit bits bits | +| 31 30-23 22-0 | +| | +| Value = (-1)^S * (1 + Mantissa) * 2^(Exponent - 127) | +| | ++-----------------------------------------------------------------+ +``` + +### Example: Decoding 0x3dcccccc (0.1f) + +Let's decode the bytes `cc cc cc 3d`: + +1. **Reverse for little-endian:** `0x3dcccccc` +2. **Convert to binary:** `00111101 11001100 11001100 11001100` +3. **Extract fields:** + - Sign (bit 31): `0` (positive) + - Exponent (bits 30-23): `01111011` = 123 + - Mantissa (bits 22-0): `10011001100110011001100` + +4. **Calculate value:** + - Actual exponent: 123 - 127 = -4 + - Mantissa value: 1.6 (approximately) + - Final value: 1.6 * 2^(-4) ? 0.1 + +### Example: Encoding -1.0f as 0xbf800000 + +For the number -1.0: + +1. **Sign:** 1 (negative) +2. **Exponent:** 127 (for 2^0 = 1) +3. **Mantissa:** 0 (because value is exactly 1.0) + +Binary: `1 01111111 00000000000000000000000` +Hex: `0xbf800000` +Little-endian: `00 00 80 bf` + +### Python for Float Conversion + +```python +import struct + +# Decode bytes to float +bytes_data = bytes.fromhex('cdcccc3d') +value = struct.unpack(' **Bytes** +2. A new panel appears showing raw hex bytes + +### Step 38: Enable Editing + +Look for the pencil icon in the Bytes window toolbar and click it to enable editing mode. + +### Step 39: Hack the Scaling Constant + +Let's change the temperature scaling to add 25% more! + +1. Press `G` and go to address `1000042c` +2. Current bytes: `cd cc cc 3d` (0.1f in little-endian) +3. Change to: `00 00 a0 40` (5.0f in little-endian) + +This changes the multiplier from 0.1 to 5.0, which will dramatically increase the temperature reading! + +### Step 40: Verify the Change + +Use Python to verify what we changed: + +```python +import struct + +# Original value +original = struct.unpack('>> import struct +>>> +>>> # Original value +>>> original = struct.unpack('>> print(f"Original: {original}") # 0.1 +Original: 0.10000000149011612 +>>> +>>> # New value +>>> new = struct.unpack('>> print(f"New: {new}") # 5.0 +New: 5.0 +``` + +--- + +## Part 19: Exporting and Testing + +### Step 41: Export the Patched Binary + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. Navigate to your build directory +4. Name the file `0x001a_operators-h.bin` +5. Click **OK** + +### Step 42: Convert to UF2 Format + +Open a terminal and run: + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x001a_operators +python ..\uf2conv.py build\0x001a_operators-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 43: Flash and Test + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Open your serial monitor + +You should see dramatically increased temperature readings! + +``` +Humidity: 60.0%, Temperature: 63.0°C +arithmetic_operator: 50 +increment_operator: 5 +relational_operator: 0 +logical_operator: 0 +bitwise_operator: 12 +assignment_operator: 11 +``` + +--- + +## Part 20: Summary and Review + +### What We Accomplished + +1. **Learned all six C operator types** - Arithmetic, increment, relational, logical, bitwise, assignment +2. **Understood post-increment behavior** - `x++` returns value BEFORE incrementing +3. **Learned about the DHT11 sensor** - One-wire protocol for temperature/humidity +4. **Found Reset_Handler from vector table** - Offset 4 contains the address +5. **Identified functions by their arguments** - GPIO pin 4, sleep 2000ms, etc. +6. **Understood IEEE-754 floating-point** - How computers represent decimals +7. **Hacked floating-point constants** - Changed 0.1f to other values + +### The Hacking Workflow + +``` ++-----------------------------------------------------------------+ +| Binary Hacking Workflow | +| | +| 1. Analyze the binary in Ghidra | +| 2. Identify target values/instructions | +| 3. Calculate file offsets from memory addresses | +| 4. Determine replacement bytes | +| 5. Patch the binary (manual in hex editor) | +| 6. Export and convert to UF2 | +| 7. Flash and test | +| | ++-----------------------------------------------------------------+ +``` + +### Key Memory Addresses + +| Memory Address | File Offset | Description | +| -------------- | ----------- | ------------------------------- | +| `0x10000000` | `0x000` | Vector table start | +| `0x10000004` | `0x004` | Reset_Handler address | +| `0x10000234` | `0x234` | main() function (approximately) | +| `0x10000410` | `0x410` | Humidity vfma instruction | +| `0x10000414` | `0x414` | Temperature vfma instruction | +| `0x1000042C` | `0x42C` | 0.1f scaling constant | + +--- + +--- + +## Key Takeaways + +1. **Post-increment returns the OLD value** - `x++` gives you x, THEN adds 1 + +2. **Bitwise left shift multiplies by 2** - `x << 1` is the same as `x * 2` + +3. **Vector table points to Reset_Handler** - Offset 4 from flash base + +4. **Arguments go in r0-r3** - Follow them to identify functions + +5. **IEEE-754 is how floats are stored** - Sign, exponent, mantissa + +6. **File offset = Memory address - Base** - 0x10000410 -> offset 0x410 + +7. **Little-endian reverses byte order** - 0x3dcccccc stored as cc cc cc 3d + +8. **Incremental testing is essential** - Test each change before the next + +10. **Binary patching has real consequences** - Sensor spoofing can be dangerous! + +--- + +## Glossary + +| Term | Definition | +| ------------------ | --------------------------------------------------- | +| **Arithmetic Op** | Operators for math (+, -, *, /, %) | +| **Assignment Op** | Operators that assign and modify (+=, -=, etc.) | +| **Bitwise Op** | Operators on individual bits (<<, >>, &, \|, ^) | +| **DHT11** | Digital humidity and temperature sensor | +| **Exponent** | Power of 2 in IEEE-754 float representation | +| **IEEE-754** | Standard for floating-point number representation | +| **Increment Op** | Operators that add/subtract 1 (++, --) | +| **Little-Endian** | Byte order where least significant byte comes first | +| **Logical Op** | Operators combining conditions (&&, \|\|, !) | +| **Mantissa** | Fractional part of IEEE-754 float | +| **Post-Increment** | `x++` - returns value, then increments | +| **Pre-Increment** | `++x` - increments, then returns value | +| **Relational Op** | Operators comparing values (<, >, ==, !=) | +| **Reset_Handler** | First function executed after CPU reset | +| **Thumb Bit** | Lowest bit of ARM address indicating Thumb mode | +| **Vector Table** | Table of exception/interrupt handler addresses | +| **vfma.f32** | ARM floating-point fused multiply-add instruction | +| **vadd.f32** | ARM floating-point add instruction | + +--- + +## Additional Resources + +### IEEE-754 Float Quick Reference + +| Value | Hex Encoding | Bytes (LE) | +| ----- | ------------ | ----------- | +| 0.1 | 0x3dcccccd | cd cc cc 3d | +| 1.0 | 0x3f800000 | 00 00 80 3f | +| -1.0 | 0xbf800000 | 00 00 80 bf | +| 2.0 | 0x40000000 | 00 00 00 40 | +| -2.0 | 0xc0000000 | 00 00 00 c0 | +| 5.0 | 0x40a00000 | 00 00 a0 40 | +| 10.0 | 0x41200000 | 00 00 20 41 | + +### ARM Floating-Point Instructions + +| Instruction | Description | +| --------------------- | ---------------------------------------- | +| `vfma.f32 Sd, Sn, Sm` | Sd = Sd + (Sn * Sm) (fused multiply-add) | +| `vadd.f32 Sd, Sn, Sm` | Sd = Sn + Sm | +| `vsub.f32 Sd, Sn, Sm` | Sd = Sn - Sm | +| `vmul.f32 Sd, Sn, Sm` | Sd = Sn * Sm | +| `vldr.f32 Sd, [addr]` | Load float from memory | +| `vstr.f32 Sd, [addr]` | Store float to memory | + +--- + +## Real-World Implications + +### Why This Matters + +Imagine a scenario where temperature sensors control critical systems: + +- **Industrial processes** - Chemical reactions that must stay within temperature ranges +- **Medical equipment** - Refrigerators storing vaccines or organs +- **Nuclear facilities** - Cooling systems for reactors +- **HVAC systems** - Climate control in sensitive environments + +By manipulating sensor readings, an attacker could: +- Cause equipment to overheat while displaying normal temperatures +- Trigger false alarms +- Bypass safety interlocks +- Cause physical damage or safety hazards + +### Defensive Measures + +1. **Redundant sensors** - Multiple sensors with consistency checks +2. **Physical security** - Prevent access to programming interfaces +3. **Anomaly detection** - Alert on sudden reading changes + +--- + +**Remember:** The techniques you learned today can be used for good (security research, debugging) or bad (sabotage, fraud). Always use your skills ethically and legally. Understanding how attacks work helps us build more secure systems! + +Happy hacking! diff --git a/WEEK09/slides/WEEK09-IMG00.svg b/WEEK09/slides/WEEK09-IMG00.svg new file mode 100644 index 0000000..3aadd8e --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 09 + + +Operators in Embedded Systems: +Debugging and Hacking Operators +w/ DHT11 Sensor Single-Wire Protocol + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK09/slides/WEEK09-IMG01.svg b/WEEK09/slides/WEEK09-IMG01.svg new file mode 100644 index 0000000..b431706 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG01.svg @@ -0,0 +1,74 @@ + + + + +C Operators Overview +Six Types of Operators in C + + + +Arithmetic + ++ - * / % +Math operations +5 * 10 = 50 + + +Increment + +x++ ++x x-- +Add/subtract by 1 +x++ returns old val + + +Relational + +> < >= <= == != +Compare values +(6 > 10) = false + + + +Logical + +&& || ! +Combine conditions +AND, OR, NOT + + +Bitwise + +<< >> & | ^ ~ +Manipulate bits +6 << 1 = 12 + + +Assignment + ++= -= *= /= +Assign and modify +x += 5 (x=x+5) + + + +This Week's Program +0x001a_operators.c demonstrates all 6 types +DHT11 temperature/humidity sensor + operator calculations + + + +KEY: +Compiler pre-computes constant expressions +In the binary, most operators become immediate values + \ No newline at end of file diff --git a/WEEK09/slides/WEEK09-IMG02.svg b/WEEK09/slides/WEEK09-IMG02.svg new file mode 100644 index 0000000..3ba9f91 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG02.svg @@ -0,0 +1,75 @@ + + + + +Arithmetic & Increment +Math Operations and Post/Pre Increment + + + +Arithmetic Operators + ++ +5 + 10 = 15 +Addition +- +10 - 5 = 5 +Subtraction +* +5 * 10 = 50 +Multiplication +/ +10 / 5 = 2 +Division +% +10 % 3 = 1 +Modulus + + + +Post vs Pre Increment + + +Post: x++ +Use value THEN increment +a = x++ --> a=5, x=6 + + +Pre: ++x +Increment THEN use value +b = ++x --> x=7, b=7 + + + +Post-Increment Step by Step + + +int x = 5; +int result = x++; + +Step 1: result = x +result gets 5 +Step 2: x = x + 1 +x becomes 6 + +Final: result = 5 +x = 6 +"Use first, THEN increment" + + + +In our code: +int increment_operator = x++; +x was 5, so increment_operator = 5, then x becomes 6 + \ No newline at end of file diff --git a/WEEK09/slides/WEEK09-IMG03.svg b/WEEK09/slides/WEEK09-IMG03.svg new file mode 100644 index 0000000..1244936 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG03.svg @@ -0,0 +1,95 @@ + + + + +Relational & Logical +Comparing Values and Combining Conditions + + + +Relational Operators +Compare two values --> true (1) or false (0) + +> +6 > 10 +false +Greater than +< +6 < 10 +true +Less than +>= +6 >= 6 +true +Greater/equal +<= +6 <= 10 +true +Less or equal +== +6 == 10 +false +Equal to +!= +6 != 10 +true +Not equal + + + +Logical Operators +Combine conditions into one result + +&& +AND -- both must be true +|| +OR -- at least one true +! +NOT -- inverts result + + +AND Truth Table + +A +B +A && B +false +false +false +false +true +false +true +false +false +true +true +true + + + +In Our Code (x=6, y=10) + +bool relational = (x > y); +(6 > 10) = false = 0 +bool logical = (x>y) && (y>x); +false && true = false = 0 + + + +In the binary: +Both compile to immediate #0 +Compiler pre-computes: constants are known at compile time +Result 0 = false, Result 1 = true + \ No newline at end of file diff --git a/WEEK09/slides/WEEK09-IMG04.svg b/WEEK09/slides/WEEK09-IMG04.svg new file mode 100644 index 0000000..02cb827 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG04.svg @@ -0,0 +1,89 @@ + + + + +Bitwise & Assignment +Bit Manipulation and Compound Assignment + + + +Bitwise Operators + +<< +6 << 1 = 12 +Left shift +>> +6 >> 1 = 3 +Right shift +& +6 & 3 = 2 +AND +| +6 | 3 = 7 +OR +^ +6 ^ 3 = 5 +XOR +~ +~6 +NOT (invert) + + +Left shift = multiply by 2 + +0 0 0 0 0 1 1 0 += 6 +0 0 0 0 1 1 0 0 += 12 + + + +Assignment Operators +Shorthand for math + assign + ++= +x += 5 +x = x + 5 +-= +x -= 2 +x = x - 2 +*= +x *= 3 +x = x * 3 +/= +x /= 2 +x = x / 2 +%= +x %= 4 +x = x % 4 + + +In our code (x=6 after x++): + +x += 5 --> 6 + 5 = 11 + + + +In Our Code (x=6, y=10) + +int bitwise = (x<<1); +6 << 1 = 12 (0b0110 --> 0b1100) + + + +Expected Output +bitwise_operator: 12 +assignment_operator: 11 +Both pre-computed by compiler as immediates + \ No newline at end of file diff --git a/WEEK09/slides/WEEK09-IMG05.svg b/WEEK09/slides/WEEK09-IMG05.svg new file mode 100644 index 0000000..4975302 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG05.svg @@ -0,0 +1,72 @@ + + + + +DHT11 Sensor +Single-Wire Temperature and Humidity + + + +DHT11 Pinout + + +DHT11 +1:VCC 2:DATA 3:NC 4:GND + +Humidity: 20-90% RH (+/-5%) +Temp: 0-50C (+/-2C) +Protocol: custom one-wire + + + +Wiring to Pico 2 + + +Pico + + +DHT11 + + + + +GPIO 4 = DATA +3.3V = VCC +GND = GND + + + +1. Host pulls LOW 18ms +2. DHT11 responds, sends 40 bits + + + +Source Code: 0x001a_operators.c + +int x = 5, y = 10; +int arithmetic = (x * y); +// 50 +int increment = x++; +// 5 (post) +bool relational = (x > y); +// false +bool logical = (x>y)&&(y>x); +// false +int bitwise = (x<<1); +// 12 +int assignment = (x += 5); +// 11 +float hum, temp; +dht11_read(&hum, &temp); + \ No newline at end of file diff --git a/WEEK09/slides/WEEK09-IMG06.svg b/WEEK09/slides/WEEK09-IMG06.svg new file mode 100644 index 0000000..ef11927 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG06.svg @@ -0,0 +1,75 @@ + + + + +Variable Flow +Tracing x Through Every Operator + + + +Tracing x Step-by-Step + + +Line +x +Result + + + +int x = 5, y = 10; +5 +x initialized to 5 + + +int arithmetic = (x * y); +5 +arithmetic = 50 + + +int increment = x++; +5-->6 +increment = 5 +use THEN increment + + +bool relational = (x > y); +6 +relational = false +6 > 10 is false + + +bool logical = (x>y)&&(y>x); +6 +logical = false +false AND true = false + + +int bitwise = (x<<1); +6 +bitwise = 12 +0b0110 << 1 = 0b1100 + + +int assignment = (x += 5); +6-->11 +assignment = 11 +6 + 5 = 11 + + + +DHT11 Output +Humidity: 51.0% +Temperature: 23.8C +dht11_read(&hum, &temp) -- passes addresses so function can write values + diff --git a/WEEK09/slides/WEEK09-IMG07.svg b/WEEK09/slides/WEEK09-IMG07.svg new file mode 100644 index 0000000..1e5d297 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG07.svg @@ -0,0 +1,77 @@ + + + + +Vector Table +Finding Reset_Handler and main() + + + +ARM Vector Table +Base address: 0x10000000 + +Offset +Contents +Purpose + + +0x00 +Initial SP +Stack ptr + +0x04 +Reset_Handler +Entry point + +0x08 +NMI_Handler +NMI + +0x0C +HardFault +Fault + + + +Decoding the Address + +At 0x10000004: +Bytes: 5d 01 00 10 + +Step 1: Reverse (little-endian) +10 00 01 5d = 0x1000015d + +Step 2: Remove Thumb bit +0x1000015d - 1 = 0x1000015c + + + +Reset_Handler --> main() + +Reset_Handler at 0x1000015c calls 3 functions: + + +Call 1: some_init() +Hardware initialization + +Call 2: main() +THIS IS WHAT WE WANT +Address: 0x10000234 + +Call 3: exit() +Never returns + +The MIDDLE function call is always main() +Navigate to 0x10000234 in Ghidra to find it + diff --git a/WEEK09/slides/WEEK09-IMG08.svg b/WEEK09/slides/WEEK09-IMG08.svg new file mode 100644 index 0000000..f520db0 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG08.svg @@ -0,0 +1,81 @@ + + + + +IEEE-754 Floats +How Computers Store Decimal Numbers + + + +32-bit Float Structure + + + +S +1 bit + + +Exponent +8 bits + + +Mantissa (Fraction) +23 bits + +Value = (-1)^S x (1 + Mantissa) x 2^(Exponent - 127) + + + +Example: Decoding 0.1f + +Little-endian bytes: +cd cc cc 3d + +Reversed (big-endian): +0x3dcccccd + +Sign: 0 +Exp: 01111011 = 123 +Mantissa: 1001100... + +Exp - 127 = -4, so value = 1.6 x 2^(-4) += 0.1 + + + +IEEE-754 Quick Reference + +Value +Hex +Bytes (LE) + + +0.1 +0x3dcccccd +cd cc cc 3d +1.0 +0x3f800000 +00 00 80 3f + +5.0 +0x40a00000 +00 00 a0 40 +10.0 +0x41200000 +00 00 20 41 + +-1.0 +0xbf800000 +00 00 80 bf + diff --git a/WEEK09/slides/WEEK09-IMG09.svg b/WEEK09/slides/WEEK09-IMG09.svg new file mode 100644 index 0000000..5c40649 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG09.svg @@ -0,0 +1,64 @@ + + + + +Hacking the Float +Changing the DHT11 Scaling Constant + + + +DHT11 Scaling Calculation +result = integer + (decimal x 0.1) +Example: temp = 23 + (8 x 0.1) = 23.8C +0.1f is our target! + + + +Key Offsets in Binary + +Offset +Bytes +Meaning + + +0x410 +a6 ee 25 7a +vfma.f32 s14,s12,s11 (humidity) + +0x414 +e6 ee a5 7a +vfma.f32 s15,s13,s11 (temp) + +0x42C +cd cc cc 3d +0.1f -- the scaling constant + + + +The Hack: 0.1f --> 5.0f + +At offset 0x42C, change: + + +Original: cd cc cc 3d +(0.1f) + + +Patched: 00 00 a0 40 +(5.0f) + +New result: 23 + (8 x 5.0) = 63.0C +Decimal part is now multiplied by 5.0 instead of 0.1 +Export .bin from Ghidra, convert to UF2, flash to Pico + diff --git a/WEEK09/slides/WEEK09-IMG10.svg b/WEEK09/slides/WEEK09-IMG10.svg new file mode 100644 index 0000000..9655865 --- /dev/null +++ b/WEEK09/slides/WEEK09-IMG10.svg @@ -0,0 +1,97 @@ + + + + +Operators & DHT11 Hacking +Operators, DHT11, IEEE-754, and Hacking + + + +6 Operator Types + +Arithmetic +x * y = 50 + +Increment +x++ returns 5, x becomes 6 + +Relational +(6 > 10) = false + +Logical +false && true = false + +Bitwise +6 << 1 = 12 + +Assignment +x += 5 = 11 + +Post-increment: use THEN increment + + + +Key Addresses + +0x10000000 +Vector table + +0x10000004 +Reset_Handler addr + +0x10000234 +main() + +0x10000410 +Humidity vfma + +0x10000414 +Temp vfma + +0x1000042C +0.1f constant (hack) + + + +IEEE-754 Format +S(1) + Exp(8) + Mantissa(23) +(-1)^S x (1+M) x 2^(E-127) +0.1f = 0x3dcccccd = cd cc cc 3d + + + +Hack Workflow +1. Analyze in Ghidra +2. Find float at 0x42C +3. Patch cd cc cc 3d + + + +Binary Hacking Steps + +Analyze +--> +Identify +--> +Offset +--> +Patch +--> +Export +--> +Test + +Project: 0x001a_operators +Source: 0x001a_operators.c with DHT11 sensor on GPIO 4 + diff --git a/WEEK10/WEEK10-SLIDES.pdf b/WEEK10/WEEK10-SLIDES.pdf new file mode 100644 index 0000000..286c32a Binary files /dev/null and b/WEEK10/WEEK10-SLIDES.pdf differ diff --git a/WEEK10/WEEK10.md b/WEEK10/WEEK10.md new file mode 100644 index 0000000..375d2ba --- /dev/null +++ b/WEEK10/WEEK10.md @@ -0,0 +1,1600 @@ +# Week 10: Conditionals in Embedded Systems: Debugging and Hacking Static & Dynamic Conditionals w/ SG90 Servo Motor PWM Basics + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Understand the difference between static and dynamic conditionals in C +- Know how if/else statements and switch/case blocks work at the assembly level +- Understand Pulse Width Modulation (PWM) and how it controls servo motors +- Calculate PWM timing from system clock to servo pulse width +- Identify conditional branches in Ghidra (beq, bne instructions) +- Hack string literals and timing delays in binary files +- Modify branch targets to change program flow +- Create "stealth" functionality by NOP-ing out print statements +- Understand IEEE-754 floating-point for angle calculations + +--- + +## Part 1: Understanding Conditionals in C + +### What Are Conditionals? + +**Conditionals** are programming structures that make decisions. They let your program choose different paths based on whether a condition is true or false. Think of them like a fork in the road - the program checks a condition and decides which way to go. + +### Two Types of Conditionals + +| Type | Description | Example | +| ----------- | ---------------------------------------------- | ------------------------------------------------- | +| **Static** | Condition value is known/fixed at compile time | `if (choice == 1)` where choice never changes | +| **Dynamic** | Condition value changes based on runtime input | `if (choice == getchar())` where user types input | + +--- + +## Part 2: Static Conditionals + +### What Makes a Conditional "Static"? + +A **static conditional** is one where the outcome is predetermined because the condition variable never changes during program execution: + +```c +int choice = 1; // This NEVER changes! + +while (true) { + if (choice == 1) { + printf("1\r\n"); // This ALWAYS runs + } else if (choice == 2) { + printf("2\r\n"); // This NEVER runs + } else { + printf("?\r\n"); // This NEVER runs + } +} +``` + +```ext ++-----------------------+ +| choice = 1 | +| set once, never | +| changes | ++-----------------------+ + | + v + [ choice == 1 ] + | + | YES + v ++-----------------------+ +| printf \'1\' | ++-----------------------+ + . + . NO (never taken) + v + [ choice == 2 ] + . + . YES (never reached) + v ++-----------------------+ +| printf \'2\' | ++-----------------------+ + . + . NO + v ++-----------------------+ +| printf \'?\' | ++-----------------------+ +``` + +### The if/else Statement + +The `if/else` structure checks conditions in order: + +```c +if (choice == 1) { +// Do something if choice is 1 +} else if (choice == 2) { +// Do something if choice is 2 +} else { +// Do something for all other values +} +``` + +### The switch Statement + +The `switch` statement is another way to handle multiple conditions: + +```c +switch (choice) { + case 1: + printf("one\r\n"); + break; + case 2: + printf("two\r\n"); + break; + default: + printf("??\r\n"); +} +``` + +**Key Differences:** + +| Feature | if/else | switch | +| ---------------- | ----------------------- | -------------------------- | +| **Condition** | Any boolean expression | Single variable comparison | +| **Values** | Ranges, complex logic | Discrete values only | +| **Fall-through** | No | Yes (without `break`) | +| **Readability** | Good for 2-3 conditions | Better for many conditions | + +--- + +## Part 3: Dynamic Conditionals + +### What Makes a Conditional "Dynamic"? + +A **dynamic conditional** is one where the condition variable changes based on runtime input: + +```c +uint8_t choice = 0; + +while (true) { + choice = getchar(); // User types a key - VALUE CHANGES! + + if (choice == '1') { + printf("1\r\n"); + } else if (choice == '2') { + printf("2\r\n"); + } else { + printf("??\r\n"); + } +} +``` + +```text + +-------------------+ + | getchar() | <----------------------------------+ + +-------------------+ | + | User types input | + v | + +-------------------+ | + | choice = input | | + +-------------------+ | + | | + v | + [ choice == '1' ] -- YES --> +-----------------------+ | + | | printf("1") | | + | NO | move servo | | + | +-----------------------+ | + v | + [ choice == '2' ] -- YES --> +-----------------------+ | + | | printf("2") | | + | NO | move servo | | + | +-----------------------+ | + v | + +-------------------+ | + | printf("??") | | + +-------------------+ | + | | + +-----------------------------------------------+ +``` + +### The getchar() Function + +`getchar()` reads a single character from the serial terminal: + +```c +uint8_t choice = getchar(); // Waits for user to type something +``` + +- Returns the ASCII value of the key pressed +- `'1'` = 0x31, `'2'` = 0x32, `'x'` = 0x78, `'y'` = 0x79 +- Blocks (waits) until a key is pressed + +--- + +## Part 4: Understanding PWM (Pulse Width Modulation) + +### What is PWM? + +**PWM** (Pulse Width Modulation) is a technique for controlling power by rapidly switching a signal on and off. The ratio of "on time" to "off time" determines the average power delivered. + +```text + PWM Signal - 50% Duty Cycle + + HIGH +-----+ +-----+ +-----+ + | | | | | | + LOW + +-----+ +-----+ +----- + |--T--| + ON OFF + + Duty Cycle = ON time / Total period = 50% +``` + +### PWM for Servo Control + +Servo motors use PWM differently - they care about the **pulse width**, not the duty cycle percentage: + +```text + Servo PWM Signal (50 Hz = 20ms period) + + 0° Position (1ms pulse): + HIGH -+ + | 1ms + LOW +----------------------------------- (19ms) ----- + + 90° Position (1.5ms pulse): + HIGH ---+ + | 1.5ms + LOW +-------------------------------- (18.5ms) ---- + + 180° Position (2ms pulse): + HIGH -----+ + | 2ms + LOW +-------------------------------- (18ms) ---- +``` + +### The Magic Numbers + +| Angle | Pulse Width | PWM Ticks (at 1MHz) | +| ----- | ----------- | ------------------- | +| 0° | 1000 µs | 1000 | +| 90° | 1500 µs | 1500 | +| 180° | 2000 µs | 2000 | + +--- + +## Part 5: PWM Timing Calculations + +### From 150 MHz to 50 Hz + +The RP2350's system clock runs at **150 MHz** (150 million cycles per second). A servo needs a **50 Hz** signal (one pulse every 20 ms). How do we bridge this gap? + +``` ext ++-----------------------+ +| System Clock | +| 150 MHz | ++-----------------------+ + | + | Divide by 150 + v ++-----------------------+ +| PWM Tick Rate | +| 1 MHz | +| (1 tick = 1 µs) | ++-----------------------+ + | + | Count to 20,000 + | Wrap at 19,999 + v ++-----------------------+ +| Servo PWM Signal | +| 50 Hz | +| (20 ms period) | ++-----------------------+ +``` + +### The Math + +**Step 1: Clock Division** +``` +PWM Tick Rate = System Clock / Divider +1,000,000 Hz = 150,000,000 Hz / 150 +``` + +**Step 2: Frame Period** +``` +Period = (Wrap Value + 1) * Tick Duration +20 ms = 20,000 ticks * 1 µs/tick +``` + +**Step 3: Pulse Width to Ticks** +``` +Ticks = Pulse Width (µs) * 1 tick/µs +1500 ticks = 1500 µs * 1 +``` + +### Worked Example: 90° Angle + +Let's calculate what happens when we command 90°: + +1. **Angle to Pulse Width:** + ``` + Pulse = MIN + (angle/180) * (MAX - MIN) + Pulse = 1000 + (90/180) * (2000 - 1000) + Pulse = 1000 + 0.5 * 1000 + Pulse = 1500 µs + ``` + +2. **Pulse to PWM Ticks:** + ``` + Level = 1500 µs * 1 tick/µs = 1500 ticks + ``` + +3. **Hardware Timing:** + - Signal HIGH for 1500 ticks (1.5 ms) + - Signal LOW for 18,500 ticks (18.5 ms) + - Total period: 20,000 ticks (20 ms) + +--- + +## Part 6: Understanding the SG90 Servo Motor + +### What is the SG90° + +The **SG90** is a small, inexpensive hobby servo motor commonly used in robotics projects: + +``` ext +[ SG90 Servo Motor ] ++-----------------------------------+ +| Motor ---> Gearbox ---> Arm | +| (0° to 180°) | ++-----------------------------------+ + | Wires + v +[ Wires ] ++-----------------------------------+ +| Orange: Signal / PWM | +| Red: VCC / 5V | +| Brown: GND / Ground | ++-----------------------------------+ +``` + +### SG90 Specifications + +| Parameter | Value | +| ----------------- | ------------------------- | +| **Voltage** | 4.8V - 6V (typically 5V) | +| **Rotation** | 0° to 180° | +| **Pulse Width** | 1000 us - 2000 us | +| **Frequency** | 50 Hz (20ms period) | +| **Stall Current** | ~650mA (can spike to 1A+) | + +### Wire Colors + +| Wire Color | Function | Connect To | +| ---------- | -------- | --------------- | +| **Brown** | GND | Ground | +| **Red** | VCC | 5V Power (VBUS) | +| **Orange** | Signal | GPIO Pin (PWM) | + +--- + +## Part 7: Power Supply Safety + +### CRITICAL WARNING + +**NEVER power the servo directly from the Pico's 3.3V pin!** + +Servos can draw over 1000mA during movement spikes. The Pico's 3.3V regulator cannot handle this and you will: +- Cause brownouts (Pico resets) +- Damage the Pico's voltage regulator +- Potentially damage your USB port + +### Correct Power Setup + +``` ext ++-----------------+ +------------------------+ +| USB Power | ---> | VBUS 5V | ++-----------------+ +------------------------+ + | | + v v + [ Servo VCC ] [ Capacitor + ] + (Red) (1000 uF 25V) + ++-----------------+ +| Pico GND | ++-----------------+ + | + +--------------------+--------------------+ + | | + v v + [ Servo GND ] [ Capacitor - ] + (Brown) + ++-----------------+ +| Pico GPIO 6 | ---> [ Servo Signal ] (Orange) ++-----------------+ +``` + +### Why the Capacitor? + +The **1000 uF capacitor** acts as a tiny battery: +- Absorbs sudden current demands when servo moves +- Prevents voltage drops that could reset the Pico +- Smooths out electrical noise + +--- + +## Part 8: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. Ghidra installed (for static analysis) +4. Python installed (for UF2 conversion) +5. A serial monitor (PuTTY, minicom, or screen) +6. An SG90 servo motor +7. A 1000 uF 25V capacitor +8. The sample projects: `0x001d_static-conditionals` and `0x0020_dynamic-conditionals` + +### Hardware Setup + +Connect your servo like this: + +| Servo Wire | Pico 2 Pin | +| --------------- | ---------- | +| Brown (GND) | GND | +| Red (VCC) | VBUS (5V) | +| Orange (Signal) | GPIO 6 | + +``` ext +Hardware Setup: + + Pico 2 Servo SG90 Capacitor (1000uF 25V) ++----------+ +-------------+ +-------------+ +| | | | | | +| GPIO 6 | -------> | Signal (Org)| | | +| | | | | | +| VBUS 5V | -------> | VCC (Red) | | | +| | | | | | | +| | +----> | | -----> | + | +| | | | | | +| GND | -------> | GND (Brn) | | | +| | | | | | | +| | +----> | | -----> | - | ++----------+ +-------------+ +-------------+ +``` + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x001d_static-conditionals/ +| +-- build/ +| | +-- 0x001d_static-conditionals.uf2 +| | +-- 0x001d_static-conditionals.bin +| +-- main/ +| | +-- 0x001d_static-conditionals.c +| +-- servo.h ++-- 0x0020_dynamic-conditionals/ +| +-- build/ +| | +-- 0x0020_dynamic-conditionals.uf2 +| | +-- 0x0020_dynamic-conditionals.bin +| +-- main/ +| | +-- 0x0020_dynamic-conditionals.c +| +-- servo.h ++-- uf2conv.py +``` + +--- + +## Part 9: Hands-On Tutorial - Static Conditionals Code + +### Step 1: Review the Source Code + +Let's examine the static conditionals code: + +**File: `0x001d_static-conditionals.c`** + +```c +#include +#include "pico/stdlib.h" +#include "servo.h" + +#define SERVO_GPIO 6 + +int main(void) { + stdio_init_all(); + + int choice = 1; // STATIC - never changes! + + servo_init(SERVO_GPIO); + + while (true) { + // if/else conditional + if (choice == 1) { + printf("1\r\n"); + } else if (choice == 2) { + printf("2\r\n"); + } else { + printf("?\r\n"); + } + + // switch/case conditional + switch (choice) { + case 1: + printf("one\r\n"); + break; + case 2: + printf("two\r\n"); + break; + default: + printf("??\r\n"); + } + + // Servo movement + servo_set_angle(0.0f); + sleep_ms(500); + servo_set_angle(180.0f); + sleep_ms(500); + } +} +``` + +### Step 2: Understand the Program Flow + +Since `choice = 1` and NEVER changes: + +``` ext ++-------------------------+ +| Start Loop Iteration | <-----------------------------------+ ++-------------------------+ | + | | + v | + [ choice == 1 ] -- TRUE --> +-------------------------+ | + | print \'1\' | | + +-------------------------+ | + | | + v | + [ switch case 1 ] | + | MATCH | + v | + +-------------------------+ | + | print \'one\' | | + +-------------------------+ | + | | + v | + +-------------------------+ | + | Move servo to 0° | | + +-------------------------+ | + | | + v | + +-------------------------+ | + | Wait 500ms | | + +-------------------------+ | + | | + v | + +-------------------------+ | + | Move servo to 180° | | + +-------------------------+ | + | | + v | + +-------------------------+ | + | Wait 500ms | ---+ + +-------------------------+ +``` + +### Step 3: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x001d_static-conditionals.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 4: Verify It's Working + +**Check the serial monitor (PuTTY at 115200 baud):** +``` +1 +one +1 +one +1 +one +... +``` + +**Watch the servo:** +- It should sweep from 0° to 180° every second +- The movement is continuous and repetitive + +--- + +## Part 10: Debugging with GDB (Static Conditionals) + +### Step 5: Start OpenOCD (Terminal 1) + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe. + +### Step 6: Start GDB (Terminal 2) + +Open a **new terminal** and launch GDB with the binary: + +```cmd +arm-none-eabi-gdb build\0x001d_static-conditionals.elf +``` + +### Step 7: Connect to the Remote Target + +In GDB, connect to OpenOCD: + +```gdb +target extended-remote :3333 +``` + +### Step 8: Halt the Running Binary + +Stop the processor: + +```gdb +monitor halt +``` + +### Step 9: Examine Main Function + +Disassemble around main to see the conditionals: + +```gdb +disassemble 0x10000234,+200 +``` + +Look for comparison and branch instructions that implement the if/else and switch logic. + +### Step 10: Set a Breakpoint at Main + +```gdb +break *0x10000234 +``` + +Reset and run to hit the breakpoint: + +```gdb +monitor reset halt +continue +``` + +### Step 11: Find the Comparison Instructions... Or Not! + +To see the assembly instructions as you execute them, tell GDB to display the current instruction automatically: + +```gdb +display/i $pc +nexti +``` + +Keep pressing **Enter** to repeat the `nexti` command and watch the instructions. + +**Wait, where is the `cmp` instruction?!** +You might notice that there is NO `cmp` instruction comparing our `choice` variable anywhere! Why? Because we hardcoded `int choice = 1;` at the start of our C code and never changed it. + +The C compiler is smart (even at basic optimization levels). It realized the `if (choice == 1)` condition would *always* be true, and the `else` conditions would *never* happen. Instead of wasting CPU cycles checking a condition that never changes, the compiler **optimized out the check entirely**! It just compiled the code inside the `choice == 1` block unconditionally. + +This is the defining characteristic of a "Static Conditional"—the condition is resolved at compile-time, not run-time! + +### Step 12: Examine the Printf/Puts Arguments + +If you look closely at your disassembly, you'll notice there are no `printf` calls! The compiler optimized our simple `printf` statements into `puts` (specifically `__wrap_puts` in the Pico SDK) because they didn't contain any complex formatting variables. + +When you reach a `bl <__wrap_puts>` instruction, check `r0` for the string address: + +```gdb +x/s $r0 +``` + +You should see strings like `"1\r"` or `"one\r"`. + +*(Wait, what happened to the `\n`? Because the `puts` function automatically adds a newline to the end of whatever it prints, the compiler cleverly trimmed the `\n` out of the string literal in memory to save space!)* + +### Step 13: Watch the Servo Commands + +Let's set a breakpoint on `servo_set_angle`. How do you know the address? If you look at your `main` disassembly, you can find the `bl` instruction calling it (for example, `0x10000310`). + +But hardcoded addresses change every time you recompile! Luckily, GDB is smart enough to resolve function names directly from the ELF file: + +```gdb +break servo_set_angle +continue +``` + +Check the argument passed to the function. You might assume the float is in `s0`, but check out the very first instruction of `servo_set_angle`: + +`vmov s14, r0` + +Because of how the ARM compiler handles arguments, the floating-point angle is actually passed into the function via the standard integer register `r0`. The `vmov` instruction immediately moves it from `r0` into the floating-point register `s14` so the math unit can use it! + +To see the angle, you can ask GDB to interpret the raw hex value in `r0` as a float. You can also step forward (`nexti` or `n`) to let the `vmov` execute, and then check `s14` directly! + +Here is exactly what that process looks like in your GDB terminal (assuming you hit `continue` to catch the second call where the angle is 180): + +```gdb +=> 0x10000310 : vmov s14, r0 +(gdb) print /f $r0 +$2 = 180 +(gdb) nexti +=> 0x10000314 : push {r4, r5, lr} +(gdb) info registers s14 +s14 180 (raw 0x43340000) +``` + +*(Note: On your very first breakpoint hit, `print /f $r0` will just show `0` because the first call in the C code is `servo_set_angle(0)`!)* + +### Step 14: Examine the Timing Delay + +Set a breakpoint on `sleep_ms` (using the function name, not a hardcoded address!) and check the delay value: + +```gdb +break sleep_ms +continue +info registers r0 +``` + +You should see `0x1f4` (500 decimal) for the 500ms delay. + +### Step 15: Watch the Loop Iterate + +Because this loop contains `sleep_ms(500)` calls, trying to use `nexti 100` to step forward will cause GDB to "hang" (either because it's waiting for multiple seconds of sleep to finish, or because GDB stepping interferes with the Pico's hardware timer interrupts!). + +Instead, since we already have breakpoints set on `servo_set_angle` and `sleep_ms`, just type `continue`! + +```gdb +continue +``` + +Every time you type `continue` (or press **Enter** to repeat it), you will see the CPU safely jump to the next function call. Because this is a *Static Conditional*, it will never hit a `cmp` instruction or take a different branch—it just bounces between `servo_set_angle` and `sleep_ms` forever! + +### Step 16: Exit GDB + +When done exploring: + +```gdb +quit +``` + +--- + +## Part 11: Setting Up Ghidra for Static Conditionals + +### Step 17: Start Ghidra + +Open a terminal and type: + +```cmd +ghidraRun +``` + +### Step 18: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x001d_static-conditionals` +5. Click **Finish** + +### Step 19: Import the Binary + +1. Open your file explorer +2. Navigate to the `0x001d_static-conditionals/build/` folder +3. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 20: Configure the Binary Format + +**Click the three dots (...) next to "Language" and:** +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` +3. Click **OK** + +### Step 21: Analyze the Binary + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete. + +--- + +## Part 12: Resolving Functions in Ghidra (Static) + +### Step 22: Navigate to Main + +1. Press `G` (Go to address) and type `10000234` +2. Right-click -> **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +### Step 23: Resolve stdio_init_all + +At address `0x10000236`: + +1. Double-click on the called function +2. Right-click -> **Edit Function Signature** +3. Change to: `bool stdio_init_all(void)` +4. Click **OK** + +### Step 24: Resolve servo_init + +Look for a function call where `r0` is loaded with `0x6` (GPIO pin 6): + +```assembly +movs r0, #0x6 ; GPIO pin 6 +bl FUN_1000027c ; servo_init +``` + +1. Right-click -> **Edit Function Signature** +2. Change to: `void servo_init(uint pin)` +3. Click **OK** + +### Step 25: Resolve puts + +Look for function calls that load string addresses into `r0`: + +```assembly +ldr r0=>DAT_10001c54 ,[DAT_10000274 ] = 00000D31h +bl FUN_10001884 undefined FUN_10001884() +``` + +**How do we know it's puts?** +- It takes a single string argument +- The hex `0x31` is ASCII "1" +- The hex `0x0d` is carriage return "\r" +- We saw "1" echoed in PuTTY + +1. Right-click -> **Edit Function Signature** +2. Change to: `int puts(char *s)` +3. Click **OK** + +### Step 26: Resolve servo_set_angle + +Look for a function call (like `bl FUN_10000310`) that occurs right after the float arguments are loaded into `r0`. + +```assembly +mov r0,r5 +bl FUN_10000310 undefined FUN_10000310() +``` + +If you double-click the `FUN_10000310` label to look inside the function, you'll find a section of code checking the pulse limits: + +```assembly +cmp.w r3,#0x7d0 +it cs +mov.cs.w r3,#0x7d0 +cmp.w r3,#0x3e8 +it cc +mov.cc.w r3,#0x3e8 +``` + +These values are: +- `0x7D0` (2000 decimal) - maximum pulse width +- `0x3E8` (1000 decimal) - minimum pulse width + +These are the servo pulse limits! + +1. Right-click -> **Edit Function Signature** +2. Change to: `void servo_set_angle(float degrees)` +3. Click **OK** + +### Step 27: Resolve sleep_ms + +Look for a function where `r0` is loaded with `0x1f4` (500 decimal): + +```assembly +mov.w r0,#0x1f4 +bl FUN_10000e20 undefined FUN_10000e20() +``` + +1. Right-click -> **Edit Function Signature** +2. Change to: `void sleep_ms(uint ms)` +3. Click **OK** + +--- + +## Part 13: Hacking Static Conditionals + +### Step 28: Open the Bytes Editor + +1. Click **Window** -> **Bytes** +2. A new panel appears showing raw hex bytes +3. Click the pencil icon to enable editing + +### Step 29: Hack #1 - Change "1" to "2" + +First, we need to find the string "1" in the binary. + +1. Go back to your `main` assembly code and look for the first `puts` call. +2. In the `ldr` instruction right before it, look for the reference next to the arrow: `r0=>DAT_10001c54`. **Double-click exactly on `DAT_10001c54`**. *(Warning: Do NOT click the reference inside the brackets like `[DAT_10000274]`, or you'll end up in the pointer table instead of the string!)* +3. The Listing and Bytes windows will automatically jump to the string's exact address! +4. Look at your **Bytes** window. You should see the byte `31` (which is ASCII for "1"). +5. Click on the `31` and type `32` to overwrite it (which is ASCII for "2"). + +### Step 30: Hack #2 - Change "one" to "fun" + +Next, let's change the word "one" to "fun": + +1. Go back to `main` and look for the second `puts` call. +2. In the `ldr` instruction, double-click the `DAT_10001c5c` reference next to the arrow (again, ignore the one in the brackets!). +3. Look at the **Bytes** window again. You'll find the bytes `6f 6e 65` (which are ASCII for "o-n-e"). +4. Click on the `6f` byte and type `66 75 6e` on your keyboard to overwrite those three bytes with "f-u-n". + +**ASCII Reference:** +| Character | Hex | +| --------- | ---- | +| o | 0x6f | +| n | 0x6e | +| e | 0x65 | +| f | 0x66 | +| u | 0x75 | +| n | 0x6e | + +### Step 31: Hack #3 - Speed Up the Servo + +Let's change the 500ms delay to a 100ms delay. Since the compiler packed the `500` directly into a `mov.w` instruction, we can't just find it in the data section. Instead, let's use Ghidra's built-in assembler to rewrite the instruction! + +1. In your `main` assembly code, look for the two `mov.w r0,#0x1f4` instructions (which happen right before calling `sleep_ms`). +2. Right-click the first `mov.w r0,#0x1f4` instruction and select **Patch Instruction** (or press `Ctrl+Shift+G`). +3. Delete the `#0x1f4` and type `#0x64` (which is 100 in hex). Press **Enter**! +4. Repeat this for the second `mov.w r0,#0x1f4` instruction. + +**Before:** 500ms delay (servo moves slowly) +**After:** 100ms delay (servo moves FAST!) + +### Step 32: Export and Flash + +When exporting from Ghidra, you must make sure to save the file inside your `build/` directory so the python script can find it! + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. **IMPORTANT:** Click the `...` next to the Output File field and navigate into your `0x001d_static-conditionals/build/` folder! +4. Save the file as `0x001d_static-conditionals-h.bin`. +5. Click **OK** + +Convert and flash (make sure your terminal is inside the `0x001d_static-conditionals/` folder, NOT the `build/` folder!): + +```cmd +python ..\uf2conv.py build\0x001d_static-conditionals-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +*(Troubleshooting: If you get `FileNotFoundError` for the `.bin` file, it means you didn't save the exported file into the `build/` folder! Go back to Ghidra and export it again. If you get an error that Python can't open `..\uf2conv.py`, it means you accidentally `cd`'d into the `build/` folder. Type `cd ..` to go back up one directory and run the command again!)* + +### Step 33: Verify the Hacks + +**Serial output now shows:** +``` +2 +fun +2 +fun +... +``` + +**The servo now moves 5x faster!** It's spinning back and forth like crazy! + +--- + +## Part 14: Dynamic Conditionals - The Source Code + +### Step 34: Review the Dynamic Code + +**File: `0x0020_dynamic-conditionals.c`** + +```c +#include +#include "pico/stdlib.h" +#include "servo.h" + +#define SERVO_GPIO 6 + +int main(void) { + stdio_init_all(); + + uint8_t choice = 0; // DYNAMIC - changes with user input! + + servo_init(SERVO_GPIO); + + while (true) { + choice = getchar(); // Wait for keyboard input + + if (choice == 0x31) { // '1' + printf("1\r\n"); + } else if (choice == 0x32) { // '2' + printf("2\r\n"); + } else { + printf("??\r\n"); + } + + switch (choice) { + case '1': + printf("one\r\n"); + servo_set_angle(0.0f); + sleep_ms(500); + servo_set_angle(180.0f); + sleep_ms(500); + break; + case '2': + printf("two\r\n"); + servo_set_angle(180.0f); + sleep_ms(500); + servo_set_angle(0.0f); + sleep_ms(500); + break; + default: + printf("??\r\n"); + } + } +} +``` + +### Step 35: Understand the Dynamic Behavior + +| User Types | Output | Servo Action | +| ------------- | ----------- | ------------ | +| '1' (0x31) | "1" + "one" | 0° -> 180° | +| '2' (0x32) | "2" + "two" | 180° -> 0° | +| Anything else | "??" + "??" | No movement | + +### Step 36: Flash and Test + +1. Flash `0x0020_dynamic-conditionals.uf2` +2. Open PuTTY +3. Press '1' - servo sweeps one direction +4. Press '2' - servo sweeps the other direction +5. Press 'x' - prints "??" and no movement + +--- + +## Part 15: Debugging with GDB (Dynamic Conditionals) + +### Step 37: Start OpenOCD (Terminal 1) + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe. + +### Step 38: Start GDB (Terminal 2) + +Open a **new terminal** and launch GDB with the binary: + +```cmd +arm-none-eabi-gdb build\0x0020_dynamic-conditionals.elf +``` + +### Step 39: Connect to the Remote Target + +In GDB, connect to OpenOCD: + +```gdb +target extended-remote :3333 +``` + +### Step 40: Halt the Running Binary + +Stop the processor: + +```gdb +monitor halt +``` + +### Step 41: Examine Main Function + +Disassemble around main to see the dynamic conditionals: + +```gdb +disassemble 0x10000234,+250 +``` + +Look for the `bl <__wrap_getchar>` call. Because `choice` is now dynamic (based on user input), the compiler *had* to generate the comparison logic! It should look something like this: + +```assembly +bl 0x10001860 <__wrap_getchar> +uxtb r4, r0 +cmp r4, #49 @ 0x31 +beq.n 0x10000270 +cmp r4, #50 @ 0x32 +``` +There they are! The `cmp` instructions checking for `0x31` ('1') and `0x32` ('2'). + +### Step 42: Set a Breakpoint After getchar + +We want to pause execution right after we type a character so we can inspect the comparison. +Find the address of the first `cmp` instruction in your disassembly (in the example above, it's `0x1000024a`) and set a breakpoint: + +```gdb +break *0x1000024a +``` +*(Make sure you use the exact address of the `cmp` instruction from YOUR disassembly!)* + +Reset and continue: + +```gdb +monitor reset halt +continue +``` + +### Step 43: Watch the Input Value + +When you press a key in PuTTY, the breakpoint hits. Check the return value: + +```gdb +info registers r0 +``` + +If you pressed '1', you should see `0x31`. If you pressed '2', you should see `0x32`. + +### Step 44: Execute the Comparison + +Right now, GDB is paused *before* the `cmp` instruction runs (the `=>` arrow points to the instruction that will execute *next*). + +To see the result of the comparison, we must execute the `cmp` instruction by stepping forward exactly once: + +```gdb +display/i $pc +stepi +``` + +Notice that the `=>` arrow has now moved to the `beq.n` (Branch if Equal) instruction. The `cmp` instruction has just executed. + +### Step 45: Examine the Branch Decisions + +Now that the comparison has run, we can look at the CPU's condition flags to see what happened: + +```gdb +info registers xpsr +``` +*(Note: Older ARM chips call this `cpsr`, but Cortex-M chips like the Pico's RP2350 call it `xpsr`!)* + +The zero flag (`Z`) determines if the branch is taken. The flags are stored in the highest nibble (the first hex digit) of the `xpsr` register in the order `N Z C V`. + +If you typed '1', the comparison resulted in zero difference, setting the `Z` flag to 1. You should see `xpsr` start with a `6` (like `0x69000000`). `6` in binary is `0110`, which means the `Z` flag (the second bit) is 1! The `beq.n` instruction will see this flag and take the branch. + +### Step 46: Watch Different Input Paths + +Continue and press different keys to see how the program takes different branches: + +```gdb +continue +``` + +Press '2' in PuTTY, then examine registers again. + +### Step 47: Examine Servo Control + +Set a breakpoint on servo_set_angle: + +```gdb +break *0x10000280 +continue +``` + +Check the angle value: + +```gdb +info registers s0 +``` + +### Step 48: Exit GDB + +When done exploring: + +```gdb +quit +``` + +--- + +## Part 16: Setting Up Ghidra for Dynamic Conditionals + +### Step 49: Create New Project + +1. Create project: `0x0020_dynamic-conditionals` +2. Import the `.bin` file +3. Configure as ARM Cortex, base address `10000000` +4. Analyze + +### Step 50: Navigate to Main + +Press `G` and go to `10000234`. + +### Step 51: Resolve Functions + +Follow the same process: + +1. **main** at `0x10000234` -> `int main(void)` +2. **stdio_init_all** -> `bool stdio_init_all(void)` +3. **servo_init** -> `void servo_init(uint pin)` +4. **puts** -> `int puts(char *s)` +5. **servo_set_angle** -> `void servo_set_angle(float degrees)` +6. **sleep_ms** -> `void sleep_ms(uint ms)` + +### Step 52: Identify getchar + +Look for a function that: +- Returns a value in `r0` +- That value is then compared against `0x31` ("1") + +```assembly +bl FUN_10001860 undefined FUN_10001860() +uxtb r4,r0 +cmp r4,#0x31 +beq LAB_10000270 +``` + +1. Right-click -> **Edit Function Signature** +2. Change to: `int getchar(void)` +3. Click **OK** + +### Step 53: Identify Hardware Addresses + +Double-click into `stdio_init_all` and look for hardware addresses: + +```assembly +ldr r0, =0x40070000 ; UART0 base address +``` + +Check the RP2350 datasheet Section 2.2 (Address Map): +- `0x40070000` = UART0 + +This confirms it's a UART initialization function! + +--- + +## Part 17: Understanding Branch Instructions + +### ARM Branch Instructions + +| Instruction | Meaning | Condition | +| ----------- | ---------------------- | ------------------ | +| `b` | Branch (always) | Unconditional jump | +| `beq` | Branch if Equal | Zero flag set | +| `bne` | Branch if Not Equal | Zero flag clear | +| `bgt` | Branch if Greater Than | Signed greater | +| `blt` | Branch if Less Than | Signed less | + +### How Conditionals Become Branches + +```c +if (choice == 0x31) { + printf("1"); +} +``` + +Becomes: + +```assembly +cmp r4, #0x31 ; Compare choice to '1' +bne skip_printf ; If NOT equal, skip the printf +; ... printf code here ... +skip_printf: +``` + +``` ext ++---------------------------------+ +| cmp r4, #0x31 | +| Sets flags based on r4 - 0x31 | ++---------------------------------+ + | + v + [ beq target_address ] + | + +--------+--------+ + | | + If r4 == 0x31 If r4 != 0x31 + | | + v v ++---------------+ +----------------------------+ +| Jump to | | Continue to | +| target_address| | next instruction | ++---------------+ +----------------------------+ +``` + +--- + +## Part 18: Advanced Hacking - Creating Stealth Commands + +### The Goal + +We want to create **secret commands** that: +1. Respond to 'x' and 'y' instead of '1' and '2' +2. Move the servo WITHOUT printing anything +3. Leave NO trace in the terminal + +### Step 54: Plan the Patches + +**Original behavior:** +- '1' (0x31) -> prints "1" and "one", moves servo +- '2' (0x32) -> prints "2" and "two", moves servo + +**Hacked behavior:** +- 'x' (0x78) -> moves servo SILENTLY (replacing '1') +- 'y' (0x79) -> moves servo SILENTLY (replacing '2') + +### Step 55: Change Comparison Values + +Navigate to the `main` function and find the two `cmp` instructions right after `getchar`: + +1. At address `1000024a`, you will see `cmp r4,#0x31`. + - Right-click it, select **Patch Instruction**, and change it to `cmp r4,#0x78` (which is ASCII 'x'). +2. At address `1000024e`, you will see `cmp r4,#0x32`. + - Right-click it, select **Patch Instruction**, and change it to `cmp r4,#0x79` (which is ASCII 'y'). + +### Step 56: Redirect Branches to Skip Prints + +For the stealth keys, we need to jump PAST the printf calls directly to the servo code. + +**Original flow:** +``` +compare -> branch -> printf("1") -> printf("one") -> servo code +``` + +**Hacked flow:** +``` +compare 'x' -> branch -> [skip prints] -> servo code +``` + +Use **Patch Instruction** to rewrite the `beq` target addresses: + +1. At address `1000024c`, you will see `beq LAB_10000270`. + - `10000270` is the block that prints "1". We want to skip it! + - Right-click, select **Patch Instruction**, and change it to `beq 0x1000027c` (this jumps straight to the `mov r0,r6` servo code!). +2. At address `10000250`, you will see `beq LAB_1000029a`. + - `1000029a` is the block that prints "2". + - Right-click, select **Patch Instruction**, and change it to `beq 0x100002a6` (this jumps straight to the `mov r0,r5` servo code!). + +### Step 57: NOP Out Print Calls (Alternative Method) + +**NOP** (No Operation) is an instruction that does absolutely nothing. Hackers use it to "erase" code without changing the size of the binary! Since we already redirected the branches to skip the prints, this is technically redundant, but let's do it anyway just to learn the technique. + +The `bl` instruction is 32-bits (4 bytes) long. Standard Thumb `nop` instructions are only 16-bits (2 bytes) long. If you try to patch a 4-byte instruction with a 2-byte instruction, Ghidra's assembler gets very confused and corrupts the code. + +To fix this, we use the special 32-bit version of NOP: `nop.w` (Wide NOP). + +1. In the Listing view, right-click the `bl FUN_10001954` instruction at `10000272`. +2. Select **Patch Instruction**. +3. Type `nop.w` (don't forget the `.w`!) and hit Enter. +4. You will see the entire 4-byte instruction neatly get replaced by a single 32-bit NOP. +5. Repeat this for the second `bl` instruction at `10000278`. + +### Step 58: Summary of Control Flow Patches + +Here is a quick summary of the stealth command patches we just applied to the control flow: + +| Location | Original Action | Patched Action | Purpose | +| ---------- | -------------------- | -------------------- | ---------------------------- | +| `1000024a` | `cmp r4,#0x31` | `cmp r4,#0x78` | Check for 'x' instead of '1' | +| `1000024e` | `cmp r4,#0x32` | `cmp r4,#0x79` | Check for 'y' instead of '2' | +| `1000024c` | `beq LAB_10000270` | `beq LAB_1000027c` | Skip printf for 'x' | +| `10000250` | `beq LAB_1000029a` | `beq LAB_100002a6` | Skip printf for 'y' | + +### Step 59: One Final Hack - Modify the Angle + +Let's also change the servo's movement angle from 180° to 30° for fun! + +If you look back near the top of the `main` function (around address `10000242`), you'll see this instruction: +`ldr r5,[DAT_100002c4] = 43340000h` + +The compiler stored the 180.0 float value (`0x43340000`) in a literal pool at address `100002c4`. To change the angle, we just need to overwrite that raw data! + +**Original:** `0x43340000` (180.0f) +**New:** `0x41f00000` (30.0f) + +Here is how to apply the patch: +1. Press `G` and jump to address `100002c4`. +2. In your **Bytes** window (make sure the Pencil icon is still clicked!), you will see the raw little-endian bytes: `00 00 34 43`. +3. Click on the first `00` and type `00 00 f0 41`. +4. The Listing view will instantly update to show the new `41f00000` value! + +*(For the math nerds, here is how we calculated `0x41f00000` manually using the IEEE-754 standard):* + +**Calculation for 30.0f:** +``` +30.0 = 1.875 * 2^4 +Sign = 0 +Exponent = 127 + 4 = 131 = 0x83 +Mantissa = 0.875 = 0x700000 + +Binary: 0 10000011 11100000000000000000000 +Hex: 0x41f00000 +Little-endian: 00 00 f0 41 +``` + +### Step 60: Export and Test + +When exporting, make sure to save it in your `build/` folder! + +1. Export as `0x0020_dynamic-conditionals-h.bin` inside `build/`. +2. Convert and flash (run from the `0x0020_dynamic-conditionals` directory!): + +```cmd +python ..\uf2conv.py build\0x0020_dynamic-conditionals-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +3. Flash and test: + - Press 'x' -> NO OUTPUT, but servo moves silently! + - Press 'y' -> NO OUTPUT, but servo moves silently! + - (The original '1' and '2' keys no longer work!) + +--- + +## Part 19: Summary and Review + +### What We Accomplished + +1. **Learned static vs dynamic conditionals** - Fixed vs runtime-determined values +2. **Understood if/else and switch/case** - Two ways to branch in C +3. **Mastered PWM calculations** - 150MHz to 50Hz servo signal +4. **Identified conditional branches in assembly** - beq, bne, cmp instructions +5. **Hacked string literals** - Changed "one" to "fun" +6. **Modified timing values** - Sped up servo from 500ms to 100ms +7. **Created stealth commands** - Hidden 'x' and 'y' keys +8. **NOPed out print statements** - Removed logging for stealth +9. **Redirected branch targets** - Changed program flow + +### Static vs Dynamic Summary + +```text + Static Conditionals + ------------------- + - Variable set once, never changes + - Same path taken every iteration + - Compiler may optimize out dead branches + - Example: int choice = 1; if (choice == 1) + + Dynamic Conditionals + -------------------- + - Variable changes based on input/sensors + - Different paths taken based on runtime state + - All branches must remain in binary + - Example: choice = getchar(); if (choice == '1') +``` + +### PWM Calculation Summary + +``` ext ++---------+ +-------------+ +-----------+ +--------------+ +| Angle | Formula | Pulse Width | 1us= | PWM Ticks | Servo | Servo Motion | +| degrees | ------> | µs | 1 tick | | Motion | | ++---------+ +-------------+ ------> +-----------+ ------> +--------------+ + | | | | + 0° ---------------- 1000 µs ------------- 1000 ticks --------- Fully CCW + 90° ---------------- 1500 µs ------------- 1500 ticks --------- Center + 180° ---------------- 2000 µs ------------- 2000 ticks --------- Fully CW +``` + +### Key Memory Addresses + +| Memory Address | Description | +| -------------- | ------------------------ | +| `0x10000234` | main() function | +| `0x40070000` | UART0 hardware registers | +| `0x1f4` | 500 (sleep_ms delay) | +| `0x7D0` | 2000 (max pulse width) | +| `0x3E8` | 1000 (min pulse width) | +| `0x43340000` | 180.0f (max angle) | + +--- + +--- + +## Key Takeaways + +1. **Static conditionals have fixed outcomes** - The same path always executes + +2. **Dynamic conditionals respond to input** - Different paths based on runtime state + +3. **PWM frequency = 50Hz for servos** - One pulse every 20ms + +4. **Pulse width encodes position** - 1ms=0°, 1.5ms=90°, 2ms=180° + +5. **beq = branch if equal** - Jumps when comparison matches + +6. **bne = branch if not equal** - Jumps when comparison doesn't match + +7. **NOP erases code without changing size** - `00 bf` in ARM Thumb + +8. **Branch targets can be redirected** - Change where code jumps to + +9. **IEEE-754 is needed for angles** - Floats have specific bit patterns + +10. **Stealth requires removing ALL output** - NOP out printf AND puts + +--- + +## Glossary + +| Term | Definition | +| ----------------------- | --------------------------------------------------- | +| **beq** | Branch if Equal - ARM conditional jump | +| **bne** | Branch if Not Equal - ARM conditional jump | +| **Dynamic Conditional** | Condition that changes based on runtime input | +| **Duty Cycle** | Percentage of time signal is HIGH | +| **getchar()** | C function that reads one character from input | +| **NOP** | No Operation - instruction that does nothing | +| **PWM** | Pulse Width Modulation - variable duty cycle signal | +| **SG90** | Common hobby servo motor model | +| **Static Conditional** | Condition with fixed/predetermined outcome | +| **switch/case** | C structure for multiple discrete value comparisons | +| **Wrap Value** | PWM counter maximum before reset | + +--- + +## Additional Resources + +### ASCII Reference Table + +| Character | Hex | Decimal | +| --------- | ---- | ------- | +| '0' | 0x30 | 48 | +| '1' | 0x31 | 49 | +| '2' | 0x32 | 50 | +| 'x' | 0x78 | 120 | +| 'y' | 0x79 | 121 | +| '\r' | 0x0d | 13 | +| '\n' | 0x0a | 10 | + +### IEEE-754 Common Angles + +| Angle | IEEE-754 Hex | Little-Endian Bytes | +| ----- | ------------ | ------------------- | +| 0.0 | 0x00000000 | 00 00 00 00 | +| 30.0 | 0x41f00000 | 00 00 f0 41 | +| 45.0 | 0x42340000 | 00 00 34 42 | +| 90.0 | 0x42b40000 | 00 00 b4 42 | +| 135.0 | 0x43070000 | 00 00 07 43 | +| 180.0 | 0x43340000 | 00 00 34 43 | + +### ARM Thumb NOP Encodings + +| Instruction | Encoding | Size | +| ----------- | ------------- | ------- | +| `nop` | `00 bf` | 2 bytes | +| `nop.w` | `00 f0 00 80` | 4 bytes | + +### RP2350 Key Addresses + +| Address | Peripheral | +| ------------ | ---------- | +| `0x40070000` | UART0 | +| `0x40078000` | UART1 | +| `0x40050000` | PWM | + +--- + +## Real-World Implications + +### Why Stealth Commands Matter + +The ability to create hidden commands has serious implications: + +**Legitimate Uses:** +- Factory test modes +- Debugging interfaces +- Emergency recovery features + +**Malicious Uses:** +- Backdoors in firmware +- Hidden surveillance features +- Unauthorized control of systems + +### Real-World Example + +Imagine a drone with hacked firmware: +- Normal keys ('1', '2') control it visibly with logging +- Hidden keys ('x', 'y') control it with NO log entries +- An attacker could operate the drone while security monitors show nothing + +### The Nuclear Fuel Rod Analogy + +A fast-moving servo is like a nuclear fuel rod: +- Both are small components with immense power +- Both require precise control to prevent damage +- Both can "go critical" if pushed beyond limits +- Both teach the importance of safety margins + +--- + +**Remember:** The techniques you learned today demonstrate how conditional logic can be manipulated at the binary level. Understanding these attacks helps us build more secure embedded systems. Always use your skills ethically and responsibly! + +Happy hacking! diff --git a/WEEK10/slides/WEEK10-IMG00.svg b/WEEK10/slides/WEEK10-IMG00.svg new file mode 100644 index 0000000..84c608d --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 10 + + +Conditionals in Embedded Systems: +Debugging and Hacking Static & Dynamic +Conditionals w/ SG90 Servo Motor PWM + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK10/slides/WEEK10-IMG01.svg b/WEEK10/slides/WEEK10-IMG01.svg new file mode 100644 index 0000000..38d8eb0 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG01.svg @@ -0,0 +1,82 @@ + + + + +Conditionals Overview +Static vs Dynamic Decision Making + + + +What Are Conditionals? +Structures that let programs choose +different paths based on conditions + + + +Static Conditional +Value fixed at compile time + + +int choice = 1; +// never changes +if (choice == 1) +printf("1"); +// always runs +else printf("2"); +// never runs + + + +Dynamic Conditional +Value changes at runtime + + +choice = getchar(); +// user types a key +if (choice == '1') +printf("1"); +// maybe runs + + + +if/else + +Feature +Description + +Condition +Any boolean expr +Values +Ranges, complex logic +Fall-through +No +Best for +2-3 conditions + + + +switch/case + +Feature +Description + +Condition +Single variable +Values +Discrete only +Fall-through +Yes (no break) +Best for +Many conditions + diff --git a/WEEK10/slides/WEEK10-IMG02.svg b/WEEK10/slides/WEEK10-IMG02.svg new file mode 100644 index 0000000..cbd97b4 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG02.svg @@ -0,0 +1,89 @@ + + + + +Static Conditionals +Fixed Outcome -- Same Path Every Time + + + +Static Code Pattern + +int choice = 1; +// NEVER changes +while (true) { +if (choice == 1) +printf("1"); +else if (choice == 2) +printf("2"); +// dead code +else + + + +Execution Flow + + +choice == 1? + + +YES + + +print "1" + +NO (never taken) + + + +choice == 2? + +NO (never reached) + + +print "?" + +Only ONE path ever executes! + + + +Every Loop Iteration (Always the Same) +1. if(1==1) --> TRUE +2. print "1" +3. switch(1) case 1 +4. print "one" +5. servo 0deg +6. sleep 500ms +7. servo 180deg + + + +Serial Output (Forever) + +1 +one +1 +// repeats forever + + + +Servo Motion (Forever) +0deg +--> +180deg +--> +0deg +Sweeps back and forth, 500ms each +Continuous, predictable motion + diff --git a/WEEK10/slides/WEEK10-IMG03.svg b/WEEK10/slides/WEEK10-IMG03.svg new file mode 100644 index 0000000..9a22eeb --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG03.svg @@ -0,0 +1,95 @@ + + + + +Dynamic Conditionals +Runtime Input Changes the Path + + + +Dynamic Code Pattern + +uint8_t choice = 0; +while (true) { +choice = getchar(); +// waits for keyboard input +if (choice == 0x31) +printf("1"); +else if (choice == 0x32) +printf("2"); + + + +Execution Flow + + +choice = getchar() + + + + +choice=='1'? + +YES + +servo 0-180 + + +NO + + +choice=='2'? + +YES + +servo 180-0 + + + +print "??" +Each iteration can take a DIFFERENT path + + + +getchar() Returns ASCII +'1' = 0x31 +'2' = 0x32 +'x' = 0x78 +'y' = 0x79 +Blocks until +keypress + + + +Input --> Behavior + +Key +Output +Servo + +'1' +"1" + "one" +0deg --> 180deg +'2' +"2" + "two" +180deg --> 0deg + + + +Two Projects +0x001d_static-conditionals +choice = 1 (fixed) +0x0020_dynamic-conditionals +choice = getchar() (user input) + diff --git a/WEEK10/slides/WEEK10-IMG04.svg b/WEEK10/slides/WEEK10-IMG04.svg new file mode 100644 index 0000000..a405f7e --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG04.svg @@ -0,0 +1,96 @@ + + + + +PWM Basics +Pulse Width Modulation for Servo Control + + + +What is PWM? +Rapidly switching a signal ON and OFF +Ratio of on-time to off-time controls power + + +HIGH + + + + + + + + +ON +OFF +ON +OFF + + + +Servo PWM (50Hz = 20ms period) + + +0deg (1ms pulse): + + + +1ms HIGH +19ms LOW + + +90deg (1.5ms pulse): + + + +1.5ms HIGH +18.5ms LOW + + +180deg (2ms pulse): + + + +2ms HIGH +18ms LOW + +Pulse WIDTH determines angle, not duty cycle +Total period always 20ms (50Hz) + + + +Angle to Pulse Width + +Angle +Pulse +Ticks (1MHz) + +0deg +1000us +1000 +90deg +1500us +1500 +180deg +2000us +2000 + + + +Formula +pulse = 1000 + (angle/180) x 1000 +Example for 90deg: +1000 + (90/180) x 1000 += 1500us = 1500 ticks + diff --git a/WEEK10/slides/WEEK10-IMG05.svg b/WEEK10/slides/WEEK10-IMG05.svg new file mode 100644 index 0000000..9499ded --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG05.svg @@ -0,0 +1,83 @@ + + + + +PWM Timing Chain +150MHz System Clock to 50Hz Servo Signal + + + +Clock Division + + +150 MHz Clock + +/ 150 + + + + +1 MHz PWM + +1 tick = 1us + + + +Step 1: 150,000,000 / 150 = 1,000,000 Hz +Each PWM tick = exactly 1 microsecond + +Step 2: Wrap at 20,000 ticks = 20ms = 50Hz +Wrap value = 19,999 + + + +SG90 Servo Motor + +Parameter +Value + +Voltage +4.8V - 6V (use 5V) +Rotation +0deg to 180deg +Pulse Width +1000us - 2000us +Frequency +50Hz (20ms period) + + + +Wiring to Pico 2 + + +Pico + + +SG90 + + + + +GPIO 6 = Signal (Orange) +VBUS 5V = VCC (Red) +GND = GND (Brown) +Add 1000uF capacitor on power! + + + +Power Safety +NEVER use 3.3V pin for servo! +Servos draw 650mA+ (spikes to 1A) +Use VBUS (5V from USB) with 1000uF 25V capacitor + diff --git a/WEEK10/slides/WEEK10-IMG06.svg b/WEEK10/slides/WEEK10-IMG06.svg new file mode 100644 index 0000000..bb7867d --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG06.svg @@ -0,0 +1,55 @@ + + + + +Static Source Code +0x001d_static-conditionals.c + + + +Full Source + + +#include <stdio.h> +#include "pico/stdlib.h" +#include "servo.h" +#define SERVO_GPIO 6 + +int main(void) { +stdio_init_all(); +int choice = 1; +// STATIC! +servo_init(SERVO_GPIO); + +while (true) { +if (choice == 1) +printf("1\r\n"); +else if (choice == 2) +printf("2\r\n"); +// dead code + + + +switch Block + +switch(choice) { +case 1: puts("one"); break; + + +Servo Loop + +servo_set_angle(0.0f); +sleep_ms(500); +// then 180 + diff --git a/WEEK10/slides/WEEK10-IMG07.svg b/WEEK10/slides/WEEK10-IMG07.svg new file mode 100644 index 0000000..edb9558 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG07.svg @@ -0,0 +1,50 @@ + + + + +Dynamic Source Code +0x0020_dynamic-conditionals.c + + + +Full Source + + +#include <stdio.h> +#include "pico/stdlib.h" +#include "servo.h" +#define SERVO_GPIO 6 + +int main(void) { +stdio_init_all(); +uint8_t choice = 0; +// DYNAMIC! +servo_init(SERVO_GPIO); + +while (true) { +choice = getchar(); +// wait for input +if (choice == 0x31) +// '1' +printf("1\r\n"); +else if (choice == 0x32) +// '2' +printf("2\r\n"); + + + +switch Block (with servo control) +case '1': print "one", servo 0-->180, sleep 500ms +case '2': print "two", servo 180-->0, sleep 500ms + diff --git a/WEEK10/slides/WEEK10-IMG08.svg b/WEEK10/slides/WEEK10-IMG08.svg new file mode 100644 index 0000000..295c649 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG08.svg @@ -0,0 +1,101 @@ + + + + +Branch Instructions +How Conditionals Become Assembly + + + +ARM Branch Instructions + +Instr +Meaning +Condition + + +b +Branch always +Always + +beq +Branch if Equal +Z flag set + +bne +Branch if != +Z flag clear + +bgt +Branch if > +Signed > + +blt +Branch if < +Signed < + + + +C --> Assembly + +C code: + +if (choice == 0x31) +printf("1"); + +Assembly: + +cmp r4, #0x31 +// compare +bne skip_printf +// skip if != + + + +Conditional Branch Flow + + +cmp r4, #0x31 + + + + +beq target_addr + + +r4==0x31: JUMP + + +r4!=0x31: continue next + +cmp sets CPU flags, branch reads them + + + +NOP (No Operation) +ARM Thumb NOP: +00 bf +2 bytes +Wide NOP: +00 f0 00 80 +Replaces 4-byte bl instruction + + + +Hacking Branches +Change branch target addr +Redirect program flow +NOP out instructions +Erase code silently + diff --git a/WEEK10/slides/WEEK10-IMG09.svg b/WEEK10/slides/WEEK10-IMG09.svg new file mode 100644 index 0000000..68d47e9 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG09.svg @@ -0,0 +1,83 @@ + + + + +Hacking Conditionals +Strings, Timing, Stealth Commands + + + +Hack 1: Change Strings +Change "1" to "2": +0x31 +--> +0x32 +"one" to "fun": +6f 6e 65 +--> +66 75 6e + + + +Hack 2: Speed Up Servo +Change sleep_ms delay: +0x1F4 (500ms) +--> +0x064 (100ms) + + + +Hack 3: Stealth Commands +Hidden keys move servo with NO output + +Patch +Original +Hacked +Purpose + + +Compare 1 +#0x31 ('1') +#0x78 ('x') +New trigger key + +Compare 2 +#0x32 ('2') +#0x79 ('y') +New trigger key + +puts calls +bl puts +00 bf 00 bf +NOP out prints + + + +Hack 4: Change Angle +180.0f --> 30.0f: +00 00 34 43 +--> +00 00 f0 41 + + + +Stealth Result +'1','2': normal output + servo +'x','y': NO output, servo moves + + + +Workflow +Patch bytes in Ghidra --> export .bin --> convert to UF2 --> flash to Pico + diff --git a/WEEK10/slides/WEEK10-IMG10.svg b/WEEK10/slides/WEEK10-IMG10.svg new file mode 100644 index 0000000..eb92463 --- /dev/null +++ b/WEEK10/slides/WEEK10-IMG10.svg @@ -0,0 +1,85 @@ + + + + +PWM & Servo Hacking +Conditionals, PWM, Servo, and Hacking + + + +Static vs Dynamic + +Static +choice = 1 (fixed) +Same path every iteration +Compiler may optimize + +Dynamic +choice = getchar() +Different paths at runtime + + + +PWM for Servos +150MHz / 150 = 1MHz tick +Wrap 20000 = 50Hz (20ms) +0deg=1000us 90deg=1500us 180deg=2000us +pulse = 1000 + (angle/180) x 1000 + + + +Branch Instructions +cmp r4, #0x31 +Compare +beq target +Jump if equal +bne target +Jump if not equal +NOP = 00 bf (erase code) + + + +Key Values +0x10000234 +main() +0x40070000 +UART0 +0x1F4 +500 (sleep_ms) +0x43340000 +180.0f IEEE-754 + + + +4 Hack Types Applied +String +"one"-->"fun" +Timing +500ms-->100ms +Stealth +NOP out prints +Angle +180.0f-->30.0f + + + +Projects +0x001d_static-conditionals +0x0020_dynamic-conditionals + + +IEEE-754 Angles +0.0f=00000000 90.0f=42b40000 +180.0f=43340000 30.0f=41f00000 + diff --git a/WEEK11/WEEK11-SLIDES.pdf b/WEEK11/WEEK11-SLIDES.pdf new file mode 100644 index 0000000..2e9a7c6 Binary files /dev/null and b/WEEK11/WEEK11-SLIDES.pdf differ diff --git a/WEEK11/WEEK11.md b/WEEK11/WEEK11.md new file mode 100644 index 0000000..577a89a --- /dev/null +++ b/WEEK11/WEEK11.md @@ -0,0 +1,1567 @@ +# Week 11: Structures and Functions in Embedded Systems: Debugging and Hacking w/ IR Remote Control and NEC Protocol Basics + +## What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Understand C structures (structs) and how they organize related data +- Know how structs are represented in memory and assembly code +- Understand the NEC infrared (IR) protocol for remote control communication +- Create and use functions with parameters and return values +- Identify struct member access patterns in Ghidra +- Recognize how compilers "flatten" structs into individual operations +- Hack GPIO pin assignments to swap LED behavior +- Understand the security implications of log/behavior desynchronization +- Analyze .elf files in addition to .bin files in Ghidra + +--- + +## Part 1: Understanding C Structures (Structs) + +### What is a Struct? + +A **structure** (or **struct**) is a user-defined data type that groups related variables together under one name. Think of it like a form with multiple fields - each field can hold different types of data, but they all belong together. + +```c +// Define a struct type +typedef struct { + uint8_t led1_pin; // GPIO pin for LED 1 + uint8_t led2_pin; // GPIO pin for LED 2 + uint8_t led3_pin; // GPIO pin for LED 3 + bool led1_state; // Is LED 1 on? + bool led2_state; // Is LED 2 on? + bool led3_state; // Is LED 3 on? +} simple_led_ctrl_t; +``` + +``` ++-----------------------------------------------------------------+ +| Structure as a Container | +| | +| simple_led_ctrl_t leds | +| +-------------------------------------------------------------+| +| | led1_pin: 16 led2_pin: 17 led3_pin: 18 || +| | +--------+ +--------+ +--------+ || +| | | 16 | | 17 | | 18 | || +| | +--------+ +--------+ +--------+ || +| | || +| | led1_state: false led2_state: false led3_state: false || +| | +--------+ +--------+ +--------+ || +| | | false | | false | | false | || +| | +--------+ +--------+ +--------+ || +| +-------------------------------------------------------------+| +| | +| All 6 members live together as ONE variable called "leds" | +| | ++-----------------------------------------------------------------+ +``` + +### Why Use Structs? + +| Without Structs (Messy!) | With Structs (Clean!) | +| -------------------------- | --------------------------- | +| `uint8_t led1_pin = 16;` | `simple_led_ctrl_t leds;` | +| `uint8_t led2_pin = 17;` | `leds.led1_pin = 16;` | +| `uint8_t led3_pin = 18;` | `leds.led2_pin = 17;` | +| `bool led1_state = false;` | `leds.led3_pin = 18;` | +| `bool led2_state = false;` | `leds.led1_state = false;` | +| `bool led3_state = false;` | ... (all in one container!) | + +**Benefits of Structs:** +1. **Organization** - Related data stays together +2. **Readability** - Code is easier to understand +3. **Maintainability** - Changes are easier to make +4. **Scalability** - Easy to add more LEDs or features +5. **Passing to Functions** - Pass one struct instead of many variables + +--- + +## Part 2: Struct Memory Layout + +### How Structs are Stored in Memory + +When you create a struct, the compiler places each member in consecutive memory locations: + +``` ++-----------------------------------------------------------------+ +| Memory Layout of simple_led_ctrl_t | +| | +| Address Member Size Value | +| ------------------------------------------------------------- | +| 0x2000000 led1_pin 1 byte 16 (0x10) | +| 0x2000001 led2_pin 1 byte 17 (0x11) | +| 0x2000002 led3_pin 1 byte 18 (0x12) | +| 0x2000003 led1_state 1 byte 0 (false) | +| 0x2000004 led2_state 1 byte 0 (false) | +| 0x2000005 led3_state 1 byte 0 (false) | +| | +| Total struct size: 6 bytes | +| | ++-----------------------------------------------------------------+ +``` + +### Accessing Struct Members + +Use the **dot operator** (`.`) to access members: + +```c +simple_led_ctrl_t leds; + +// Set values +leds.led1_pin = 16; +leds.led1_state = true; + +// Read values +printf("Pin: %d\n", leds.led1_pin); +``` + +### Pointer to Struct (Arrow Operator) + +When you have a **pointer** to a struct, use the **arrow operator** (`->`): + +```c +simple_led_ctrl_t leds; +simple_led_ctrl_t *ptr = &leds; // Pointer to the struct + +// These are equivalent: +leds.led1_pin = 16; // Using dot with struct variable +ptr->led1_pin = 16; // Using arrow with pointer +(*ptr).led1_pin = 16; // Dereferencing then dot (same thing) +``` + +``` ++-----------------------------------------------------------------+ +| Dot vs Arrow Operator | +| | +| struct_variable.member <-- Use with actual struct | +| | +| pointer_to_struct->member <-- Use with pointer to struct | +| | +| The arrow (->) is shorthand for (*pointer).member | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 3: Designated Initializers + +### Clean Struct Initialization + +C allows you to initialize struct members by name using **designated initializers**: + +```c +simple_led_ctrl_t leds = { + .led1_pin = 16, + .led2_pin = 17, + .led3_pin = 18, + .led1_state = false, + .led2_state = false, + .led3_state = false +}; +``` + +**Benefits:** +- Clear which value goes to which member +- Order doesn't matter (can rearrange lines) +- Self-documenting code +- Easy to add new members later + +--- + +## Part 4: Understanding the NEC IR Protocol + +### What is Infrared (IR) Communication? + +**Infrared** communication uses invisible light pulses to send data. Your TV remote uses IR to send commands to your TV. The LED in the remote flashes on and off very quickly in specific patterns that represent different buttons. + +``` ++-----------------------------------------------------------------+ +| IR Communication | +| | +| Remote Control IR Receiver | +| +----------+ +----------+ | +| | Button | | | | +| | 1 | --- IR Light Pulses --- | ++ | | +| | +---+ | ~~~~~~~~~~~~> | Sensor | | +| | | Tx | | | | | +| | +---+ | +----+-----+ | +| | IR LED | | | +| +----------+ v | +| GPIO Pin | +| (Digital signal) | +| | ++-----------------------------------------------------------------+ +``` + +### The NEC Protocol + +**NEC** is one of the most common IR protocols. When you press a button, the remote sends: + +1. **Leader pulse** - 9ms HIGH, 4.5ms LOW (says "attention!") +2. **Address** - 8 bits identifying the device +3. **Address Inverse** - 8 bits (for error checking) +4. **Command** - 8 bits for the button pressed +5. **Command Inverse** - 8 bits (for error checking) + +``` ++-----------------------------------------------------------------+ +| NEC Protocol Frame | +| | +| +---------+---------+---------+---------+---------+---------+ | +| | Leader | Address | Address | Command | Command | Stop | | +| | Pulse | 8-bit | Inverse | 8-bit | Inverse | Bit | | +| | 9+4.5ms | | 8-bit | | 8-bit | | | +| +---------+---------+---------+---------+---------+---------+ | +| | +| Total: 32 bits of data (+ leader + stop) | +| | ++-----------------------------------------------------------------+ +``` + +### NEC Command Codes for Our Remote + +| Button | NEC Command Code | Hex Value | +| ------ | ---------------- | --------- | +| 1 | 0x0C | 12 | +| 2 | 0x18 | 24 | +| 3 | 0x5E | 94 | + +**Note:** Different remotes have different codes. These are specific to our example remote. + +--- + +## Part 5: Understanding Functions in C + +### What is a Function? + +A **function** is a reusable block of code that performs a specific task. Functions help organize code and avoid repetition. + +```c +// Function definition +int add_numbers(int a, int b) { + return a + b; +} + +// Function call +int result = add_numbers(5, 3); // result = 8 +``` + +### Function Components + +``` ++-----------------------------------------------------------------+ +| Anatomy of a Function | +| | +| return_type function_name ( parameters ) { | +| // function body | +| return value; | +| } | +| | +| Example: | +| +-------------------------------------------------------------+| +| | int ir_to_led_number ( int ir_command ) { || +| | --- --------------- --------------- || +| | | | | || +| | | | +-- Parameter (input) || +| | | +-- Function name || +| | +-- Return type (what it gives back) || +| | || +| | if (ir_command == 0x0C) return 1; <-- Body || +| | if (ir_command == 0x18) return 2; || +| | return 0; <-- Return value || +| | } || +| +-------------------------------------------------------------+| +| | ++-----------------------------------------------------------------+ +``` + +### Types of Functions + +| Type | Description | Example | +| ---------------------------- | ------------------------- | ---------------------------- | +| **No params, no return** | Just does something | `void leds_all_off(void)` | +| **With params, no return** | Takes input, no output | `void blink_led(pin, count)` | +| **No params, with return** | No input, gives output | `int ir_getkey(void)` | +| **With params, with return** | Takes input, gives output | `int ir_to_led_number(cmd)` | + +--- + +## Part 6: Functions with Struct Pointers + +### Passing Structs to Functions + +When passing a struct to a function, you usually pass a **pointer** to avoid copying all the data: + +```c +// Function takes a POINTER to the struct +void leds_all_off(simple_led_ctrl_t *leds) { + gpio_put(leds->led1_pin, false); // Use arrow operator! + gpio_put(leds->led2_pin, false); + gpio_put(leds->led3_pin, false); +} + +// Call with address-of operator +simple_led_ctrl_t my_leds; +leds_all_off(&my_leds); // Pass the ADDRESS of my_leds +``` + +``` ++-----------------------------------------------------------------+ +| Passing Struct by Pointer | +| | +| main() { | +| simple_led_ctrl_t leds; <-- Struct lives here | +| leds_all_off(&leds); <-- Pass ADDRESS (pointer) | +| } | | +| | | +| v | +| leds_all_off(simple_led_ctrl_t *leds) { | +| gpio_put(leds->led1_pin, false); | +| ---- | +| | | +| +-- Arrow because leds is a POINTER | +| } | +| | +| WHY use pointers? | +| - Efficient: Only 4 bytes (address) instead of entire struct | +| - Allows modification: Function can change the original | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 7: How Compilers Handle Structs + +### Struct "Flattening" in Assembly + +When the compiler converts your C code to assembly, it "flattens" struct operations into individual memory accesses: + +**C Code:** +```c +gpio_init(leds.led1_pin); // leds.led1_pin = 16 +gpio_init(leds.led2_pin); // leds.led2_pin = 17 +gpio_init(leds.led3_pin); // leds.led3_pin = 18 +``` + +**Assembly (what the compiler produces):** +```assembly +movs r0, #0x10 ; r0 = 16 (led1_pin value) +bl gpio_init ; call gpio_init(16) + +movs r0, #0x11 ; r0 = 17 (led2_pin value) +bl gpio_init ; call gpio_init(17) + +movs r0, #0x12 ; r0 = 18 (led3_pin value) +bl gpio_init ; call gpio_init(18) +``` + +``` ++-----------------------------------------------------------------+ +| Struct Flattening | +| | +| C Level (High-level abstraction): | +| +-------------------------------------------------------------+| +| | gpio_init(leds.led1_pin); || +| | gpio_init(leds.led2_pin); || +| | gpio_init(leds.led3_pin); || +| +-------------------------------------------------------------+| +| | | +| | Compiler transforms | +| v | +| Assembly Level (Flattened): | +| +-------------------------------------------------------------+| +| | movs r0, #16 ; Just the VALUE, no struct reference || +| | bl gpio_init || +| | movs r0, #17 ; Next value directly || +| | bl gpio_init || +| | movs r0, #18 ; Next value directly || +| | bl gpio_init || +| +-------------------------------------------------------------+| +| | +| The struct abstraction DISAPPEARS at the assembly level! | +| We just see individual values being loaded and used. | +| | ++-----------------------------------------------------------------+ +``` + +### Why This Matters for Reverse Engineering + +- In Ghidra, you won't always see "struct" - just individual values +- You must recognize PATTERNS (sequential values like 16, 17, 18) +- Understanding flattening helps you reconstruct the original struct + +--- + +## Part 8: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. Ghidra installed (for static analysis) +4. Python installed (for UF2 conversion) +5. A serial monitor (PuTTY, minicom, or screen) +6. An IR receiver module (like VS1838B) +7. An IR remote control (any NEC-compatible remote) +8. Three LEDs (red, green, yellow) with resistors +9. The sample projects: `0x0023_structures` and `0x0026_functions` + +### Hardware Setup + +**IR Receiver Wiring:** + +| IR Receiver Pin | Pico 2 Pin | +| --------------- | ---------- | +| VCC | 3.3V | +| GND | GND | +| OUT/DATA | GPIO 5 | + +**LED Wiring:** + +| LED | GPIO Pin | Resistor | +| ------ | -------- | --------- | +| Red | GPIO 16 | 220-330 ohm | +| Green | GPIO 17 | 220-330 ohm | +| Yellow | GPIO 18 | 220-330 ohm | + +``` ++-----------------------------------------------------------------+ +| Complete Wiring Diagram | +| | +| Pico 2 Components | +| +----------+ | +| | | +-------------+ | +| | GPIO 5 |--------------+ IR Receiver | | +| | | | (VS1838B) | | +| | | +------+------+ | +| | | | | +| | GPIO 16 |---[220 ohm]---(RED LED)----+ | +| | | | | +| | GPIO 17 |---[220 ohm]---(GRN LED)----+ | +| | | | | +| | GPIO 18 |---[220 ohm]---(YEL LED)----+ | +| | | | | +| | 3.3V |-------------------------+-- IR VCC | +| | | | | +| | GND |-------------------------+-- All GNDs | +| | | | +| +----------+ | +| | ++-----------------------------------------------------------------+ +``` + +### Project Structure + +``` +Embedded-Hacking/ ++-- 0x0023_structures/ +| +-- build/ +| | +-- 0x0023_structures.uf2 +| | +-- 0x0023_structures.bin +| +-- main/ +| | +-- 0x0023_structures.c +| +-- ir.h ++-- 0x0026_functions/ +| +-- build/ +| | +-- 0x0026_functions.uf2 +| | +-- 0x0026_functions.bin +| | +-- 0x0026_functions.elf +| +-- main/ +| | +-- 0x0026_functions.c +| +-- ir.h ++-- uf2conv.py +``` + +--- + +## Part 9: Hands-On Tutorial - Structures Code + +### Step 1: Review the Source Code + +Let's examine the structures code: + +**File: `0x0023_structures.c`** + +```c +#include +#include +#include "pico/stdlib.h" +#include "ir.h" + +#define IR_PIN 5 + +typedef struct { + uint8_t led1_pin; + uint8_t led2_pin; + uint8_t led3_pin; + bool led1_state; + bool led2_state; + bool led3_state; +} simple_led_ctrl_t; + +int main(void) { + stdio_init_all(); + + simple_led_ctrl_t leds = { + .led1_pin = 16, + .led2_pin = 17, + .led3_pin = 18, + .led1_state = false, + .led2_state = false, + .led3_state = false + }; + + gpio_init(leds.led1_pin); gpio_set_dir(leds.led1_pin, GPIO_OUT); + gpio_init(leds.led2_pin); gpio_set_dir(leds.led2_pin, GPIO_OUT); + gpio_init(leds.led3_pin); gpio_set_dir(leds.led3_pin, GPIO_OUT); + + ir_init(IR_PIN); + printf("IR receiver on GPIO %d ready\n", IR_PIN); + + while (true) { + int key = ir_getkey(); + if (key >= 0) { + printf("NEC command: 0x%02X\n", key); + + // Turn all off first + leds.led1_state = false; + leds.led2_state = false; + leds.led3_state = false; + + // Check NEC codes + if (key == 0x0C) leds.led1_state = true; // GPIO16 + if (key == 0x18) leds.led2_state = true; // GPIO17 + if (key == 0x5E) leds.led3_state = true; // GPIO18 + + // Apply states + gpio_put(leds.led1_pin, leds.led1_state); + gpio_put(leds.led2_pin, leds.led2_state); + gpio_put(leds.led3_pin, leds.led3_state); + + sleep_ms(10); + } else { + sleep_ms(1); + } + } +} +``` + +### Step 2: Understand the Program Flow + +``` ++-----------------------------------------------------------------+ +| Program Flow | +| | +| 1. Initialize UART (stdio_init_all) | +| 2. Create LED struct with pins 16, 17, 18 | +| 3. Initialize GPIO pins as outputs | +| 4. Initialize IR receiver on GPIO 5 | +| 5. Enter infinite loop: | +| a. Check for IR key press | +| b. If key received: | +| - Print the NEC command code | +| - Turn all LEDs off | +| - Check which button: 0x0C, 0x18, or 0x5E | +| - Turn on the matching LED | +| - Apply states to GPIO pins | +| c. Sleep briefly and repeat | +| | ++-----------------------------------------------------------------+ +``` + +### Step 3: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0023_structures.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 4: Verify It's Working + +**Open PuTTY (115200 baud) and test:** +- Press "1" on remote -> Red LED lights, terminal shows `NEC command: 0x0C` +- Press "2" on remote -> Green LED lights, terminal shows `NEC command: 0x18` +- Press "3" on remote -> Yellow LED lights, terminal shows `NEC command: 0x5E` + +--- + +## Part 10: Debugging with GDB (Structures) + +### Step 5: Start OpenOCD (Terminal 1) + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe. + +### Step 6: Start GDB (Terminal 2) + +Open a **new terminal** and launch GDB with the binary: + +```cmd +arm-none-eabi-gdb build\0x0023_structures.elf +``` + +### Step 7: Connect to the Remote Target + +In GDB, connect to OpenOCD: + +```gdb +target extended-remote :3333 +``` + +### Step 8: Halt the Running Binary + +Stop the processor: + +```gdb +monitor halt +``` + +### Step 9: Examine Main Function + +Disassemble around main to see struct initialization: + +```gdb +disassemble 0x10000234,+200 +``` + +Look for the struct member initialization sequence (mov instructions with values 16, 17, 18). + +### Step 10: Set a Breakpoint at Main + +```gdb +break *0x10000234 +``` + +Reset and run to hit the breakpoint: + +```gdb +monitor reset halt +continue +``` + +### Step 11: Observe the Flattened Struct + +Instead of allocating the struct on the stack, the compiler completely **flattened and optimized** it out! There is no `sub sp` instruction for the struct. + +Examine the `main` disassembly again: + +```gdb +disassemble 0x10000234,+200 +``` + +Notice how values 16, 17, and 18 are just loaded directly into registers (`movs r0, #16`, etc.) and passed to functions. The struct abstraction is entirely gone. + +### Step 12: Watch GPIO Initialization + +Set a breakpoint on the first `gpio_init` call and watch the LED pin get initialized: + +```gdb +break *0x1000023c +continue +info registers r0 +``` + +You should see `r0 = 16`. If you set breakpoints on the subsequent calls (`0x1000024c`, `0x10000258`) and continue, you will see `17` and `18`. + +### Step 13: Examine IR Key Processing + +Because `ir_getkey` is polled in a loop and returns `-1` constantly when no key is pressed, breaking at the immediate return (`0x10000274`) will just flood you with `-1`s! + +Instead, set a breakpoint *inside* the `if (key >= 0)` block at `0x10000278`. This is right after the `blt.n` conditional branch that skips over the processing logic when no key is pressed: + +```gdb +break *0x10000278 +continue +``` + +Now press a button on the remote. The program will halt. At this point, the key value has been moved into `r4` (from the `subs r4, r0, #0` instruction earlier): + +```gdb +info registers r4 +``` + +You'll see the decimal value of the NEC code (e.g., 12 for `0x0C`, 24 for `0x18`, or 94 for `0x5E`). + +### Step 14: Watch the Conditional Checks + +Let's look at the comparisons where the code checks which button was pressed: + +```gdb +break *0x10000280 +continue +``` + +*(Note: If the program doesn't halt immediately, press a button on the remote to trigger the breakpoint!)* + +```gdb +x/8i $pc +``` + +Notice the compiler uses decimal comparisons: `cmp r4, #12` (which is `0x0c`), `cmp r4, #24` (which is `0x18`), and a subtraction trick `sub.w r4, r4, #94` (which is `0x5e`) to determine which button was pressed. + +### Step 15: Observe Inlined GPIO Operations + +The compiler even inlined the `gpio_put` calls! First, let's clear any old breakpoints so we don't accidentally stop somewhere else (like you might have seen if you hit an old breakpoint first!): + +```gdb +delete +break *0x10000298 +continue +``` + +*(Note: Again, press a button on the remote if it doesn't halt immediately!)* + +Now check the registers: + +```gdb +info registers r1 r2 r3 r4 +``` + +Depending on which button you pressed, one of the state registers will be `1` (ON) and the others will be `0` (OFF): +- `r1` = State for the **Red** LED (GPIO 16) +- `r3` = State for the **Green** LED (GPIO 17) +- `r4` = State for the **Yellow** LED (GPIO 18) +- `r2` = `16` (`0x10`), which is the starting pin number being written to. + +*(Note: Since you are paused here, the physical LEDs haven't updated yet! They will only change after these `mcrr` instructions execute.)* + +Instead of branching to `gpio_put`, it uses direct hardware instructions (disassembled as `mcrr` on the RP2350) to write the states directly to the GPIO hardware. + +### Step 16: Exit GDB + +When done exploring: + +```gdb +quit +``` + +--- + +## Part 11: Setting Up Ghidra for Structures + +### Step 17: Start Ghidra + +Open a terminal and type: + +```cmd +ghidraRun +``` + +### Step 18: Create a New Project + +1. Click **File** -> **New Project** +2. Select **Non-Shared Project** +3. Click **Next** +4. Enter Project Name: `0x0023_structures` +5. Click **Finish** + +### Step 19: Import the Binary + +1. Navigate to the `0x0023_structures/build/` folder +2. **Drag and drop** the `.bin` file into Ghidra's project window + +### Step 20: Configure the Binary Format + +**Click the three dots (...) next to "Language" and:** +1. Search for "Cortex" +2. Select **ARM Cortex 32 little endian default** +3. Click **OK** + +**Click the "Options..." button and:** +1. Change **Block Name** to `.text` +2. Change **Base Address** to `10000000` +3. Click **OK** + +### Step 21: Analyze the Binary + +1. Double-click on the file in the project window +2. A dialog asks "Analyze now?" - Click **Yes** +3. Use default analysis options and click **Analyze** + +Wait for analysis to complete. + +--- + +## Part 12: Resolving Functions - Structures Project + +### Step 22: Navigate to Main + +1. Press `G` (Go to address) and type `10000234` +2. Right-click -> **Edit Function Signature** +3. Change to: `int main(void)` +4. Click **OK** + +### Step 23: Resolve stdio_init_all + +At address `0x10000236`: + +1. Double-click on the called function +2. Right-click -> **Edit Function Signature** +3. Change to: `bool stdio_init_all(void)` +4. Click **OK** + +### Step 24: Identify gpio_init from Struct Pattern + +Look for three consecutive calls with values 16, 17, 18: + +```assembly +1000023a 10 20 movs r0,#0x10 +1000023c 00 f0 8c f9 bl FUN_10000558 ; gpio_init + +1000024a 11 20 movs r0,#0x11 +1000024c 00 f0 84 f9 bl FUN_10000558 ; gpio_init + +10000256 12 20 movs r0,#0x12 +10000258 00 f0 7e f9 bl FUN_10000558 ; gpio_init +``` + +This pattern reveals the struct members! Update the function signature: +1. Right-click on `FUN_10000558` -> **Edit Function Signature** +2. Change to: `void gpio_init(uint gpio)` +3. Click **OK** + +### Step 25: Resolve ir_init + +Look for a function call with GPIO 5: + +```assembly +10000262 05 20 movs r0,#0x5 +10000264 00 f0 38 f8 bl FUN_100002d8 ; ir_init +``` + +1. Right-click on `FUN_100002d8` -> **Edit Function Signature** +2. Change to: `void ir_init(uint pin)` +3. Click **OK** + +### Step 26: Resolve printf + +Right after ir_init, look for the "IR receiver on GPIO" string being loaded and passed to a function: + +```assembly +1000026a 19 48 ldr r0=>s_IR_receiver_on_GPIO_%d... +1000026c 03 f0 46 f9 bl FUN_100034fc ; printf +``` + +1. Right-click on `FUN_100034fc` -> **Edit Function Signature** +2. Change to: `int printf(char *format,...)` +3. Check the **Varargs** checkbox +4. Click **OK** + +### Step 27: Resolve ir_getkey + +Look for a function that returns a value checked against conditions: + +```assembly +10000270 00 f0 46 f8 bl FUN_10000300 ; Call ir_getkey +10000274 04 1e subs r4,r0,#0x0 ; Check if >= 0 +10000276 1e db blt LAB_100002b6 ; If negative, no key pressed +``` + +1. Right-click on `FUN_10000300` -> **Edit Function Signature** +2. Change to: `int ir_getkey(void)` +3. Click **OK** + +### Step 28: Resolve sleep_ms + +Look for calls with 10 (0x0A) or 1 (0x01): + +```assembly +100002a8 0a 20 movs r0,#0xa +100002aa 00 f0 81 fe bl FUN_10000fb0 ; sleep_ms +``` + +1. Right-click on `FUN_10000fb0` -> **Edit Function Signature** +2. Change to: `void sleep_ms(uint ms)` +3. Click **OK** + +--- + +## Part 13: Recognizing Struct Patterns in Assembly + +### Step 29: Identify GPIO Set Direction + +After each `gpio_init`, look for direction setting: + +```assembly +10000240 4f f0 01 04 mov.w r4,#0x1 ; direction = output +10000244 10 23 movs r3,#0x10 ; GPIO 16 +10000246 44 ec 44 30 mcrr p0,0x4,r3,r4,cr4 ; Configure GPIO direction register +``` + +This is the compiler's heavily optimized version of `gpio_set_dir(pin, GPIO_OUT)`. + +### Step 30: Map the Struct Members + +Create a mental (or written) map: + +``` ++-----------------------------------------------------------------+ +| Struct Member Mapping | +| | +| Assembly Value -> Struct Member -> Physical LED | +| ------------------------------------------------------------- | +| 0x10 (16) -> led1_pin -> Red LED | +| 0x11 (17) -> led2_pin -> Green LED | +| 0x12 (18) -> led3_pin -> Yellow LED | +| | +| NEC Code -> State Member -> Action | +| ------------------------------------------------------------- | +| 0x0C -> led1_state=true -> Red LED ON | +| 0x18 -> led2_state=true -> Green LED ON | +| 0x5E -> led3_state=true -> Yellow LED ON | +| | ++-----------------------------------------------------------------+ +``` + +--- + +## Part 14: Hacking Structures + +### Step 31: Enable Instruction Patching + +We will use Ghidra's **Patch Instruction** feature to modify the assembly directly instead of editing raw bytes. + +### Step 32: Swap LED Pin Assignments + +We'll swap the red and green LED pins to reverse their behavior! Because the compiler fully flattened the struct, modifying the `gpio_init` pins won't actually change the main loop's behavior (since all three pins are initialized anyway). We must patch the hardcoded pins inside the **main loop** itself! + +**Find and patch the `movs` calls in the loop:** + +1. Navigate to `10000296` where the red LED pin is loaded: `movs r2,#0x10` +2. Right-click the instruction -> **Patch Instruction** (or press Ctrl+Shift+G) +3. Change `#0x10` to `#0x11` (swap red to green's pin) and press **Enter** +4. Navigate to `1000029c` where the green LED pin is loaded: `movs r2,#0x11` +5. Right-click the instruction -> **Patch Instruction** +6. Change `#0x11` to `#0x10` (swap green to red's pin) and press **Enter** + +**Before:** +``` +LED 1 (0x0C) -> GPIO 16 -> Red LED +LED 2 (0x18) -> GPIO 17 -> Green LED +``` + +**After:** +``` +LED 1 (0x0C) -> GPIO 17 -> Green LED (SWAPPED!) +LED 2 (0x18) -> GPIO 16 -> Red LED (SWAPPED!) +``` + +### Step 33: Export and Flash + +1. Click **File** -> **Export Program** +2. Set **Format** to **Raw Bytes** +3. Name: `0x0023_structures-h.bin` +4. Click **OK** + +Convert and flash: + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0023_structures +python ..\uf2conv.py build\0x0023_structures-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 34: Verify the Hack + +**Open PuTTY and test:** +- Press "1" on remote -> **GREEN** LED lights (was red!) +- Terminal still shows `NEC command: 0x0C` +- Press "2" on remote -> **RED** LED lights (was green!) +- Terminal still shows `NEC command: 0x18` + +**The log says one thing, but the hardware does another!** + +--- + +## Part 15: Security Implications - Log Desynchronization + +### The Danger of Mismatched Logs + +``` ++-----------------------------------------------------------------+ +| Log vs Reality Desynchronization | +| | +| +-----------------+ +-----------------+ | +| | Terminal Log | | Physical LEDs | | +| +-----------------+ +-----------------+ | +| | NEC: 0x0C | +------- | GREEN LED on | <-- Mismatch! | +| | (expects RED) | | (not red!) | | +| +-----------------+ +-----------------+ | +| | NEC: 0x18 | +------- | RED LED on | <-- Mismatch! | +| | (expects GREEN) | | (not green!) | | +| +-----------------+ +-----------------+ | +| | +| The OPERATOR sees correct logs but WRONG physical behavior! | +| | ++-----------------------------------------------------------------+ +``` + +### Real-World Example: Stuxnet + +**Stuxnet** was a cyberweapon that: +- Attacked Iranian nuclear centrifuges +- Made centrifuges spin at dangerous speeds +- Fed FALSE "everything normal" data to operators +- Operators saw stable readings while equipment was destroyed + +Our LED example demonstrates the same principle: +- Logs show expected behavior +- Hardware performs different actions +- Attackers can hide malicious activity + +--- + +## Part 16: Functions Project - Advanced Code + +### Step 35: Review the Functions Code + +**File: `0x0026_functions.c`** (key functions shown) + +```c +// Map IR command to LED number +int ir_to_led_number(int ir_command) { + if (ir_command == 0x0C) return 1; + if (ir_command == 0x18) return 2; + if (ir_command == 0x5E) return 3; + return 0; +} + +// Get GPIO pin for LED number +uint8_t get_led_pin(simple_led_ctrl_t *leds, int led_num) { + if (led_num == 1) return leds->led1_pin; + if (led_num == 2) return leds->led2_pin; + if (led_num == 3) return leds->led3_pin; + return 0; +} + +// Turn off all LEDs +void leds_all_off(simple_led_ctrl_t *leds) { + gpio_put(leds->led1_pin, false); + gpio_put(leds->led2_pin, false); + gpio_put(leds->led3_pin, false); +} + +// Blink an LED +void blink_led(uint8_t pin, uint8_t count, uint32_t delay_ms) { + for (uint8_t i = 0; i < count; i++) { + gpio_put(pin, true); + sleep_ms(delay_ms); + gpio_put(pin, false); + sleep_ms(delay_ms); + } +} + +// Main command processor +int process_ir_led_command(int ir_command, simple_led_ctrl_t *leds, uint8_t blink_count) { + if (!leds || ir_command < 0) return -1; + + leds_all_off(leds); + int led_num = ir_to_led_number(ir_command); + if (led_num == 0) return 0; + + uint8_t pin = get_led_pin(leds, led_num); + blink_led(pin, blink_count, 50); + gpio_put(pin, true); + + return led_num; +} +``` + +### Step 36: Understand the Function Call Chain + +``` ++-----------------------------------------------------------------+ +| Function Call Chain | +| | +| main() | +| | | +| +--> process_ir_led_command(key, &leds, 3) | +| | | +| +--> leds_all_off(&leds) | +| | +--> gpio_put() * 3 | +| | | +| +--> ir_to_led_number(ir_command) | +| | +--> returns 1, 2, or 3 | +| | | +| +--> get_led_pin(&leds, led_num) | +| | +--> returns GPIO pin number | +| | | +| +--> blink_led(pin, 3, 50) | +| | +--> gpio_put() + sleep_ms() in loop | +| | | +| +--> gpio_put(pin, true) | +| | ++-----------------------------------------------------------------+ +``` + +### Step 37: Flash and Test + +1. Flash `0x0026_functions.uf2` to your Pico 2 +2. Open PuTTY +3. Press remote buttons: + - "1" -> Red LED blinks 3 times, then stays on + - "2" -> Green LED blinks 3 times, then stays on + - "3" -> Yellow LED blinks 3 times, then stays on + +--- + +## Part 17: Debugging with GDB (Functions) + +### Step 38: Start OpenOCD (Terminal 1) + +Open a terminal and start OpenOCD: + +```powershell +openocd -s "$env:USERPROFILE\.pico-sdk\openocd\0.12.0+dev\scripts" -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000" +``` + +You should see output indicating OpenOCD connected successfully to your Pico 2 via the Debug Probe. + +### Step 39: Start GDB (Terminal 2) + +Open a **new terminal** and launch GDB with the binary: + +```cmd +arm-none-eabi-gdb build\0x0026_functions.elf +``` + +### Step 40: Connect to the Remote Target + +In GDB, connect to OpenOCD: + +```gdb +target extended-remote :3333 +``` + +### Step 41: Halt the Running Binary + +Stop the processor: + +```gdb +monitor halt +``` + +### Step 42: Examine the Function Layout + +Disassemble to see the multiple functions: + +```gdb +disassemble 0x10000234,+300 +``` + +You'll see multiple function prologues (push) and epilogues (pop) for the helper functions. + +### Step 43: Discover the Missing Functions + +The C code relies heavily on helper functions (`process_ir_led_command`, `leds_all_off`, `ir_to_led_number`, `blink_led`). But if you scroll through the disassembly, you'll notice a distinct lack of function prologues, epilogues, or `bl` calls to anything other than `sleep_ms` or `printf`! + +The compiler has aggressively **inlined** every single helper function into the main loop. + +Let's trace exactly how the compiler flattened this logic. First, set a breakpoint right when a key press is detected: + +```gdb +break *0x10000284 +continue +``` + +*(Press a button on your remote to trigger the breakpoint!)* + +### Step 44: Examine leds_all_off (Inlined) + +In the C code, `process_ir_led_command` starts by calling `leds_all_off(&leds)`. + +Let's look at the next few instructions starting from our breakpoint: +```gdb +x/8i $pc +``` + +You'll see: +```assembly +0x10000284 : mov r1, r4 +0x10000286 : ldr r0, [pc, #156] +0x10000288 : bl 0x10003554 <__wrap_printf> +0x1000028c : movs r5, #16 +0x1000028e : mcrr 0, 4, r5, r6, cr0 +0x10000292 : movs r3, #17 +0x10000294 : mcrr 0, 4, r3, r6, cr0 +0x10000298 : movs r2, #18 +``` +Because `r6` was set to `0` earlier, the compiler is just directly writing `0` to pins 16, 17, and 18 using `mcrr`. It bypassed the function call entirely! + +### Step 45: Examine ir_to_led_number (Inlined) + +Next, the C code calls `ir_to_led_number` and `get_led_pin`. Let's see how the compiler handled that by inspecting further down: + +```gdb +x/4i 0x1000029e +``` + +```assembly +0x1000029e : cmp r4, #12 +0x100002a0 : beq.n 0x100002c6 +0x100002a2 : cmp r4, #24 +0x100002a4 : beq.n 0x1000030a +``` +Instead of a separate function, it simply compares the button code in `r4` (`12`, `24`, `94`) and branches straight to the correct blinking logic! + +### Step 46: Watch the blink_led Loop + +Let's set a breakpoint where the blinking loop begins for Button 1 (which handles the Red LED on pin 16). + +```gdb +break *0x100002c6 +continue +``` + +Once halted, inspect the setup: +```gdb +x/6i $pc +``` + +```assembly +0x100002c6 : mov.w r8, #1 @ led_num = 1 +0x100002ca : movs r4, #3 @ blink_count = 3 +0x100002cc : mcrr 0, 4, r5, r7, cr0 @ Turn LED ON (r7=1) +0x100002d0 : movs r0, #50 @ Delay 50ms +0x100002d2 : bl 0x10001008 +0x100002d6 : mcrr 0, 4, r5, r6, cr0 @ Turn LED OFF (r6=0) +``` +The compiler placed the blink count in `r4` and handles the toggling with `mcrr` instructions surrounding `sleep_ms`. + +### Step 47: The Struct Pointer is a Lie + +If you were trying to find the `leds` struct pointer to examine the pins using `x/6xb`, you'd be looking forever. Because the compiler realized the struct is never passed to any external non-inlined functions, it **never bothered creating it in memory**. It simply kept track of the pin numbers (16, 17, 18) directly in registers during compilation! + +### Step 48: Observe the Loop Condition + +Continue execution until you hit the end of the blink loop: + +```gdb +break *0x100002e0 +continue +``` + +At this point, GDB is halted **right before** it performs the subtraction. If you check `r3` now, it will contain random leftover garbage (like `0x400b0000`) because the instruction hasn't run yet! Check `r4` (the current blink count): + +```gdb +info registers r4 +``` + +Now, step forward two instructions to let it actually do the math: + +```gdb +stepi 2 +info registers r3 r4 +``` + +```assembly +0x100002e0 : subs r3, r4, #1 +0x100002e2 : ands.w r4, r3, #255 +0x100002e6 : bne.n 0x100002cc +``` +You will now see `r3` become `2`, and `r4` become `2`! It successfully subtracted 1 from the blink count and stored it back. Since it hasn't hit 0 yet, the `bne.n` instruction will branch back to the start of the blink (`0x100002cc`) to flash the LED again! + +### Step 49: Exit GDB + +When done exploring: + +```gdb +quit +``` + +--- + +## Part 18: Analyzing .ELF Files in Ghidra + +### Step 50: Create New Ghidra Project + +1. Create project: `0x0026_functions` +2. Import the `.elf` file (NOT the .bin this time!) + +### Why Use .ELF Instead of .BIN? + +| Feature | .BIN File | .ELF File | +| -------------- | -------------------- | --------------------------- | +| **Symbols** | None | Function/variable names | +| **Sections** | Raw bytes only | .text, .data, .rodata, etc. | +| **Debug info** | None | May include debug symbols | +| **Size** | Smaller | Larger | +| **Use case** | Flashing to hardware | Analysis and debugging | + +### Step 51: Import and Analyze the .ELF + +1. Drag and drop the `.elf` file into Ghidra +2. Ghidra automatically detects ARM format! +3. Click **Yes** to analyze +4. Wait for analysis to complete + +### Step 52: Explore the Symbol Tree + +With .ELF files, you get more information: +1. Look at the **Symbol Tree** panel +2. Expand **Functions** - you may see named functions! +3. Expand **Labels** - data labels may appear + +--- + +## Part 19: Hacking the Functions Project + +### Step 53: Find LED Pin Values + +Look for the struct initialization pattern: + +```assembly +movs r0, #0x10 ; led1_pin = 16 +movs r0, #0x11 ; led2_pin = 17 +movs r0, #0x12 ; led3_pin = 18 +``` + +### Step 54: Swap LED 1 and LED 3 + +We'll swap the red (GPIO 16) and yellow (GPIO 18) LEDs: + +**Find and patch in the .bin file:** +1. Change `0x10` (16) to `0x12` (18) +2. Change `0x12` (18) to `0x10` (16) + +**Before:** +``` +Button 1 -> LED 1 -> GPIO 16 -> Red +Button 3 -> LED 3 -> GPIO 18 -> Yellow +``` + +**After:** +``` +Button 1 -> LED 1 -> GPIO 18 -> Yellow (SWAPPED!) +Button 3 -> LED 3 -> GPIO 16 -> Red (SWAPPED!) +``` + +### Step 55: Export the Patched .BIN + +**Important:** Even though we analyzed the .elf, we patch the .bin! + +1. Open the original `.bin` file in Ghidra (or a hex editor) +2. Apply the patches +3. Export as `0x0026_functions-h.bin` + +### Step 56: Convert and Flash + +```cmd +cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0026_functions +python ..\uf2conv.py build\0x0026_functions-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2 +``` + +### Step 57: Verify the Hack + +**Open PuTTY and test:** +- Press "1" -> **YELLOW** LED blinks (was red!) +- Terminal shows: `LED 1 activated on GPIO 16` (WRONG - it's actually GPIO 18!) +- Press "3" -> **RED** LED blinks (was yellow!) +- Terminal shows: `LED 3 activated on GPIO 18` (WRONG - it's actually GPIO 16!) + +**Again, logs don't match reality!** + +--- + +## Part 20: Summary and Review + +### What We Accomplished + +1. **Learned C structures** - Grouping related data together +2. **Understood struct memory layout** - How members are stored consecutively +3. **Mastered dot and arrow operators** - Accessing struct members +4. **Learned the NEC IR protocol** - How remotes communicate +5. **Understood functions with parameters** - Passing data in and out +6. **Saw struct flattening in assembly** - How compilers transform structs +7. **Analyzed .ELF files** - Getting more symbol information +8. **Hacked GPIO assignments** - Swapping LED behavior +9. **Discovered log desynchronization** - Security implications + +### Struct Operations Summary + +``` ++-----------------------------------------------------------------+ +| Struct Operations | +| | +| Definition: | +| typedef struct { | +| uint8_t pin; | +| bool state; | +| } led_t; | +| | +| Creation: | +| led_t led = { .pin = 16, .state = false }; | +| | +| Access (variable): led.pin | +| Access (pointer): ptr->pin or (*ptr).pin | +| | +| Passing to function: void func(led_t *led) | +| Calling: func(&led) | +| | ++-----------------------------------------------------------------+ +``` + +### Function Types Summary + +``` ++-----------------------------------------------------------------+ +| Function Patterns | +| | +| No params, no return: | +| void leds_all_off(void) | +| | +| With params, no return: | +| void blink_led(uint8_t pin, uint8_t count, uint32_t delay) | +| | +| No params, with return: | +| int ir_getkey(void) | +| | +| With params, with return: | +| int ir_to_led_number(int ir_command) | +| | +| With struct pointer: | +| uint8_t get_led_pin(simple_led_ctrl_t *leds, int led_num) | +| | ++-----------------------------------------------------------------+ +``` + +### Key Memory Addresses + +| Memory Address | Description | +| -------------- | ------------------------------- | +| `0x10000234` | main() function | +| `0x10` (16) | GPIO 16 - Red LED (led1_pin) | +| `0x11` (17) | GPIO 17 - Green LED (led2_pin) | +| `0x12` (18) | GPIO 18 - Yellow LED (led3_pin) | +| `0x05` | GPIO 5 - IR receiver | +| `0x0C` | NEC code for button 1 | +| `0x18` | NEC code for button 2 | +| `0x5E` | NEC code for button 3 | + +--- + +--- + +## Key Takeaways + +1. **Structs group related data** - Better organization than separate variables + +2. **Dot operator for variables, arrow for pointers** - `.` vs `->` + +3. **Designated initializers are cleaner** - `.member = value` syntax + +4. **Compilers flatten structs** - You see values, not struct names, in assembly + +5. **NEC protocol uses 8-bit commands** - 0x0C, 0x18, 0x5E for our buttons + +6. **Functions separate concerns** - Each function does one job + +7. **.ELF files contain more info than .BIN** - Symbols, sections, debug data + +8. **Log desynchronization is dangerous** - Logs can lie about real behavior + +9. **Pattern recognition is key** - Consecutive values like 16, 17, 18 reveal structs + +10. **Always patch the .bin for flashing** - .elf is for analysis only + +--- + +## Glossary + +| Term | Definition | +| -------------------------- | -------------------------------------------------- | +| **Arrow Operator (->)** | Accesses struct member through a pointer | +| **Designated Initializer** | Syntax `.member = value` for struct initialization | +| **Dot Operator (.)** | Accesses struct member from a struct variable | +| **.ELF File** | Executable and Linkable Format - contains symbols | +| **Flattening** | Compiler converting structs to individual values | +| **IR (Infrared)** | Invisible light used for remote control | +| **Log Desynchronization** | When logs don't match actual system behavior | +| **Member** | A variable inside a struct | +| **NEC Protocol** | Common IR communication standard | +| **Struct** | User-defined type grouping related variables | +| **typedef** | Creates an alias for a type | + +--- + +## Additional Resources + +### NEC IR Command Reference + +| Button | Command | Binary | +| ------ | ------- | --------- | +| 1 | 0x0C | 0000 1100 | +| 2 | 0x18 | 0001 1000 | +| 3 | 0x5E | 0101 1110 | + +### GPIO Pin Quick Reference + +| GPIO | Default Function | Our Usage | +| ---- | ---------------- | ----------- | +| 5 | General I/O | IR Receiver | +| 16 | General I/O | Red LED | +| 17 | General I/O | Green LED | +| 18 | General I/O | Yellow LED | + +### Struct Size Calculation + +| Type | Size (bytes) | +| ---------- | ------------ | +| `uint8_t` | 1 | +| `bool` | 1 | +| `uint16_t` | 2 | +| `uint32_t` | 4 | +| `int` | 4 | +| `float` | 4 | +| `pointer` | 4 (on ARM32) | + +--- + +## Real-World Implications + +### What You've Learned in This Course + +Over these weeks, you've built skills that few people possess: + +1. **Hardware fundamentals** - GPIO, I2C, PWM, IR protocols +2. **Reverse engineering** - Ghidra, disassembly, function identification +3. **Binary patching** - Modifying compiled code +4. **Security awareness** - Understanding vulnerabilities + +### The Power and Responsibility + +The techniques you've learned can be used for: + +**Good:** +- Security research +- Debugging proprietary systems +- Understanding how things work +- Career in cybersecurity + +**Danger:** +- Unauthorized system access +- Sabotage of critical infrastructure +- Fraud and deception + +**Always use your skills ethically and legally!** + +### Keep Learning + +This is just the beginning: +- Explore more complex protocols (SPI, CAN bus) +- Learn dynamic analysis with debuggers +- Study cryptographic implementations +- Practice on CTF challenges + +--- + +**Congratulations on completing this course! You now have the curiosity, persistence, and skills that embedded systems engineers and security researchers thrive on. Keep experimenting, documenting, and sharing your work. The world needs more builders and defenders like you!** + +Happy hacking! :) diff --git a/WEEK11/slides/WEEK11-IMG00.svg b/WEEK11/slides/WEEK11-IMG00.svg new file mode 100644 index 0000000..0469f90 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG00.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + 4F 70 65 6E 4F 43 44 + 10 00 02 34 08 B5 01 + 47 44 42 20 52 45 56 + 20 08 20 00 FF AA 00 + 52 50 32 33 35 30 00 + 0A 0A 0F 12 12 1A 1A + 41 52 4D 76 38 2D 4D + 00 FF 41 00 D4 FF 88 + 47 48 49 44 52 41 00 + FF 00 40 C0 C0 C0 00 + + + + + + + + + + + + +Embedded Systems +Reverse Engineering + + + + + +// WEEK 11 + + +Structures and Functions in +Embedded Systems: Debugging and Hacking +w/ IR Remote Control & NEC Protocol + + + + + +George Mason University + + + +RP2350 // ARM Cortex-M33 + diff --git a/WEEK11/slides/WEEK11-IMG01.svg b/WEEK11/slides/WEEK11-IMG01.svg new file mode 100644 index 0000000..6701ba4 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG01.svg @@ -0,0 +1,70 @@ + + + + +C Structures (Structs) +Grouping Related Data Together + + + +What is a Struct? +A user-defined type that groups +related variables under one name +Like a form with multiple fields +-- each field holds different data + + + +Struct Definition + +typedef struct { +uint8_t led1_pin; +uint8_t led2_pin; +uint8_t led3_pin; +bool led1_state; +bool led2_state; +bool led3_state; +} simple_led_ctrl_t; + + + +Why Use Structs? +1. +Organization +Related data stays together +2. +Readability +Code easier to understand +3. +Scalability +Easy to add more features +4. +Pass to Functions + + + +simple_led_ctrl_t leds + +pin1: 16 + +pin2: 17 + +pin3: 18 + +state1: 0 + +state2: 0 + +state3: 0 + diff --git a/WEEK11/slides/WEEK11-IMG02.svg b/WEEK11/slides/WEEK11-IMG02.svg new file mode 100644 index 0000000..6157da6 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG02.svg @@ -0,0 +1,85 @@ + + + + +Struct Memory Layout +How Structs Are Stored and Accessed + + + +Memory Layout (6 bytes total) + +Address +Member +Size +Value + + +0x20000000 +led1_pin +1 byte +16 (0x10) + +0x20000001 +led2_pin +1 byte +17 (0x11) + +0x20000002 +led3_pin +1 byte +18 (0x12) + +0x20000003 +led1_state +1 byte +0 (false) + +0x20000004 +led2_state +1 byte +0 (false) + +0x20000005 +led3_state +1 byte +0 (false) + + + +Dot Operator ( . ) +Use with struct variable + +leds.led1_pin = 16; +leds.led1_state = true; + + + +Arrow Operator ( -> ) +Use with pointer to struct + +ptr->led1_pin = 16; +// same as (*ptr).led1_pin + + + +Designated Initializers + +simple_led_ctrl_t leds = { +.led1_pin = 16, .led2_pin = 17, .led3_pin = 18 +}; +Clear which value goes +to which member +Order doesn't matter + diff --git a/WEEK11/slides/WEEK11-IMG03.svg b/WEEK11/slides/WEEK11-IMG03.svg new file mode 100644 index 0000000..c44ccd0 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG03.svg @@ -0,0 +1,88 @@ + + + + +NEC IR Protocol +Infrared Remote Control Communication + + + +How IR Works +Remote sends invisible light pulses +IR receiver on GPIO 5 reads signal +IR LED flashes in specific patterns +Each pattern = different button + + + +NEC Protocol Frame (32 bits) + + +Leader + +Address + +Addr Inv + +Command + +Cmd Inv + +Stop + +9ms+4.5ms +8-bit +8-bit check +8-bit +8-bit check + +Leader says "attention!", address identifies device, command is the button pressed + + + +NEC Command Codes + +Button +NEC Code +LED + + +1 +0x0C +Red (GP16) + +2 +0x18 +Green (GP17) + +3 +0x5E +Yellow (GP18) + + + +Hardware Wiring +GPIO 5 +IR Receiver (VS1838B) +GPIO 16 +Red LED + 220 ohm +GPIO 17 +Green LED + 220 ohm +GPIO 18 +Yellow LED + 220 ohm + + + +Projects: 0x0023_structures and 0x0026_functions + diff --git a/WEEK11/slides/WEEK11-IMG04.svg b/WEEK11/slides/WEEK11-IMG04.svg new file mode 100644 index 0000000..719946b --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG04.svg @@ -0,0 +1,91 @@ + + + + +Functions in C +Reusable Blocks of Code + + + +Anatomy of a Function + +int ir_to_led_number(int ir_command) { + +^^^ +^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^ +ret +function name +parameter + + +if (ir_command == 0x0C) return 1; +// body + return value + + + +Function Types + +Type +Example + + +No params, no ret +leds_all_off() + +Params, no return +blink_led(..) + +No params, return +ir_getkey() + +Params + return +ir_to_led_num() + +Struct pointer +get_led_pin() + + + +Key Functions + +ir_to_led_number(cmd) +Maps NEC code to LED 1/2/3 + +get_led_pin(leds, num) +Returns GPIO pin for LED + +blink_led(pin, cnt, ms) +Blinks LED cnt times + + + +Function Call Chain + +main() +--> +process_ir_led_command() + +1. leds_all_off() +Turn all LEDs off + +2. ir_to_led_number() +Map NEC to LED + +3. get_led_pin() +Get GPIO pin + +4. blink_led() +Blink + stay on + diff --git a/WEEK11/slides/WEEK11-IMG05.svg b/WEEK11/slides/WEEK11-IMG05.svg new file mode 100644 index 0000000..9058acc --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG05.svg @@ -0,0 +1,66 @@ + + + + +Struct Pointers in Functions +Passing Data Efficiently + + + +Why Pass by Pointer? +Efficient +4 bytes (address) not 6 +Modifiable +Function can change original +Standard +Embedded systems practice + + + +Arrow Operator +leds->led1_pin +Same as (*leds).led1_pin +Use -> when leds is a pointer + + + +leds_all_off() + +void leds_all_off( +simple_led_ctrl_t *leds) { +gpio_put(leds->led1_pin, 0); +gpio_put(leds->led2_pin, 0); + + + +blink_led() + +void blink_led(uint8_t pin, +uint8_t count, uint32_t ms){ +gpio_put(pin, true); +sleep_ms(ms); + + + +process_ir_led_command() -- Main Command Processor + +int process_ir_led_command(int cmd, +simple_led_ctrl_t *leds, uint8_t blink_count) { +leds_all_off(leds); +// turn all off first +int num = ir_to_led_number(cmd); +// map NEC to LED +blink_led(get_led_pin(leds, num), +// blink then stay on + diff --git a/WEEK11/slides/WEEK11-IMG06.svg b/WEEK11/slides/WEEK11-IMG06.svg new file mode 100644 index 0000000..84c7359 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG06.svg @@ -0,0 +1,57 @@ + + + + +Structures Source Code +0x0023_structures.c + + + +Full Source + + +#include <stdio.h> +#include "pico/stdlib.h" +#include "ir.h" + +typedef struct { +uint8_t led1_pin, led2_pin, led3_pin; +bool led1_state, led2_state, led3_state; +} simple_led_ctrl_t; + +int main(void) { +stdio_init_all(); +simple_led_ctrl_t leds = { +.led1_pin=16, .led2_pin=17, .led3_pin=18 +}; + +gpio_init(leds.led1_pin); +// init 16, 17, 18 +ir_init(5); +// IR on GPIO 5 +while (true) { +// main loop + + + +Main Loop Flow +ir_getkey() +--> +check NEC code +--> +set state +--> +gpio_put() +0x0C=LED1(red) 0x18=LED2(green) 0x5E=LED3(yellow) + diff --git a/WEEK11/slides/WEEK11-IMG07.svg b/WEEK11/slides/WEEK11-IMG07.svg new file mode 100644 index 0000000..514e9f0 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG07.svg @@ -0,0 +1,73 @@ + + + + +Struct Flattening +How Compilers Transform Structs + + + +C Code (High Level) + +gpio_init(leds.led1_pin); +// leds.led1_pin = 16 +gpio_init(leds.led2_pin); +// leds.led2_pin = 17 + + + +Assembly (Flattened) + +movs r0, #0x10 +// 16 +bl gpio_init +movs r0, #0x11 +// 17 +bl gpio_init + + + +The Key Insight +Struct abstraction DISAPPEARS +at assembly level +You see individual values (16, 17, 18) not struct names + + + +Struct Member Mapping + +Assembly +Struct Member +Physical +NEC Code + + +0x10 (16) +led1_pin +Red LED +0x0C + +0x11 (17) +led2_pin +Green LED +0x18 + +0x12 (18) +led3_pin +Yellow LED +0x5E + +Sequential values (16,17,18) reveal the struct pattern +Recognize patterns to reconstruct original structs in Ghidra + diff --git a/WEEK11/slides/WEEK11-IMG08.svg b/WEEK11/slides/WEEK11-IMG08.svg new file mode 100644 index 0000000..8eb24ae --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG08.svg @@ -0,0 +1,77 @@ + + + + +Hacking Structures +Swapping GPIO Pin Assignments + + + +Swap LED 1 and LED 2 +Find gpio_init values: +0x10 (16) +--> +0x11 (17) +0x11 (17) +--> +0x10 (16) +Swap the two byte values + + + +Result After Hack + +Before: +Btn 1 --> GPIO 16 --> Red +Btn 2 --> GPIO 17 --> Green + +After: +SWAPPED! + + + +Before (Normal) +Btn 1 (0x0C) --> GPIO 16 +Red +Btn 2 (0x18) --> GPIO 17 +Green +Log and LED match correctly + + +After (Hacked) +Btn 1 (0x0C) --> GPIO 17 +Green! +Btn 2 (0x18) --> GPIO 16 +Red! +Log says RED but GREEN lights + + + +Log Desynchronization + +Terminal Log: +NEC command: 0x0C +(expects Red) + +Physical LED: +GREEN LED on +MISMATCH! + +Operator sees correct logs but WRONG behavior + + + +Stuxnet: +False "normal" data to operators, equipment destroyed + diff --git a/WEEK11/slides/WEEK11-IMG09.svg b/WEEK11/slides/WEEK11-IMG09.svg new file mode 100644 index 0000000..e6ae7a2 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG09.svg @@ -0,0 +1,73 @@ + + + + +.ELF vs .BIN Analysis +Ghidra Analysis of 0x0026_functions + + + +.ELF vs .BIN Comparison + +Feature +.BIN File +.ELF File + + +Symbols +None +Function names + +Sections +Raw bytes only +.text .data .rodata + +Debug info +None +May include debug + +Use case +Flash to device +Analysis + debug + + + +Importing .BIN +Manual setup required: +ARM Cortex 32 little endian +Block: .text +Base: 10000000 +No function names + + +Importing .ELF +Auto-detected by Ghidra: +ARM format recognized +Sections auto-loaded +Symbol tree populated +Named functions visible + + + +Important Rule +Analyze the .ELF +for symbol information +Patch the .BIN +for flashing + + + +Export Workflow +Patch .bin in Ghidra --> uf2conv.py --> flash to Pico 2 + diff --git a/WEEK11/slides/WEEK11-IMG10.svg b/WEEK11/slides/WEEK11-IMG10.svg new file mode 100644 index 0000000..3906d05 --- /dev/null +++ b/WEEK11/slides/WEEK11-IMG10.svg @@ -0,0 +1,85 @@ + + + + +Structs & IR Protocol +Structs, Functions, IR, and Hacking + + + +C Structures +Group related data together +Dot (.) for variables +Arrow (->) for pointers +Designated init: .pin = 16 + + + +NEC IR Protocol +32-bit frame: addr + cmd +0x0C +Button 1 +0x18 +Button 2 +0x5E +Button 3 + + + +Functions +Reusable blocks, one job each +ir_to_led_number() +get_led_pin() / blink_led() +process_ir_led_command() + + + +Assembly Flattening +Structs vanish in assembly +Only see values: 0x10 0x11 0x12 +Pattern recognition is key +.ELF has symbols, .BIN doesn't + + + +Key Values +0x10000234 +main() +GPIO 5 +IR receiver +GPIO 16/17/18 +Red/Green/Yellow + + + +Hacking Techniques +GPIO swap +0x10 <--> 0x11 +Log desync +Logs lie! +Stuxnet +Same concept + + + +Projects +0x0023_structures +0x0026_functions + + + +Key Takeaway +Patch bytes, mislead logs +hardware does what YOU say + diff --git a/aapcs32.pdf b/aapcs32.pdf new file mode 100644 index 0000000..4373fef --- /dev/null +++ b/aapcs32.pdf @@ -0,0 +1,3592 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F10 256 0 R /F11 268 0 R /F2+0 304 0 R /F3 3 0 R /F4 14 0 R + /F5+0 308 0 R /F6 152 0 R /F7 215 0 R /F8+0 312 0 R /F9 246 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Times-Roman /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/Contents 398 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +5 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa) +>> /Border [ 0 0 0 ] /Rect [ 118.301 561.0236 391.1152 571.8236 ] /Subtype /Link /Type /Annot +>> +endobj +6 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/issues) +>> /Border [ 0 0 0 ] /Rect [ 275.1154 533.4236 406.6965 544.2236 ] /Subtype /Link /Type /Annot +>> +endobj +7 0 obj +<< +/Annots [ 5 0 R 6 0 R ] /Contents 399 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +8 0 obj +<< +/A << +/S /URI /Type /Action /URI (http://creativecommons.org/licenses/by-sa/4.0/) +>> /Border [ 0 0 0 ] /Rect [ 202.7023 719.4236 438.1145 730.2236 ] /Subtype /Link /Type /Annot +>> +endobj +9 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 97 0 R /XYZ 77.02362 668.0336 0 ] /Rect [ 193.1223 539.8236 232.2262 550.6236 ] /Subtype /Link /Type /Annot +>> +endobj +10 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://www.apache.org/licenses/LICENSE-2.0) +>> /Border [ 0 0 0 ] /Rect [ 57.02362 507.4236 262.4416 518.2236 ] /Subtype /Link /Type /Annot +>> +endobj +11 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 97 0 R /XYZ 77.02362 668.0336 0 ] /Rect [ 434.0436 354.2236 468.2331 365.0236 ] /Subtype /Link /Type /Annot +>> +endobj +12 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://www.arm.com/company/policies/trademarks) +>> /Border [ 0 0 0 ] /Rect [ 57.02362 256.2236 290.6413 267.0236 ] /Subtype /Link /Type /Annot +>> +endobj +13 0 obj +<< +/Annots [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ] /Contents 400 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +14 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font +>> +endobj +15 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 718.0436 102.0974 727.2236 ] /Subtype /Link /Type /Annot +>> +endobj +16 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 533.9986 718.6174 538.252 727.7974 ] /Subtype /Link /Type /Annot +>> +endobj +17 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 77.02362 702.8636 128.5976 712.0436 ] /Subtype /Link /Type /Annot +>> +endobj +18 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 533.3848 703.4374 538.252 712.6174 ] /Subtype /Link /Type /Annot +>> +endobj +19 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 650.6236 0 ] /Rect [ 77.02362 687.6836 133.659 696.8636 ] /Subtype /Link /Type /Annot +>> +endobj +20 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 650.6236 0 ] /Rect [ 533.3848 688.2574 538.252 697.4374 ] /Subtype /Link /Type /Annot +>> +endobj +21 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 595.8236 0 ] /Rect [ 77.02362 672.5036 223.9948 681.6836 ] /Subtype /Link /Type /Annot +>> +endobj +22 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 595.8236 0 ] /Rect [ 533.3848 673.0774 538.252 682.2574 ] /Subtype /Link /Type /Annot +>> +endobj +23 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 77.02362 657.3236 125.5458 666.5036 ] /Subtype /Link /Type /Annot +>> +endobj +24 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 533.3848 657.8974 538.252 667.0774 ] /Subtype /Link /Type /Annot +>> +endobj +25 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 574.6236 0 ] /Rect [ 77.02362 642.1436 163.2243 651.3236 ] /Subtype /Link /Type /Annot +>> +endobj +26 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 574.6236 0 ] /Rect [ 533.3848 642.7174 538.252 651.8974 ] /Subtype /Link /Type /Annot +>> +endobj +27 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 399.8236 0 ] /Rect [ 77.02362 626.9636 147.9692 636.1436 ] /Subtype /Link /Type /Annot +>> +endobj +28 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 399.8236 0 ] /Rect [ 533.3848 627.5374 538.252 636.7174 ] /Subtype /Link /Type /Annot +>> +endobj +29 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 334.2236 0 ] /Rect [ 77.02362 611.7836 164.2665 620.9636 ] /Subtype /Link /Type /Annot +>> +endobj +30 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 334.2236 0 ] /Rect [ 533.3848 612.3574 538.252 621.5374 ] /Subtype /Link /Type /Annot +>> +endobj +31 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 236.2236 0 ] /Rect [ 77.02362 596.6036 133.8645 605.7836 ] /Subtype /Link /Type /Annot +>> +endobj +32 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 236.2236 0 ] /Rect [ 533.3848 597.1774 538.252 606.3574 ] /Subtype /Link /Type /Annot +>> +endobj +33 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 158 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 581.4236 147.133 590.6036 ] /Subtype /Link /Type /Annot +>> +endobj +34 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 158 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 533.9986 581.9974 538.252 591.1774 ] /Subtype /Link /Type /Annot +>> +endobj +35 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 158 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 77.02362 566.2436 156.0263 575.4236 ] /Subtype /Link /Type /Annot +>> +endobj +36 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 158 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 533.3848 566.8174 538.252 575.9974 ] /Subtype /Link /Type /Annot +>> +endobj +37 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 158 0 R /XYZ 57.02362 678.2236 0 ] /Rect [ 97.02362 551.0636 278.233 560.2436 ] /Subtype /Link /Type /Annot +>> +endobj +38 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 158 0 R /XYZ 57.02362 678.2236 0 ] /Rect [ 533.3848 551.6374 538.252 560.8174 ] /Subtype /Link /Type /Annot +>> +endobj +39 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 158 0 R /XYZ 57.02362 437.4236 0 ] /Rect [ 97.02362 535.8836 182.7201 545.0636 ] /Subtype /Link /Type /Annot +>> +endobj +40 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 158 0 R /XYZ 57.02362 437.4236 0 ] /Rect [ 533.3848 536.4574 538.252 545.6374 ] /Subtype /Link /Type /Annot +>> +endobj +41 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 216 0 R /XYZ 57.02362 573.8236 0 ] /Rect [ 77.02362 520.7036 139.5048 529.8836 ] /Subtype /Link /Type /Annot +>> +endobj +42 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 216 0 R /XYZ 57.02362 573.8236 0 ] /Rect [ 533.3848 521.2774 538.252 530.4574 ] /Subtype /Link /Type /Annot +>> +endobj +43 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 216 0 R /XYZ 57.02362 243.4236 0 ] /Rect [ 77.02362 505.5236 192.4796 514.7036 ] /Subtype /Link /Type /Annot +>> +endobj +44 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 216 0 R /XYZ 57.02362 243.4236 0 ] /Rect [ 533.3848 506.0974 538.252 515.2774 ] /Subtype /Link /Type /Annot +>> +endobj +45 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 221 0 R /XYZ 57.02362 574.6236 0 ] /Rect [ 77.02362 490.3436 171.3823 499.5236 ] /Subtype /Link /Type /Annot +>> +endobj +46 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 221 0 R /XYZ 57.02362 574.6236 0 ] /Rect [ 528.5176 490.9174 538.252 500.0974 ] /Subtype /Link /Type /Annot +>> +endobj +47 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 223 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 475.1636 90.61477 484.3436 ] /Subtype /Link /Type /Annot +>> +endobj +48 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 223 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 529.7452 475.7374 538.252 484.9174 ] /Subtype /Link /Type /Annot +>> +endobj +49 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 228 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 459.9836 112.2796 469.1636 ] /Subtype /Link /Type /Annot +>> +endobj +50 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 228 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 529.7452 460.5574 538.252 469.7374 ] /Subtype /Link /Type /Annot +>> +endobj +51 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 228 0 R /XYZ 57.02362 688.6236 0 ] /Rect [ 77.02362 444.8036 146.7365 453.9836 ] /Subtype /Link /Type /Annot +>> +endobj +52 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 228 0 R /XYZ 57.02362 688.6236 0 ] /Rect [ 528.5176 445.3774 538.252 454.5574 ] /Subtype /Link /Type /Annot +>> +endobj +53 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 228 0 R /XYZ 57.02362 539.8236 0 ] /Rect [ 77.02362 429.6236 147.7749 438.8036 ] /Subtype /Link /Type /Annot +>> +endobj +54 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 228 0 R /XYZ 57.02362 539.8236 0 ] /Rect [ 528.5176 430.1974 538.252 439.3774 ] /Subtype /Link /Type /Annot +>> +endobj +55 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 232 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 414.4436 163.7258 423.6236 ] /Subtype /Link /Type /Annot +>> +endobj +56 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 232 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 529.7452 415.0174 538.252 424.1974 ] /Subtype /Link /Type /Annot +>> +endobj +57 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 232 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 77.02362 399.2636 192.8233 408.4436 ] /Subtype /Link /Type /Annot +>> +endobj +58 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 232 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 528.5176 399.8374 538.252 409.0174 ] /Subtype /Link /Type /Annot +>> +endobj +59 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 232 0 R /XYZ 57.02362 213.4236 0 ] /Rect [ 97.02362 384.0836 231.3357 393.2636 ] /Subtype /Link /Type /Annot +>> +endobj +60 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 232 0 R /XYZ 57.02362 213.4236 0 ] /Rect [ 528.5176 384.6574 538.252 393.8374 ] /Subtype /Link /Type /Annot +>> +endobj +61 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 234 0 R /XYZ 57.02362 723.4236 0 ] /Rect [ 97.02362 368.9036 208.2587 378.0836 ] /Subtype /Link /Type /Annot +>> +endobj +62 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 234 0 R /XYZ 57.02362 723.4236 0 ] /Rect [ 528.5176 369.4774 538.252 378.6574 ] /Subtype /Link /Type /Annot +>> +endobj +63 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 234 0 R /XYZ 57.02362 639.2236 0 ] /Rect [ 77.02362 353.7236 213.0838 362.9036 ] /Subtype /Link /Type /Annot +>> +endobj +64 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 234 0 R /XYZ 57.02362 639.2236 0 ] /Rect [ 528.5176 354.2974 538.252 363.4774 ] /Subtype /Link /Type /Annot +>> +endobj +65 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 77.02362 338.5436 162.4922 347.7236 ] /Subtype /Link /Type /Annot +>> +endobj +66 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 528.5176 339.1174 538.252 348.2974 ] /Subtype /Link /Type /Annot +>> +endobj +67 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 546.2236 0 ] /Rect [ 97.02362 323.3636 167.816 332.5436 ] /Subtype /Link /Type /Annot +>> +endobj +68 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 546.2236 0 ] /Rect [ 528.5176 323.9374 538.252 333.1174 ] /Subtype /Link /Type /Annot +>> +endobj +69 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 450.8236 0 ] /Rect [ 97.02362 308.1836 149.8714 317.3636 ] /Subtype /Link /Type /Annot +>> +endobj +70 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 450.8236 0 ] /Rect [ 528.5176 308.7574 538.252 317.9374 ] /Subtype /Link /Type /Annot +>> +endobj +71 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 355.4236 0 ] /Rect [ 97.02362 293.0036 148.508 302.1836 ] /Subtype /Link /Type /Annot +>> +endobj +72 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 355.4236 0 ] /Rect [ 528.5176 293.5774 538.252 302.7574 ] /Subtype /Link /Type /Annot +>> +endobj +73 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 260.0236 0 ] /Rect [ 97.02362 277.8236 157.4093 287.0036 ] /Subtype /Link /Type /Annot +>> +endobj +74 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 260.0236 0 ] /Rect [ 528.5176 278.3974 538.252 287.5774 ] /Subtype /Link /Type /Annot +>> +endobj +75 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 238 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 97.02362 262.6436 225.445 271.8236 ] /Subtype /Link /Type /Annot +>> +endobj +76 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 238 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 528.5176 263.2174 538.252 272.3974 ] /Subtype /Link /Type /Annot +>> +endobj +77 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 239 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 247.4636 193.0712 256.6436 ] /Subtype /Link /Type /Annot +>> +endobj +78 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 239 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 529.7452 248.0374 538.252 257.2174 ] /Subtype /Link /Type /Annot +>> +endobj +79 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 239 0 R /XYZ 57.02362 677.8236 0 ] /Rect [ 77.02362 232.2836 166.7654 241.4636 ] /Subtype /Link /Type /Annot +>> +endobj +80 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 239 0 R /XYZ 57.02362 677.8236 0 ] /Rect [ 528.5176 232.8574 538.252 242.0374 ] /Subtype /Link /Type /Annot +>> +endobj +81 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 239 0 R /XYZ 57.02362 601.4236 0 ] /Rect [ 97.02362 217.1036 177.7446 226.2836 ] /Subtype /Link /Type /Annot +>> +endobj +82 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 239 0 R /XYZ 57.02362 601.4236 0 ] /Rect [ 528.5176 217.6774 538.252 226.8574 ] /Subtype /Link /Type /Annot +>> +endobj +83 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 242 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 97.02362 201.9236 212.2144 211.1036 ] /Subtype /Link /Type /Annot +>> +endobj +84 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 242 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 528.5176 202.4974 538.252 211.6774 ] /Subtype /Link /Type /Annot +>> +endobj +85 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 242 0 R /XYZ 57.02362 180.8236 0 ] /Rect [ 77.02362 186.7436 225.8961 195.9236 ] /Subtype /Link /Type /Annot +>> +endobj +86 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 242 0 R /XYZ 57.02362 180.8236 0 ] /Rect [ 528.5176 187.3174 538.252 196.4974 ] /Subtype /Link /Type /Annot +>> +endobj +87 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 243 0 R /XYZ 57.02362 525.0236 0 ] /Rect [ 97.02362 171.5636 161.6228 180.7436 ] /Subtype /Link /Type /Annot +>> +endobj +88 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 243 0 R /XYZ 57.02362 525.0236 0 ] /Rect [ 528.5176 172.1374 538.252 181.3174 ] /Subtype /Link /Type /Annot +>> +endobj +89 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 248 0 R /XYZ 57.02362 707.4236 0 ] /Rect [ 77.02362 156.3836 159.0968 165.5636 ] /Subtype /Link /Type /Annot +>> +endobj +90 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 248 0 R /XYZ 57.02362 707.4236 0 ] /Rect [ 528.5176 156.9574 538.252 166.1374 ] /Subtype /Link /Type /Annot +>> +endobj +91 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 248 0 R /XYZ 57.02362 275.8236 0 ] /Rect [ 97.02362 141.2036 207.7955 150.3836 ] /Subtype /Link /Type /Annot +>> +endobj +92 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 248 0 R /XYZ 57.02362 275.8236 0 ] /Rect [ 528.5176 141.7774 538.252 150.9574 ] /Subtype /Link /Type /Annot +>> +endobj +93 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 77.02362 126.0236 148.761 135.2036 ] /Subtype /Link /Type /Annot +>> +endobj +94 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 528.5176 126.5974 538.252 135.7774 ] /Subtype /Link /Type /Annot +>> +endobj +95 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 490.2236 0 ] /Rect [ 77.02362 110.8436 168.1625 120.0236 ] /Subtype /Link /Type /Annot +>> +endobj +96 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 490.2236 0 ] /Rect [ 528.5176 111.4174 538.252 120.5974 ] /Subtype /Link /Type /Annot +>> +endobj +97 0 obj +<< +/Annots [ 15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R + 25 0 R 26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R + 35 0 R 36 0 R 37 0 R 38 0 R 39 0 R 40 0 R 41 0 R 42 0 R 43 0 R 44 0 R + 45 0 R 46 0 R 47 0 R 48 0 R 49 0 R 50 0 R 51 0 R 52 0 R 53 0 R 54 0 R + 55 0 R 56 0 R 57 0 R 58 0 R 59 0 R 60 0 R 61 0 R 62 0 R 63 0 R 64 0 R + 65 0 R 66 0 R 67 0 R 68 0 R 69 0 R 70 0 R 71 0 R 72 0 R 73 0 R 74 0 R + 75 0 R 76 0 R 77 0 R 78 0 R 79 0 R 80 0 R 81 0 R 82 0 R 83 0 R 84 0 R + 85 0 R 86 0 R 87 0 R 88 0 R 89 0 R 90 0 R 91 0 R 92 0 R 93 0 R 94 0 R + 95 0 R 96 0 R ] /Contents 401 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +98 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 255 0 R /XYZ 57.02362 154.6236 0 ] /Rect [ 77.02362 752.8436 144.7829 762.0236 ] /Subtype /Link /Type /Annot +>> +endobj +99 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 255 0 R /XYZ 57.02362 154.6236 0 ] /Rect [ 528.5176 753.4174 538.252 762.5974 ] /Subtype /Link /Type /Annot +>> +endobj +100 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 737.6636 148.8542 746.8436 ] /Subtype /Link /Type /Annot +>> +endobj +101 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 529.7452 738.2374 538.252 747.4174 ] /Subtype /Link /Type /Annot +>> +endobj +102 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 688.6236 0 ] /Rect [ 77.02362 722.4836 255.0654 731.6636 ] /Subtype /Link /Type /Annot +>> +endobj +103 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 688.6236 0 ] /Rect [ 528.5176 723.0574 538.252 732.2374 ] /Subtype /Link /Type /Annot +>> +endobj +104 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 612.2236 0 ] /Rect [ 97.02362 707.3036 307.5219 716.4836 ] /Subtype /Link /Type /Annot +>> +endobj +105 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 612.2236 0 ] /Rect [ 528.5176 707.8774 538.252 717.0574 ] /Subtype /Link /Type /Annot +>> +endobj +106 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 401.6236 0 ] /Rect [ 97.02362 692.1236 191.2329 701.3036 ] /Subtype /Link /Type /Annot +>> +endobj +107 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 401.6236 0 ] /Rect [ 528.5176 692.6974 538.252 701.8774 ] /Subtype /Link /Type /Annot +>> +endobj +108 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 434.6236 0 ] /Rect [ 77.02362 676.9436 323.9453 686.1236 ] /Subtype /Link /Type /Annot +>> +endobj +109 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 434.6236 0 ] /Rect [ 528.5176 677.5174 538.252 686.6974 ] /Subtype /Link /Type /Annot +>> +endobj +110 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 351.0236 0 ] /Rect [ 77.02362 661.7636 256.8248 670.9436 ] /Subtype /Link /Type /Annot +>> +endobj +111 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 351.0236 0 ] /Rect [ 528.5176 662.3374 538.252 671.5174 ] /Subtype /Link /Type /Annot +>> +endobj +112 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 263.8236 0 ] /Rect [ 77.02362 646.5836 177.5457 655.7636 ] /Subtype /Link /Type /Annot +>> +endobj +113 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 263.8236 0 ] /Rect [ 528.5176 647.1574 538.252 656.3374 ] /Subtype /Link /Type /Annot +>> +endobj +114 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 165.8236 0 ] /Rect [ 97.02362 631.4036 266.6161 640.5836 ] /Subtype /Link /Type /Annot +>> +endobj +115 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 165.8236 0 ] /Rect [ 528.5176 631.9774 538.252 641.1574 ] /Subtype /Link /Type /Annot +>> +endobj +116 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 264 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 97.02362 616.2236 272.1182 625.4036 ] /Subtype /Link /Type /Annot +>> +endobj +117 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 264 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 528.5176 616.7974 538.252 625.9774 ] /Subtype /Link /Type /Annot +>> +endobj +118 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 264 0 R /XYZ 57.02362 702.4236 0 ] /Rect [ 97.02362 601.0436 267.7367 610.2236 ] /Subtype /Link /Type /Annot +>> +endobj +119 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 264 0 R /XYZ 57.02362 702.4236 0 ] /Rect [ 528.5176 601.6174 538.252 610.7974 ] /Subtype /Link /Type /Annot +>> +endobj +120 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 264 0 R /XYZ 57.02362 639.8236 0 ] /Rect [ 97.02362 585.8636 259.6123 595.0436 ] /Subtype /Link /Type /Annot +>> +endobj +121 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 264 0 R /XYZ 57.02362 639.8236 0 ] /Rect [ 528.5176 586.4374 538.252 595.6174 ] /Subtype /Link /Type /Annot +>> +endobj +122 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 570.6836 199.0076 579.8636 ] /Subtype /Link /Type /Annot +>> +endobj +123 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 529.7452 571.2574 538.252 580.4374 ] /Subtype /Link /Type /Annot +>> +endobj +124 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 677.8236 0 ] /Rect [ 77.02362 555.5036 139.9307 564.6836 ] /Subtype /Link /Type /Annot +>> +endobj +125 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 677.8236 0 ] /Rect [ 528.5176 556.0774 538.252 565.2574 ] /Subtype /Link /Type /Annot +>> +endobj +126 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 639.8236 0 ] /Rect [ 97.02362 540.3236 188.8049 549.5036 ] /Subtype /Link /Type /Annot +>> +endobj +127 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 639.8236 0 ] /Rect [ 528.5176 540.8974 538.252 550.0774 ] /Subtype /Link /Type /Annot +>> +endobj +128 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 270 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 97.02362 525.1436 176.0823 534.3236 ] /Subtype /Link /Type /Annot +>> +endobj +129 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 270 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 528.5176 525.7174 538.252 534.8974 ] /Subtype /Link /Type /Annot +>> +endobj +130 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 270 0 R /XYZ 57.02362 580.4236 0 ] /Rect [ 97.02362 509.9636 196.0478 519.1436 ] /Subtype /Link /Type /Annot +>> +endobj +131 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 270 0 R /XYZ 57.02362 580.4236 0 ] /Rect [ 528.5176 510.5374 538.252 519.7174 ] /Subtype /Link /Type /Annot +>> +endobj +132 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 274 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 97.02362 494.7836 187.5013 503.9636 ] /Subtype /Link /Type /Annot +>> +endobj +133 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 274 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 528.5176 495.3574 538.252 504.5374 ] /Subtype /Link /Type /Annot +>> +endobj +134 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 274 0 R /XYZ 57.02362 514.8236 0 ] /Rect [ 97.02362 479.6036 198.345 488.7836 ] /Subtype /Link /Type /Annot +>> +endobj +135 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 274 0 R /XYZ 57.02362 514.8236 0 ] /Rect [ 528.5176 480.1774 538.252 489.3574 ] /Subtype /Link /Type /Annot +>> +endobj +136 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 274 0 R /XYZ 57.02362 267.4236 0 ] /Rect [ 97.02362 464.4236 254.323 473.6036 ] /Subtype /Link /Type /Annot +>> +endobj +137 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 274 0 R /XYZ 57.02362 267.4236 0 ] /Rect [ 528.5176 464.9974 538.252 474.1774 ] /Subtype /Link /Type /Annot +>> +endobj +138 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 274 0 R /XYZ 57.02362 194.0236 0 ] /Rect [ 97.02362 449.2436 157.4093 458.4236 ] /Subtype /Link /Type /Annot +>> +endobj +139 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 274 0 R /XYZ 57.02362 194.0236 0 ] /Rect [ 528.5176 449.8174 538.252 458.9974 ] /Subtype /Link /Type /Annot +>> +endobj +140 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 287 0 R /XYZ 57.02362 606.6236 0 ] /Rect [ 77.02362 434.0636 216.1318 443.2436 ] /Subtype /Link /Type /Annot +>> +endobj +141 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 287 0 R /XYZ 57.02362 606.6236 0 ] /Rect [ 528.5176 434.6374 538.252 443.8174 ] /Subtype /Link /Type /Annot +>> +endobj +142 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 292 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 418.8836 290.3945 428.0636 ] /Subtype /Link /Type /Annot +>> +endobj +143 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 292 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 529.7452 419.4574 538.252 428.6374 ] /Subtype /Link /Type /Annot +>> +endobj +144 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 292 0 R /XYZ 57.02362 687.4236 0 ] /Rect [ 77.02362 403.7036 142.9787 412.8836 ] /Subtype /Link /Type /Annot +>> +endobj +145 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 292 0 R /XYZ 57.02362 687.4236 0 ] /Rect [ 528.5176 404.2774 538.252 413.4574 ] /Subtype /Link /Type /Annot +>> +endobj +146 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 292 0 R /XYZ 57.02362 589.4236 0 ] /Rect [ 77.02362 388.5236 185.9539 397.7036 ] /Subtype /Link /Type /Annot +>> +endobj +147 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 292 0 R /XYZ 57.02362 589.4236 0 ] /Rect [ 528.5176 389.0974 538.252 398.2774 ] /Subtype /Link /Type /Annot +>> +endobj +148 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 294 0 R /XYZ 57.02362 516.2236 0 ] /Rect [ 97.02362 373.3436 179.3246 382.5236 ] /Subtype /Link /Type /Annot +>> +endobj +149 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 294 0 R /XYZ 57.02362 516.2236 0 ] /Rect [ 528.5176 373.9174 538.252 383.0974 ] /Subtype /Link /Type /Annot +>> +endobj +150 0 obj +<< +/Annots [ 98 0 R 99 0 R 100 0 R 101 0 R 102 0 R 103 0 R 104 0 R 105 0 R 106 0 R 107 0 R + 108 0 R 109 0 R 110 0 R 111 0 R 112 0 R 113 0 R 114 0 R 115 0 R 116 0 R 117 0 R + 118 0 R 119 0 R 120 0 R 121 0 R 122 0 R 123 0 R 124 0 R 125 0 R 126 0 R 127 0 R + 128 0 R 129 0 R 130 0 R 131 0 R 132 0 R 133 0 R 134 0 R 135 0 R 136 0 R 137 0 R + 138 0 R 139 0 R 140 0 R 141 0 R 142 0 R 143 0 R 144 0 R 145 0 R 146 0 R 147 0 R + 148 0 R 149 0 R ] /Contents 402 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +151 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 274 0 R /XYZ 57.02362 665.8236 0 ] /Rect [ 376.5736 314.6236 472.6996 325.4236 ] /Subtype /Link /Type /Annot +>> +endobj +152 0 obj +<< +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F6 /Subtype /Type1 /Type /Font +>> +endobj +153 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 270 0 R /XYZ 57.02362 582.9236 0 ] /Rect [ 411.7756 303.8236 496.7922 314.6236 ] /Subtype /Link /Type /Annot +>> +endobj +154 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 242 0 R /XYZ 57.02362 598.4236 0 ] /Rect [ 411.2333 251.4236 495.7797 262.2236 ] /Subtype /Link /Type /Annot +>> +endobj +155 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 242 0 R /XYZ 57.02362 598.4236 0 ] /Rect [ 221.829 240.6236 276.4838 251.4236 ] /Subtype /Link /Type /Annot +>> +endobj +156 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 274 0 R /XYZ 57.02362 196.5236 0 ] /Rect [ 273.7679 229.8236 376.6703 240.6236 ] /Subtype /Link /Type /Annot +>> +endobj +157 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 248 0 R /XYZ 57.02362 278.3236 0 ] /Rect [ 299.0673 198.2236 397.9047 209.0236 ] /Subtype /Link /Type /Annot +>> +endobj +158 0 obj +<< +/Annots [ 151 0 R 153 0 R 154 0 R 155 0 R 156 0 R 157 0 R ] /Contents 403 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +159 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 351.8236 0 ] /Rect [ 387.9862 710.8236 503.0658 721.6236 ] /Subtype /Link /Type /Annot +>> +endobj +160 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 351.8236 0 ] /Rect [ 221.829 700.0236 271.1927 710.8236 ] /Subtype /Link /Type /Annot +>> +endobj +161 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 270 0 R /XYZ 57.02362 582.9236 0 ] /Rect [ 265.3612 626.0236 350.3778 636.8236 ] /Subtype /Link /Type /Annot +>> +endobj +162 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 768.0236 0 ] /Rect [ 378.037 583.6236 491.161 594.4236 ] /Subtype /Link /Type /Annot +>> +endobj +163 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/products/software-development-tools/compilers/arm-compiler-5/docs/101028/latest/1-preface) +>> /Border [ 0 0 0 ] /Rect [ 363.1351 562.0236 386.2767 572.8236 ] /Subtype /Link /Type /Annot +>> +endobj +164 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 642.3236 0 ] /Rect [ 396.16 562.0236 472.6556 572.8236 ] /Subtype /Link /Type /Annot +>> +endobj +165 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 292 0 R /XYZ 57.02362 769.8236 0 ] /Rect [ 221.829 551.2236 490.1187 562.0236 ] /Subtype /Link /Type /Annot +>> +endobj +166 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 292 0 R /XYZ 57.02362 592.4236 0 ] /Rect [ 221.829 519.6236 327.0868 530.4236 ] /Subtype /Link /Type /Annot +>> +endobj +167 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 768.0236 0 ] /Rect [ 371.8451 476.4236 484.9691 487.2236 ] /Subtype /Link /Type /Annot +>> +endobj +168 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 493.2236 0 ] /Rect [ 221.829 465.6236 357.7782 476.4236 ] /Subtype /Link /Type /Annot +>> +endobj +169 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 614.7236 0 ] /Rect [ 363.4999 465.6236 506.1508 476.4236 ] /Subtype /Link /Type /Annot +>> +endobj +170 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 614.7236 0 ] /Rect [ 221.829 454.8236 292.4799 465.6236 ] /Subtype /Link /Type /Annot +>> +endobj +171 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 351.8236 0 ] /Rect [ 298.2015 454.8236 465.5057 465.6236 ] /Subtype /Link /Type /Annot +>> +endobj +172 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 768.0236 0 ] /Rect [ 440.3426 444.0236 517.9984 454.8236 ] /Subtype /Link /Type /Annot +>> +endobj +173 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 493.2236 0 ] /Rect [ 221.829 433.2236 357.7782 444.0236 ] /Subtype /Link /Type /Annot +>> +endobj +174 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 283 0 R /XYZ 57.02362 391.0236 0 ] /Rect [ 233.0482 412.4236 509.5829 423.2236 ] /Subtype /Link /Type /Annot +>> +endobj +175 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 283 0 R /XYZ 57.02362 391.0236 0 ] /Rect [ 221.829 401.6236 262.3817 412.4236 ] /Subtype /Link /Type /Annot +>> +endobj +176 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 244 0 R /XYZ 57.02362 691.4236 0 ] /Rect [ 233.0482 380.0236 295.3759 390.8236 ] /Subtype /Link /Type /Annot +>> +endobj +177 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 242 0 R /XYZ 57.02362 598.4236 0 ] /Rect [ 233.0482 358.4236 375.1102 369.2236 ] /Subtype /Link /Type /Annot +>> +endobj +178 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 244 0 R /XYZ 57.02362 599.4236 0 ] /Rect [ 225.3402 305.2236 308.4012 316.0236 ] /Subtype /Link /Type /Annot +>> +endobj +179 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 239 0 R /XYZ 57.02362 680.8236 0 ] /Rect [ 314.1229 305.2236 396.806 316.0236 ] /Subtype /Link /Type /Annot +>> +endobj +180 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 232 0 R /XYZ 57.02362 215.9236 0 ] /Rect [ 225.3402 283.6236 351.8719 294.4236 ] /Subtype /Link /Type /Annot +>> +endobj +181 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 437.6236 0 ] /Rect [ 357.5936 283.6236 528.5277 294.4236 ] /Subtype /Link /Type /Annot +>> +endobj +182 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 437.6236 0 ] /Rect [ 221.829 272.8236 315.6346 283.6236 ] /Subtype /Link /Type /Annot +>> +endobj +183 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 642.3236 0 ] /Rect [ 321.3563 272.8236 397.8519 283.6236 ] /Subtype /Link /Type /Annot +>> +endobj +184 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 232 0 R /XYZ 57.02362 215.9236 0 ] /Rect [ 280.2103 251.2236 406.7421 262.0236 ] /Subtype /Link /Type /Annot +>> +endobj +185 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 437.6236 0 ] /Rect [ 412.4637 251.2236 518.5389 262.0236 ] /Subtype /Link /Type /Annot +>> +endobj +186 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 57.02362 437.6236 0 ] /Rect [ 221.829 240.4236 380.4935 251.2236 ] /Subtype /Link /Type /Annot +>> +endobj +187 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 264 0 R /XYZ 57.02362 642.3236 0 ] /Rect [ 386.2152 240.4236 483.2684 251.2236 ] /Subtype /Link /Type /Annot +>> +endobj +188 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 264 0 R /XYZ 57.02362 642.3236 0 ] /Rect [ 221.829 229.6236 281.7133 240.4236 ] /Subtype /Link /Type /Annot +>> +endobj +189 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 583.8236 0 ] /Rect [ 287.4349 229.6236 465.8925 240.4236 ] /Subtype /Link /Type /Annot +>> +endobj +190 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 280 0 R /XYZ 57.02362 443.8236 0 ] /Rect [ 429.576 208.8236 513.1029 219.6236 ] /Subtype /Link /Type /Annot +>> +endobj +191 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 280 0 R /XYZ 57.02362 443.8236 0 ] /Rect [ 221.829 198.0236 275.4203 208.8236 ] /Subtype /Link /Type /Annot +>> +endobj +192 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 280 0 R /XYZ 57.02362 268.6236 0 ] /Rect [ 298.0741 198.0236 388.0917 208.8236 ] /Subtype /Link /Type /Annot +>> +endobj +193 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 283 0 R /XYZ 57.02362 391.0236 0 ] /Rect [ 221.829 176.4236 498.3636 187.2236 ] /Subtype /Link /Type /Annot +>> +endobj +194 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 283 0 R /XYZ 57.02362 391.0236 0 ] /Rect [ 221.829 165.6236 262.3817 176.4236 ] /Subtype /Link /Type /Annot +>> +endobj +195 0 obj +<< +/Annots [ 159 0 R 160 0 R 161 0 R 162 0 R 163 0 R 164 0 R 165 0 R 166 0 R 167 0 R 168 0 R + 169 0 R 170 0 R 171 0 R 172 0 R 173 0 R 174 0 R 175 0 R 176 0 R 177 0 R 178 0 R + 179 0 R 180 0 R 181 0 R 182 0 R 183 0 R 184 0 R 185 0 R 186 0 R 187 0 R 188 0 R + 189 0 R 190 0 R 191 0 R 192 0 R 193 0 R 194 0 R ] /Contents 404 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +196 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 97 0 R /XYZ 77.02362 668.0336 0 ] /Rect [ 266.2919 706.6236 300.4813 717.4236 ] /Subtype /Link /Type /Annot +>> +endobj +197 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 97 0 R /XYZ 77.02362 637.6736 0 ] /Rect [ 320.327 689.8236 402.5151 700.6236 ] /Subtype /Link /Type /Annot +>> +endobj +198 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 97 0 R /XYZ 77.02362 622.4936 0 ] /Rect [ 386.6185 689.8236 502.3921 700.6236 ] /Subtype /Link /Type /Annot +>> +endobj +199 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 97 0 R /XYZ 77.02362 607.3136 0 ] /Rect [ 244.829 679.0236 288.8051 689.8236 ] /Subtype /Link /Type /Annot +>> +endobj +200 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 680.8236 0 ] /Rect [ 233.0482 598.8236 284.161 609.6236 ] /Subtype /Link /Type /Annot +>> +endobj +201 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 501.0986 104.2136 511.8986 ] /Subtype /Link /Type /Annot +>> +endobj +202 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 469.4986 102.6667 480.2986 ] /Subtype /Link /Type /Annot +>> +endobj +203 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 448.6986 101.3483 459.4986 ] /Subtype /Link /Type /Annot +>> +endobj +204 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 417.0986 106.5998 427.8986 ] /Subtype /Link /Type /Annot +>> +endobj +205 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/docs/ddi0406/c/arm-architecture-reference-manual-armv7-a-and-armv7-r-edition) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 396.2986 103.3742 407.0986 ] /Subtype /Link /Type /Annot +>> +endobj +206 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/docs/ddi0100/latest/armv5-architecture-reference-manual) +>> /Border [ 0 0 0 ] /Rect [ 161.5428 385.4986 328.3724 396.2986 ] /Subtype /Link /Type /Annot +>> +endobj +207 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/docs/ddi0100/latest/armv5-architecture-reference-manual) +>> /Border [ 0 0 0 ] /Rect [ 161.5428 374.6986 327.6473 385.4986 ] /Subtype /Link /Type /Annot +>> +endobj +208 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/docs/ddi0100/latest/armv5-architecture-reference-manual) +>> /Border [ 0 0 0 ] /Rect [ 161.5428 363.8986 214.6859 374.6986 ] /Subtype /Link /Type /Annot +>> +endobj +209 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/docs/ddi0406/c/arm-architecture-reference-manual-armv7-a-and-armv7-r-edition) +>> /Border [ 0 0 0 ] /Rect [ 161.5428 332.2986 328.3724 343.0986 ] /Subtype /Link /Type /Annot +>> +endobj +210 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/docs/ddi0406/c/arm-architecture-reference-manual-armv7-a-and-armv7-r-edition) +>> /Border [ 0 0 0 ] /Rect [ 161.5428 321.4986 329.9632 332.2986 ] /Subtype /Link /Type /Annot +>> +endobj +211 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/docs/ddi0406/c/arm-architecture-reference-manual-armv7-a-and-armv7-r-edition) +>> /Border [ 0 0 0 ] /Rect [ 161.5428 310.6986 315.659 321.4986 ] /Subtype /Link /Type /Annot +>> +endobj +212 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/products/software-development-tools/compilers/arm-compiler-5/docs/101028/latest/1-preface) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 289.8986 86.16522 300.6986 ] /Subtype /Link /Type /Annot +>> +endobj +213 0 obj +<< +/A << +/S /URI /Type /Action /URI (http://itanium-cxx-abi.github.io/cxx-abi/abi.html) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 269.0986 102.1218 279.8986 ] /Subtype /Link /Type /Annot +>> +endobj +214 0 obj +<< +/A << +/S /URI /Type /Action /URI (http://itanium-cxx-abi.github.io/) +>> /Border [ 0 0 0 ] /Rect [ 161.5428 269.0986 304.7826 279.8986 ] /Subtype /Link /Type /Annot +>> +endobj +215 0 obj +<< +/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F7 /Subtype /Type1 /Type /Font +>> +endobj +216 0 obj +<< +/Annots [ 196 0 R 197 0 R 198 0 R 199 0 R 200 0 R 201 0 R 202 0 R 203 0 R 204 0 R 205 0 R + 206 0 R 207 0 R 208 0 R 209 0 R 210 0 R 211 0 R 212 0 R 213 0 R 214 0 R ] /Contents 405 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +217 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 201.0056 748.2236 248.5746 759.0236 ] /Subtype /Link /Type /Annot +>> +endobj +218 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 449.613 748.2236 502.4421 759.0236 ] /Subtype /Link /Type /Annot +>> +endobj +219 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 268.4177 737.4236 314.9821 748.2236 ] /Subtype /Link /Type /Annot +>> +endobj +220 0 obj +<< +/Annots [ 217 0 R 218 0 R 219 0 R ] /Contents 406 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +221 0 obj +<< +/Contents 407 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +222 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 310.5617 285.8236 360.5518 296.6236 ] /Subtype /Link /Type /Annot +>> +endobj +223 0 obj +<< +/Annots [ 222 0 R ] /Contents 408 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +224 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 300 0 R /XYZ 63.02362 669.7843 0 ] /Rect [ 240.57 439.0836 245.1509 447.7236 ] /Subtype /Link /Type /Annot +>> +endobj +225 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 243 0 R /XYZ 57.02362 381.6236 0 ] /Rect [ 377.6037 414.4236 499.2927 425.2236 ] /Subtype /Link /Type /Annot +>> +endobj +226 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 248 0 R /XYZ 57.02362 278.3236 0 ] /Rect [ 243.6935 386.8236 342.5309 397.6236 ] /Subtype /Link /Type /Annot +>> +endobj +227 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 300 0 R /XYZ 63.02362 534.1843 0 ] /Rect [ 164.5568 347.2836 169.1377 355.9236 ] /Subtype /Link /Type /Annot +>> +endobj +228 0 obj +<< +/Annots [ 224 0 R 225 0 R 226 0 R 227 0 R ] /Contents 409 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +229 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 232 0 R /XYZ 57.02362 215.9236 0 ] /Rect [ 356.9717 385.6236 483.5035 396.4236 ] /Subtype /Link /Type /Annot +>> +endobj +230 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/docs/ddi0406/c/arm-architecture-reference-manual-armv7-a-and-armv7-r-edition) +>> /Border [ 0 0 0 ] /Rect [ 398.8121 354.0236 439.1626 364.8236 ] /Subtype /Link /Type /Annot +>> +endobj +231 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 234 0 R /XYZ 57.02362 725.9236 0 ] /Rect [ 356.9717 301.6236 456.3541 312.4236 ] /Subtype /Link /Type /Annot +>> +endobj +232 0 obj +<< +/Annots [ 229 0 R 230 0 R 231 0 R ] /Contents 410 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +233 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 300 0 R /XYZ 63.02362 501.7843 0 ] /Rect [ 446.6472 482.8836 451.228 491.5236 ] /Subtype /Link /Type /Annot +>> +endobj +234 0 obj +<< +/Annots [ 233 0 R ] /Contents 411 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +235 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 300 0 R /XYZ 63.02362 480.1843 0 ] /Rect [ 137.2326 189.8836 145.6102 198.5236 ] /Subtype /Link /Type /Annot +>> +endobj +236 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 769.8236 0 ] /Rect [ 115.5676 174.2236 280.8063 185.0236 ] /Subtype /Link /Type /Annot +>> +endobj +237 0 obj +<< +/Annots [ 235 0 R 236 0 R ] /Contents 412 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +238 0 obj +<< +/Contents 413 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +239 0 obj +<< +/Contents 414 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +240 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 248 0 R /XYZ 57.02362 278.3236 0 ] /Rect [ 153.1144 705.0236 262.5182 715.8236 ] /Subtype /Link /Type /Annot +>> +endobj +241 0 obj +<< +/Annots [ 240 0 R ] /Contents 415 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +242 0 obj +<< +/Contents 416 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +243 0 obj +<< +/Contents 417 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +244 0 obj +<< +/Contents 418 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +245 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 255 0 R /XYZ 57.02362 157.6236 0 ] /Rect [ 79.15888 591.0236 135.9802 601.8236 ] /Subtype /Link /Type /Annot +>> +endobj +246 0 obj +<< +/BaseFont /Symbol /Name /F9 /Subtype /Type1 /Type /Font +>> +endobj +247 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 354.7829 127.4236 394.426 138.2236 ] /Subtype /Link /Type /Annot +>> +endobj +248 0 obj +<< +/Annots [ 245 0 R 247 0 R ] /Contents 419 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +249 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 493.2236 0 ] /Rect [ 113.11 528.0236 256.7985 538.8236 ] /Subtype /Link /Type /Annot +>> +endobj +250 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 254 0 R /XYZ 63.02362 692.6236 0 ] /Rect [ 254.7809 528.0236 303.9894 538.8236 ] /Subtype /Link /Type /Annot +>> +endobj +251 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 769.8236 0 ] /Rect [ 390.1159 345.8236 536.3139 356.6236 ] /Subtype /Link /Type /Annot +>> +endobj +252 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 267 0 R /XYZ 57.02362 769.8236 0 ] /Rect [ 57.02362 335.0236 100.3361 345.8236 ] /Subtype /Link /Type /Annot +>> +endobj +253 0 obj +<< +/Annots [ 249 0 R 250 0 R 251 0 R 252 0 R ] /Contents 420 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +254 0 obj +<< +/Contents 421 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +255 0 obj +<< +/Contents 422 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +256 0 obj +<< +/BaseFont /Courier-Oblique /Encoding /WinAnsiEncoding /Name /F10 /Subtype /Type1 /Type /Font +>> +endobj +257 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 357.5946 333.4236 405.3076 344.2236 ] /Subtype /Link /Type /Annot +>> +endobj +258 0 obj +<< +/Annots [ 257 0 R ] /Contents 423 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +259 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 242 0 R /XYZ 57.02362 598.4236 0 ] /Rect [ 366.9435 369.8236 509.0056 380.6236 ] /Subtype /Link /Type /Annot +>> +endobj +260 0 obj +<< +/Annots [ 259 0 R ] /Contents 424 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +261 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 300 0 R /XYZ 63.02362 458.5843 0 ] /Rect [ 260.5519 288.6836 265.1328 297.3236 ] /Subtype /Link /Type /Annot +>> +endobj +262 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 260 0 R /XYZ 57.02362 769.8236 0 ] /Rect [ 173.9401 229.0236 284.324 239.8236 ] /Subtype /Link /Type /Annot +>> +endobj +263 0 obj +<< +/Annots [ 261 0 R 262 0 R ] /Contents 425 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +264 0 obj +<< +/Contents 426 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +265 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/products/software-development-tools/compilers/arm-compiler-5/docs/101028/latest/1-preface) +>> /Border [ 0 0 0 ] /Rect [ 447.7723 282.2236 470.9139 293.0236 ] /Subtype /Link /Type /Annot +>> +endobj +266 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/products/software-development-tools/compilers/arm-compiler-5/docs/101028/latest/1-preface) +>> /Border [ 0 0 0 ] /Rect [ 447.7723 239.8236 470.9139 250.6236 ] /Subtype /Link /Type /Annot +>> +endobj +267 0 obj +<< +/Annots [ 265 0 R 266 0 R ] /Contents 427 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +268 0 obj +<< +/BaseFont /Courier-Bold /Encoding /WinAnsiEncoding /Name /F11 /Subtype /Type1 /Type /Font +>> +endobj +269 0 obj +<< +/Contents 428 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +270 0 obj +<< +/Contents 429 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +271 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 57.02362 768.0236 0 ] /Rect [ 79.15888 224.8236 158.9265 235.6236 ] /Subtype /Link /Type /Annot +>> +endobj +272 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 231.2228 214.0236 274.799 224.8236 ] /Subtype /Link /Type /Annot +>> +endobj +273 0 obj +<< +/A << +/S /URI /Type /Action /URI (http://itanium-cxx-abi.github.io/cxx-abi/abi.html) +>> /Border [ 0 0 0 ] /Rect [ 297.4528 214.0236 336.551 224.8236 ] /Subtype /Link /Type /Annot +>> +endobj +274 0 obj +<< +/Annots [ 271 0 R 272 0 R 273 0 R ] /Contents 430 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +275 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 280 0 R /XYZ 57.02362 268.6236 0 ] /Rect [ 102.1589 740.4236 192.1765 751.2236 ] /Subtype /Link /Type /Annot +>> +endobj +276 0 obj +<< +/Annots [ 275 0 R ] /Contents 431 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +277 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 283 0 R /XYZ 57.02362 391.0236 0 ] /Rect [ 436.0915 297.4236 537.0585 308.2236 ] /Subtype /Link /Type /Annot +>> +endobj +278 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 283 0 R /XYZ 57.02362 391.0236 0 ] /Rect [ 57.02362 286.6236 291.0456 297.4236 ] /Subtype /Link /Type /Annot +>> +endobj +279 0 obj +<< +/A << +/S /URI /Type /Action /URI (http://itanium-cxx-abi.github.io/cxx-abi/abi.html) +>> /Border [ 0 0 0 ] /Rect [ 116.8552 227.0236 160.2882 237.8236 ] /Subtype /Link /Type /Annot +>> +endobj +280 0 obj +<< +/Annots [ 277 0 R 278 0 R 279 0 R ] /Contents 432 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +281 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 232 0 R /XYZ 57.02362 719.2236 0 ] /Rect [ 328.7849 703.6236 457.2395 714.4236 ] /Subtype /Link /Type /Annot +>> +endobj +282 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 276 0 R /XYZ 57.02362 569.8236 0 ] /Rect [ 243.7155 430.8236 426.2441 441.6236 ] /Subtype /Link /Type /Annot +>> +endobj +283 0 obj +<< +/Annots [ 281 0 R 282 0 R ] /Contents 433 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +284 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 448.7028 502.8236 492.279 513.6236 ] /Subtype /Link /Type /Annot +>> +endobj +285 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 493.2236 0 ] /Rect [ 475.884 462.2236 537.2426 473.0236 ] /Subtype /Link /Type /Annot +>> +endobj +286 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 253 0 R /XYZ 57.02362 493.2236 0 ] /Rect [ 57.02362 451.4236 142.884 462.2236 ] /Subtype /Link /Type /Annot +>> +endobj +287 0 obj +<< +/Annots [ 284 0 R 285 0 R 286 0 R ] /Contents 434 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +288 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/products/software-development-tools/compilers/arm-compiler-5/docs/101028/latest/1-preface) +>> /Border [ 0 0 0 ] /Rect [ 150.9074 416.0236 174.049 426.8236 ] /Subtype /Link /Type /Annot +>> +endobj +289 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 292 0 R /XYZ 57.02362 219.6236 0 ] /Rect [ 212.4265 353.2236 530.2035 364.0236 ] /Subtype /Link /Type /Annot +>> +endobj +290 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 292 0 R /XYZ 57.02362 219.6236 0 ] /Rect [ 62.02362 342.4236 159.9137 353.2236 ] /Subtype /Link /Type /Annot +>> +endobj +291 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 293 0 R /XYZ 57.02362 364.8236 0 ] /Rect [ 181.8933 342.4236 456.1706 353.2236 ] /Subtype /Link /Type /Annot +>> +endobj +292 0 obj +<< +/Annots [ 288 0 R 289 0 R 290 0 R 291 0 R ] /Contents 435 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +293 0 obj +<< +/Contents 436 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +294 0 obj +<< +/Contents 437 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +295 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 228 0 R /XYZ 240.57 446.8236 0 ] /Rect [ 63.02362 657.1843 68.7497 667.9843 ] /Subtype /Link /Type /Annot +>> +endobj +296 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 228 0 R /XYZ 164.5568 355.0236 0 ] /Rect [ 63.02362 521.5843 68.7497 532.3843 ] /Subtype /Link /Type /Annot +>> +endobj +297 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 234 0 R /XYZ 446.6472 490.6236 0 ] /Rect [ 63.02362 489.1843 68.7497 499.9843 ] /Subtype /Link /Type /Annot +>> +endobj +298 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 237 0 R /XYZ 137.2326 197.6236 0 ] /Rect [ 63.02362 467.5843 68.7497 478.3843 ] /Subtype /Link /Type /Annot +>> +endobj +299 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 263 0 R /XYZ 260.5519 296.4236 0 ] /Rect [ 63.02362 445.9843 68.7497 456.7843 ] /Subtype /Link /Type /Annot +>> +endobj +300 0 obj +<< +/Annots [ 295 0 R 296 0 R 297 0 R 298 0 R 299 0 R ] /Contents 438 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 397 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +301 0 obj +<< +/Filter [ /FlateDecode ] /Length 707 +>> +stream +xœmÕKoÚ@…á=¿ÂËV]€ç» !H¤,zQ©ºw`H©ŠAù÷õñAé¨-R2/¾$ƈo¼|Z=µûK5þÒ7ë|©vûvÛåóñÚmrõœ_öí¨NÕv¿¹ÜÞ ¿7‡æ4÷7¯_Ï—|xjwÇÑlV¿ö'Ï—îµz·^VùgóýºnÚóûÑøs·Íݾ}ùÿÙõõtú•¹½T“Ñ|^mó®ÿ›Ó§æ«ñ?·ü¹àÛë)Wix_S¹9nóùÔlr×´/y4›LæÕ,ç£Ünÿ:W§)ïyÞm~4ÝíÚIÿš÷]½x@'öZúN“z‰Vö +mlA;{¸>†N =åqEßñozãɇã÷lC/yý½*lE?þéºð×uÑ©h)Z‹¶¢½è(zZô]Ñ‹¢ï‹^]øëÂ_þDšþ~é›þaý ûœèOØ·D¾%úû¥oúö9ÑŸ°Ÿ‰þ„g”èï—¾éï—¾éOxÖ‰þ„gèOøl$úûe4ú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_á7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßàwú~§ßáwú~§ßáwú‡ï §øÞpú~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ÿ6!n“³sîmm®]×§aƒ#gßæ·yy:žp~~ÁÆŸkendstream +endobj +302 0 obj +<< +/Filter [ /FlateDecode ] /Length 19706 /Length1 37228 +>> +stream +xœì½ xTEÖ0\uën}{盧/7!’°@èD `B$é˜a‚„°‰ÑÁ€è F™qgðuÜFDÆ—’ËwªnwÇq–wþçùioß­êÔÙÏ©SÕa„ÍGe“˜œ×mF7xrŽÜÂò‚J㋦ápøΨQQYh*B\Ük%•SÊì1c*B<Ü£ç§L›U’·t±Ü_ChðùÒ₢ؼ¬Q ? ï{•Âóé,BY&¸.-¯yH‰¾ûÏpŸðöNs¬ 7¡‚÷,/x¨’?+>ˆÐÈCp¯V”‡»G< ÷€ßÝr¥»ºææ94¡åéûʪâʾÒ_àrPŠ0oÁ+‘×)Â!L?“ߣÎTEBdžãøÏQÅÍß¡–›Jt~TSvIFr!õæMÑWóÅ¥r|!á›ÝDú?Œ0;›ˆv5Ÿ=å€<Œ(" ÉÈ€dD&ôÓþ™‘Y‘ Ù‘ù _äD~È @„‚Q +Ea(©(E¢(bP'‹:£8ÔuEñ¨J@‰( uGÉ(õ@=Q/ÔÝ…RQÔõCw£þhP•†ÒÑ@4e ÁhŠ2Ñ04e¡h$…²Ñ=h4ƒrÐX”‹Æ¡ñ(8|/ºMD“Ðý( É¨¡bT‚¦ RT†¦¢Sz¢Ft>o¢]hÞw%ðøAxÒÀíC‹Ñtxò6>Ž—qÝàÙvt‚–µè8ÙÅ#< °>íÏ +ºŠsÐ+#ûâTIä?’…Í7òŸó'Qo¾š?ÉçóÕ8…<#ä +ÛáH%ï€L‡ñG¨$_’rˆÄ[ÐGä$Ù…>…Q@/`Œ:´Í\|±Íãæp£áÉQá$Ú7¼?‰7ãS€ÝA¼Aë Ï E›ñ ë8úZDr¸y E)\ à`„þQ5þ hƒÆu…g€=Œ5™}‡’nÂö¹‚æÁÈ9h«Ø(úJQ0 +åØvü6¾$®F è¹b ž´ÓÏ +›Éçã]èK>_š °ß¡Á˜¯p£¢tŽ™¢ hê‹“e€)}ŠNJÃøD褹@5BnÒ$é†÷{Ð>Ô¬Eu‰Ñ+öþ=7ñÍuø1îoè$šWÂ_^ƒ²¢µ½*‰O8ŒâUÛ^.&³h¯ëžñê»yÝâo¹Um’ºeï5ÏRoÞÌÏ y{…½$FÞËÇD}|§—w‹ž=^ÝÛ’1È5#<3.é<†çƒØ;:è^!þËÌß«–ªÚêó¨­¸¸8•hkùa+X¬„‚\&þo`Y˜Çñ(±éô¥îÈvúÒéKI>ö{L„=¢„GÍÕ$¸ùSm­d¹öm•‡xŒø)ðÙ,ï.°¡|W¯Œô’ M:–:là{¦Ô÷б€wMÇ¢‡f¤$ñÝÓû Hí•Ù=x¯üý&9 aÃ'‰†°û»Œ°¿Ø|ɦµ4]¶]N¶§¦:Rþ©ÝQâå¿i_Û¾N¶]þà2QvhFÛ$û9}E) ‡ã(hßÛ£ìfê]0èî¤I] â IaÑ1Ìvþåå‘ίà•~%á;0ÅÿLôw&à¨H^ЧN ÜtêÙ£WJ²îÀ•õ·áàsé ÓëŸ{þI×ìôËÿáÆ?d.NÞ‰%SnÇÀ¿.ˆP_|jËžðÈy¡!Wûþ·!ÔÁrhxµrá D#Šuùˆh³,01P"¼„±Év¾ùÄé6íô¯A¯ö»”œ„£zú¤}ã§¶.7Òoòå x€öæ ìû‡° d³ˆ(l)PÄ„Ç`_Ðç/·ƒâ$Q>1ì£6ØÂ™ÝÚaìÚ;!.á/É&nØšm?·œF‰GH3ØW„3¢ˆÜÕü.7k%‹¡ûˆ†ÏðpÉf´@d£‹lt:ð%ϰ˜×‡Z¯½‰°„Sðûd18^5%&ñ>ÀÚcËI˜‰¬ÄŠS^øÅÃÏïž;w7wýáçŸxîîÝHÇSÒñ´þð¤hžÐ;34¥»®yÐ~Ýwónˆ>W`¬þh•«_bRù‡&õâͱv2?jêr¤Ó;Q¶#¦wú­Šï’xŸÐn†^Äg\j·qªaœ)2(µS$—2H»Øtú"¸Ûe{ªÝ‘j÷Oë·5]½xÉö¸x Oè›$<|¯’3|¯1çÞá{MôË’sïø×YdÇð=àæá»ì½ïÊú‚3Õöñêµ?ÕxÝ èlˆíÉ, gDœ-{õfßÀ%%~øÁü‰÷öíûîöÌ dG„„¬ÅµÝîM¾”[½566{È„j«„dQâ¢ Ô š¹Z«Óäîã°Û,f“Aö&$AJ€¨ÚšO°Úït¿~ $ønJÝIrÙØÈMF³/drø†Ëx"Ž FœµaG”;zF°ƒ÷×>ÌÁ1ƒJqtYÃ|—öôhÜ_{²´aŠvnÊÓ¥Ú;8?G{—•ÅÚ>R«à-ZÁFmßm2ÞL xäF¼ÒH´K;Å—‹¾×w†È÷ºkl$§•XÜ©3g4*¡8,„KŒK æââÓ}ì¶H%8Ž÷ ]"ΉŸ¸D°G=ê·Ì†º<*@f-û)aR\Äi3,u’Í‚˜Dea ò¨¨ë¾×F݇• G@.ªWýû5û÷¯CuáuIÉMÌû€KïãÏ<SiªÊ–¯Á+µž¨©á(¹8»îOz¦8ÃX\µëž‡ØuÇdgž‰{¥—ýnjÉûîýñDÑÄ=cƼxß….|PT3ûÁç-˜£ÂݸnÝ^q¥aünôîµÏ²|õüb—^õʦo[ ÂÍSÇçæŸÑFÙ+&Œ/¥º) o^â˜ö‡ùR,Ì.–¹zE[c:Åt²ÆFǦ£'LaO$<ðD´ø„é±NŽ£WõˆŽ1³Ób0[#Ì]-Áfkwcyí®¯ÇóßkfüëN¹Ú&o»ü7Ý…ƒš_L¾Úï"{b»¬sMøšÀ'o–Ñš™é©Fûç1)8ʧÝ;áýq……ãÆŽÝ|ðµ§¾Ö¼.·pò¸q…E¤{Có„†ð͇^Û²åÀAnÕšGÖ×/\T?ïük¯;÷Ú¡s\AýÂGÖ¬ydÁÚyßÿ¯h>÷ÚëMw X-èŠÙáÔñ™ü-öv›¹ÏšŒñD×>É(DâBPçü]PÜÉÑõp 2NByx‚2Ö1!hBx^Ò”°9èI´oàÖÉõÆÕÎz¿úÈ Ý FƒI¶›bM¸`C 1Ðhñ qú…†'Ç¢XgˆrtñéâÛÙ™˜Ü×ÐÑê3 y˜!Ëw¸spà¨ä\<ÁgëÈó¹/üþ䩦2{~òt<Ë4Û¾­Æë¸za„ -òFÓƦ•É É{“S'¢‰z@sYî{§ˆœÉ?MkQûXß–öâoï{ºáþí™ÚÜÜ7]$ðàG£\vdÀ›$"pNù+¢S¦ó ˆ¦4^œ†sS¶C°¤a¼g„ìẵœÚÑrŠë&ð-§vÑ‹]\7€¹ù¦¿4ðÒ.äø‹ô?@Ö±Ä8Ø›%ùWh^£µÍ#>âæq‹@‡ìûÑ&òsæí2t.¸åSnÑVŠ÷9øÚc@ÛWÑ"Ž‚e£˜2ØQçNÒ4ZÇ»™ÎíczÙÍå‹‚ Ãã‚"éÜPQa’ØÄ„ +Ùчƒæä]-×@!¿/×}iíÍ |7æÀü¬ÁL«+ !Ö0â  ®Rý¾HCLç9»Í‘’ ›Œì6¶ ßÜòMO=ÿ=õÔ lо»qCû„lí¤vŽ“0t +îS´jm‰V«UãÇð,<?Féþ!~Ъîr¦“žkH¨Á ‡‹!Ýö´}¹Áw8dƒ˜ „øÛˆJÞ Fï牥€ aôô¥Ã‡Yðdø´c¹ð5Þbc>»’Çò¹B®4›Ÿ-Ì® ”xÄòA|°RƒfˆÓƒªƒkB¢% ƒ/ ÙvÛÁÆ={!}ºOêÙ&?<ÍJD„—qo6g#S +F<·äþSÍ>=þ ì›qo vu×®]3ñª>åë2g®Mx¢{òoÝ·­2TûŠÑ¿ d^ ôwF•®äôQ–—¨> NsƒaµÒ ®ŽZ%®p>ç⃈o`H'ÕB|à beƒ_Ž—Æ`¸}þwébÛ%aà EaájQžæT|D»y\§®¸§~Ñ@2`Õ³Úoµ/&šónùGlÛ³¿~ó³ëǼQU},ï3lzœÄ„7­üðÛ˜˜·»'¯­{¤~ûÌÊê9Ñ^QÕ÷÷=ü<Õí"óVÐ+¼ßW(63"ÄœŽˆQj€YÅ6)(D”yó½Æord¢„îN‰Eì‹ÔCµæ‘Ç@¼Ç¨P»Q4å¡24=Š$?ÜuÂ]I/<22çâ<Ï&‹±„ ó9’b§êkˆ¨qXë©9s¬e’Ó|œlNÙ¡5àü·™Œ6ƒŒŠ÷P4ÉÅIö%¶Ð É·Á¶ÌÌ5 æÒÖ0ÿ¬˜‰a¶fÜ^2¶vÄFm„dkºL͘Ú1HkÒåCr9!£o/*I`KCüøøë8Z;­}3éíÒ ‡xá½÷^¸çéáÌ.í «U»üç¿hUÕãÝ“öoÚ´?ºó+u€ÿZæW¢ÑxW´ˆÌKL¨ÁOlñÛfk0-‹\²"Æi ó !áÁ1àh@‘.2Ws±ùb› +¹|£ãø$w’œä ÇE |_7æ<í’OÌrŽxI‰R©[ŠHöã¶.ݲe)ØõdÖ»§¬}÷=ð1´+Ÿh-Úeœƒ³ž$}>óôk¯=ýÌAnVct'í[í›qµo¾úLû3sT“ñ¶0}eèT)ÈED…®ÁÎŽØyðÈ„ƒƒ%˜¸5±¸šøL…4þu˜|Òà !ÉSüp9ÆÃ< ©ÂPa +Ù‹öŠè GáˆäpË'§°Ö’"œÉ½¾@ ©xÿåÀãåŒÇQ0èŠ ÇŠ aÝ«ÂVÄ>›`Šî⌱À“ƒ;·Fü +²þ¦KŒ¹^›ewz±¶-›§ÙVtŠ· + f ù—·è·|å¶m+Wnߦm[¸ +ÝüŸ´U žxVûî»ï´ï¶]µháêÕ ­âÞÙX[»ñÉ%µsÕ}ó_þío_ž¿Oy„]{Iû4kîïѨpÐÈW¹÷aëWŸa?ܶh÷†që¼úDiºŠó6Åj!.‹¸ˆßá•5dVÒ‹/,ÈSqåÔ)êù(M¯#Ð܃õ7 —dÒf~ÚS…ai-Ê_†\ŽbÙÈ)š¨–³Þœä,š$œD4ÂÕåïT¯vˆÏI˜CÑ|h5+Ù~¼l……s7®ñ2-W‘+Ú*mõ~üþvü>«Sá|áyÆÃ3¤Câ&HàQeCSrë +ø$ú9ÛZözfÇ•]žÜ΋û(W׿WéÚŸ“D.àK”C¿C‰ ³1y‘\Ù¯%n×÷ãr&ƒÕ`p{A/ èe±«kt˜Aä•Pù.ñYj«÷_ 2^ Ãæ >ä‹:ùÄÐÈb¢~؇EËfOÍ”Mž“«—m'áM÷Ï2ÌVf«z-Ë' + nîÿ©AZa¦DWpíñãïüê® RSMõrÁ¤7§4~4tÂøÄXY5 ¯ÚX¼07¯ç¤îyƒJ½ë­-YËrs{:ûõÐó?m“ô ð ø,TïJ0ÃÎ@g0Ù™eí‘bÝ´³·swôÎÞG¤ô Bb€©sP×°Î™Ž®]:gÆß=Âvþ[ÒúavG™|º‰-pœ>b{çr2„ - é V‹`eo&õ:qó0ˆv|\º74XG†L9`$N¾¿ÁíV8ü{¦è%™ØNÑ”7ú´È§³GÿŽË'RÐ2² ¦o×xÎc+gϪã"ú=9e÷ïÿðü”M}ëžØ6ÀUªÙ;ç“ü§^ª./þO-ø¾tÂ\íìúZãüùK–þbýúiüÀœá£´·´/¸Àºg·>¾bÛVmèˆÌïß}÷úð¬E-ªßG/=p({Ñò4W‰öË7·hžZZ>îwÁ”EsçâÌ×÷ãasçÕîi˜üÙí{í·"å¿®û³EAo¹Ò&`z4Åv…(ÈÎA΢H Šô¡ÁN™¾€,Fª§9Œ@«†¬hhÈ”›ôšáÅÓ—í ­'ùko8Òõ~Ÿj¢ƒt+¶rVÉ*[Ñx4U¢È a™‰÷Ã\.Ïe›¦àRî!<ƒ{˜Tñ3¥‡äZ¼”›oZÏm ky=É¡³ A¢¸CÚe.F›ó)—ú»¥-÷/=#XZÉžë]ñ€EàÕxгO=õ¬vw]³jÕÍÈñŸ_Ÿÿpý6íÊ–/¸c-Ö._±˜+Ñú»«¬Ü~ø¥eÏøªÇ׿ûGPÐꛄXð¨—+Èü´eRoÇO£=<˜¿}EhFI¾¶ Š¢'éÕµ¤W¬ÁáÁ GsO^¢/×y“!¶äó…7‘vÛ0ZøyÉÔ¯Ñ^Ðfã%xÌ’¯…ÉgÕþ ÕŽNºÿÔСx Ià-C˜ …½>&¸œ¨Þ´ÉœMAB 9…x›Å¦z +ýIûò}Ã<±/&‚ã0^}¼`¸ö±v\K‡qöáµZ©–­‰7f✀ã±ÿvm6_û…¶–ùd*Çå0¾‘Ž.Öó\=Z ×ó/(6HŸò&Ê’ÓMM­òJÚn†ÑYîé9Ž‘½-AÜÑ–TîZsšZÞÕraW+ü(€o@q.‡>ÿ\ÑëdQÐVc{ÐQÇÈ„–J.»eï{êÐ]-½‘G–4w +E©. Á¸ž×ËŽ§í{œõ–UòŠ0…Ø{ð)F$×—š/67µÊT;Í\Z «€«ñH‘÷o/_þmm?瘮}Ö =£MÇËñ¤'°ä®l^®]Ö¾Æ>ØñÀŽ3xÕö–ycÆâ ¸Wà Cÿþþ|í×ÚûÚï´_Çxiú2ÞÆ»|åzî-PD \¸Ë€½¬mfD¿‹p‘´/›q¦Àö}qâØ{Üÿ¼÷^K$Ðß²‰+ºÞ•rÙ¯fu„„WыNJ[6¶ÊÂfÚ¬ö€]æ$A/lÕ [‘B¨ï:d¢øæZ­„Á1¢t—‘CR½°-0 ²˜êA±Ì‹,F@¢ByHa›]æls¾¹Î¼ÅÌ`ÛDÏœðØ{'/Œ°¤Z­}{u×Ú·Zy2•Õ ¾qu–í±HvB¾Ýë9ÓeH>zÑ Br!Ê £®%l:×֚ڼΦ0ûdBá¾æ9ƒìÇu:˽¹^By7X(å¦p3¸™Â"n©P'¯áž”?çœà#ƒL%<³@: ]Å.R/¾—ÐKì)%™Òˆ‹Ï\¢Kr™&“|˜AL‘f +•¦åd¹ð¸X'Õ™6’§Ä§¤ýä—Ò;äé÷äé ò%ÿ…ðgñ;rMø^ŒŸø šø 0GPˤºó-Á$Hû[K +•í2nfËÐæ ÜoZº£V»™ÊÖâb\&f48„ôA<­Yéë%I.C’”-Í'óy^W0Ä÷¸ß7ß,?³K‡!† ú•«±K²ÄÙ1'Óá Š‚”bHW$ŽÈÀpÙÑB‘ ˆ!|øn¦¶C½5å;O·«v·&tf´¯ÒB9ŸKh2pŠ“ó•|”N\'I•:)ªÒCê©”qss¤YÊ|n¡´PYÉùñØH|p0‰Âñ$Vîlèû‘\9ÏP,O5Ìg|ŒÔã'‰/›Kãh=6ŠrwÃsñ<ÜímÞqm^“p¦Y&×®w›®ܪg)ÌïÌr…IvZg³Cž‘Ä©‚ˆ%.„ï%y|P³^JMd+“mÚÅ´*œº$WR/î.i(7D*ãJ¤ùœ$bƒèÄAâ`œ)ŽÃãÅb\&ÎãGÅz¼QÜb´1¬ÁEÛ™À±[Û¤]i™ +ØÞç?¾Þ•ÿøF8øêËζ«áÕ;P½^à ´¦@§-€¡×®†G]T +«ÞÅêîŠ}“ØóZ &çÏc¬Ý<ûà‡´¥ÚíZc²´FíSí3­ÅA8ݪݫm¦³¼æÇ0CöÆ"þ1‹|PWÄ!Ž6EæxØi8òÕ½–®¬Šç2ZáÎÎû/:—Zã7‘›îþÁ«µÇ6n|L» ¿{ƒbxC{OHlù͵KžØ~á܇Ÿ´ì ¼Ð®yxŠF»ºØmœ›Ì& 6›MéÖ0cN0Çf¶B¶ÌXæ• mc`ŒJm—@ÁÁ*yíXç£/M´1ëÀÀX¹úaD”MçîKùùþÙxý7Ú‡ßÀœe. LdLmÖ÷Æõॠ+A/¹zBžGÑN'í»& ÞÎ×£51œuSŒáÁŠ ´t“}#;ÛÎ7À/Ò‹®z>´º¨öëlta ’£‰ûÃããFÅ=—c…‚ðÛ5õÝ)´z­>qÿ¶—gnŸýÉﵵϧ~3Î¥ªÕnœóÉ{Øÿ¯e¶¾Ó»×ü…Åá]Ïî?û§¤Äßf ^ú‹Š‡Ãº~þÈÅN4Æ^»¢û$4Ìeugî‚ôÇ%ȶÓ›/2;JÖ÷ÚÀŒJfõ%ÉÞú’2„#¸pÉfp* [ †‰Ä³ò!òß´\>Þr¤ëghu £}àSâ`<;r¹üdÎnDB½e…-pÈ!Ê]QÓma-é&Ù“eÂD6ܧÎg‹¡aEŸÂ#Ó-eßñ=o¿µç¸ö§ÚGà|§_9uê +YÞ|Ÿv^ûwÁÑïÜHD¯ºbyç‰#z¤'tï²c”Nxü"¢ $@Fèÿ€¾CœÉ–©ŽOF¤ Â&¹d‘D$q2Oý±/Ä ]P'܉‹ãã„Q•ïB)8…ëÇ÷z‹CQÎà2ùLaˆ˜‡rÅ®Œ/f£0-šÅϦ‹óåõh6“!̇¸a-GNá³ø¿k9 +¾ÛŸÿ'Œ!$m§±Ïqe +A¢ñ”R $H1*\¦»)DrÁÞOÈ5Ak;B¦tRâQ6ƒ¬ï1JÈl;íÙ5r)9ùöÁ¶õÜ:D,ö~+r¢À)W)¥³ Q·?×_è¡$)YÜ!]q)yÜTîaŠ’¯Ìáæq ó„ùÊZ®^•ƒ €º™K<èžd@^QLÈDœ¼S4Ù,*!¨¢*©r”!Z‰1ªÕÒëCzò)B’ÜËj`J² Fƒñ0ŽåLB:ÜtÙ%» ƒ”&—ÅeÏAŒ7e[J¸)¤€Ÿ,ä‹ùR¾\d(RŠŒ3As¸‡ÈL¾F˜%Î’fÊ•òC¦y¦y–%\-YÊ/5ÖYÖñ[,/Zî¥–ŠˆJ)Ê€£7z~Ô–ià»ßÒ@bþ2= ?°]¿¢¯¶æå%.ÑÌ‹H1ê‡9Î+óÑ<…›–É2P*‡¤á{ӳǻ|Yšj°ƒtY® +‚³’l—ZÿsùÂ+ É^B‚1§[á_žMŠ$^pWãég5•Cgµû´¼?rNÏÖ£”ækÜœ–Å$”úŒfðŸ³X¼ÆÕÙ“cN 'Â|¨"Éé4™9‰w àM$ó&Ž7!¤Pué'p¾\O.‰KÉ æ\œKpÉ÷p÷÷ÈÅÜ#ÜjÎæ‡ƒH¸Ò Ç‘Þø.âR`ÎJ"•Ê….ˆÆuº«÷,ÞŒŸ<Ûrå8P±‘+iþfGõö>às8ËÁVº‚Yþ)Ó¢{º Ô  r8G¢B«l´¾¤°åQ_š»hoîv¬Â»\ çR€饞’ • 1ÊÈèG‚d›1ÑØ“¤ÊŒCÈ0y”q,É“KH™ì6Î$ÉóŒ[Œ~žâ<] ÃÕ|}s69zãn²·yŠpfã ÷®üªÖõþñ¢/øí¾.+¿SÜÇíD/ÁÔ• DrkeÍ‹Úo*`µùD2[W¾Ê +¢´ž±ï¸rExuß7×1øÖ‚ÁgÀ“ üÁg-’¼‹-Óµ`q5…u”í ¸&¹ld'Þ'P\Ñ@^ôl³ê[°è®¶ûŠmcb˜ +ä°'çÖÆóè{íêD¾ŽÖkÉáóá.\5Z  :þÃêè˜G©É!.ü˜6TzŒ·œÂ˵é\7ªÇ—´PÞWÛÍö¸¢à÷y­%Ð ²À~Þ÷Æ´Ýuuº¾ìâ¯pËÄhÛÝeÀ¯ —y2ó¶óžž*¤¾•geG>zÙ‘bÝ8hƒvP,ÑÅ3€Ì9otãç€OŒA‡\±áFƒíôXìê’ðƒ!¢í+üMÈŸ˜ ²1œÈ¾€)'NƒÿÕõ²Éæ«t'Û+K/WERhRXRx’š‘9 Öê +s…»TW„+2;4;,;<[ÍŽÈŽÌŽ­Œ]ZV^«ÖF,Ž\Û{%6ÌÛÕÛÉÛ!?,?<_ͨ « ¯T+#æ‡ÍŸ¯Îh¿Vv7î ‚j-¤Ft(-so|´{{ÃÆÆ‡–î>ÞrsÏ­ËߟSüÆ„ÿ½Â¥”Ì™\}ö•¸¬–»J +Þ|æõÃŽyËvÅÆ6Ó|õ ðj+èòÕ»\ä€Éj8à\am ^ˆŽ!&QÌrÒ䫬¶p‘®D¹œ´??l~XC<½ë/€*f‹yXÓ¿ÔȧÏ=ñÄsôhy¼ÏKsN ›7OÌy©Ï\âñÏ??7º¨@;¤]ƒÏ¡‚¢€ ¦ûêÈç Ã@4ÀŒ–ॼe‰y©rÀÎðo¤…;‡ õͲ5_ôîl´$ÿ×ËtZl ž¼2¸!XÀí’¾Öýöžù|äSÙ/9òröS#Gl›ØYL7,Ž}†ï¹»k× 'O^èÚuWt4dÁÜ'ŠÍ•/~`hÓùtY|ò +K#^é6’¹!v‡1#”™Xrr+¿š:ð‹–y˜8963ñk_/'Ï46öyéáã7ÑÍã¿Ôr8·cpìç&}iGQ„eø *МzðšüòEÁ¨Ò ù¿a‰¼TpîÄÂ~-à€£Ñ´"$ØÉÉN çÖŒ†b“gáÕKúâíU}.n@hehCèoC¯„ +Ð<€à,ÄK‰r¢!^q#7vsn§;Ø0ñAÊâý7­åQP‰±]âç5ï3|uêÑÉ…¿}@»ªÅqÍŸ`©‘Û¶tã 7iÂG{ôØÓ%ß…ìƒj6­{eÏfêá×€×>(Ï"ذIÞ)âZ´Î"R8 IA6[Y¾ÔÏ)Ô)u§la×lËqSs¿¦&‡¾í8™®³%³¸ìw9³ N:%$C±žTGõL¡æÅ]Û[8'jïØ»wÏë¢ï†ìÒºæDò~ÝÈמ§¼Örù Àk#ê ™}T )ÔàXâãwÀJtŠjŒ=d8`}=(´S ’MCD‡C͈cë·º:4]ÔB;ÃV^@+ºÌïÒÐå+ò·qms“»±GUú +yf[ýšmÛÖÔokÔ´ë»ï¹góè_¾’ºïá_77ÿúá}©ÜÝïž?ÿîÑóç¿Ò>Ѿ {9¾Ë뿺·p2¤Htµ»ÏäB¶ïû äEŒ¿=Àò ˆX°Xk±7šÖ)r‘Ô7fÓ~føýè4Ýÿ•´/ßÉêÐQve;ÝÀÍ|_ÔøðÃõ»Hyú›G¸­-÷q›·l~ckK­èÛ²¹¸èjCoÂà³`\º¦ØfFoð/¡Cœ€e n][½ØL ¶Ö½Û«u±¥Ö7៣AôýàÝ<§å2xFdEƒ\!FNB–7LR­ð::dzÉ&Ûq”Ë&4ØÆ _Lu´­¡3!À@v—=Ûžo¯´ëùzë™ú€Ïþrp÷²,6êŠo*Ø vþyy¸ ÆV ÒÄݹ®y­ó6e4¸caóâ ›6¶Ês?Ï)²ËÅ ]å\2h¹š›),ä– Ë«¹µÂ:ùYÎA«™œ‘(RgËÓZfWÉe*%ù¦ed1dЉuÒF²NÚEžöKïHHß‘+ä;þ +D«”´HI3UéÁ\ÌW-{¸®´= ú6—á -W[vsQ-½m²‹|­ã(5­ûÝ\f›àÝKyEu°Dßï/yx%…‚ÝD¢ ®N¢Ã`Eb¨ä4Õ†ª¤1øP MBv«,‹ÙvÙša'Š•Bš›/é«®ýú]¼Ê +’T ]>IÑÙÑ•Ñ+£àó«è¢oF@+õåÖöºÙ¦¤N]Iã2/|ñUÓ붨šùØöì5ûy²ìáý„ªìÓ›¨Êr›ŸyòW϶Ôòù{¦L~µÊ»hðA½:ÚÌ¡ÛÛÌE¯Í¼’ïü“»ÕjœÇj`hj4ºŸÎ|Ž?øñ€05Òz¡Ãzq83nÙóçŠ8ÍçIóäy†yÊ<ãÓ<ó<Ë<ë<Û<ûGCà•@{ÇÝ8¶V¯Ùý|ýêÝ»W_Áíò•¿hß`;ùèócÇ>ÿâÝ£_nÒÞÕ.i_ƒ3OŸí‹ïb±ñ øÅ­€#ý]ÁÞØØhY_'‡B!.a²]6a»xÑ]=>þ)ŒÇcZ™ãI%:¤Õ´eÜ]ÞübGËQÙÕ.—À_y¤»[ý6ÃÏ›ë4ZW¿x(”e:C çi½½ø¹¿,p{9]àŽÂ‰Þ˜ÍU·Eò>­OËžva¼h×÷óêøÙ!—÷` FRki4’¦~ƒ4Œ0ßqûô ¨_ÉöÙâCµJÏqÚTÊŸ ÏŒßôpêàbŸ„òŠÃ~ü–} P%…‚ÀÆsCŽuÆ‹EŸ{êrc”›ÊÑ:×n>·ŠÛÊÉt 1°šx â;!Z܈ãU¹'ê‰û>|’LkW™$“, ]r.ÊÅy$Ï–KP .#eü¡TÌ—§£<‡Ìá§ ³ÅÅh1^F–Ad]"®Ekñ:n#YϯÖ‰;„çĽòaù#ù¦Üß[«ÂQw¿'áIok÷]çó›sÈî LGr=G&ü•+S«×Ç*2–ÖÇþ¤zâ¯nSOÔ¿i§ûu­;wŒ:#)g±þã,³gk—¿ÿp»n +œç'D*=•L.S¬¸”{¹{…±J¶RÁU%Ê,Æ,ažPËmàÖ k”CÜ!á×ÜQò!Tà Dä‚" p29¹@âÇ Ár°Á×è4ÑÕ‹(.–Dð1B¤)Åȱ†h%ÂeJ%½ø^r*­;rCÉ`Þŧëkµò à e‘Ö©s¹lþa´8ZÊ–Çr”±ÆBT„‹¹©¤˜Ÿ*L§J†ã“Û2Mdz¸¹ä!~.Èwž8[š'=$Ï2Ì3ÌQfçšjéê±eZ‡×p«É&þI®šl]‰kM[,ÛÑv¼•ÛJžçŸvŠ;¥çå­¦-¿ä^"¯ó¯ †_Yš¸·É þ=aÓ‰`LÿÃQF•ÛøÙ§g?û´Q;wö/ßžíXK¦ÒãFYÛ<t¤/ØÑ,Ð#è,ÐåLÞNx‰žs˜Ø9»Z*vƒ‚éɨ€Êì 0éŠÄc^ã8•ôzH´ÞœdÎ$ƒ… Ã0£ËœG­•Ë#ã„\1OÊ•s yÆl³Ûüžg~¯‘žÇ[¥½æß˜?2ß4'ÒíN\«^YòEÚx×Yí vð,~Y«:‹ãpŸßòQË›¸QÊ ãü´qóe;P_fÅË]%™3Ø‘•²!«ÅnEV³ÝdFôd1ƒášì`¶éf£Á†ŒB-yÝbM€Ùz·z䚇WOËÊI‰Ðúê·ôÝÙ‡.ÉáG6¯!Óô¸<ò¬s KzÊ$ëûÁÁ\ÒåèÙ)È#ëåi£¾ýÑk žßz1+iJnºc©:úÄNÜ.SŒ²Õ@‚宲jìERå$#åWã×@yÉ“ï7æã|®„äóùÂdyžq¾ñEcp‡bõƒdjK÷Jó\î•–b>Gó¹Õ;H ý[HÛÇûÃ|,Ítõ´:bŒ!Èæ 5 +jAÆ´68wú¼c2(B´_ +UΩ«C‘´õçzR}£kªgohó¥&:©€¼À‘Úº 65 ïƒP:Q ™ÉÝØû£¸êÕ×SUa{øBi®Ìû÷ÿ~ç´Çï¾»îß÷ôXî½î ¹½±r͇߬«©«®¿òáêºñ]{êñÀàÇ7]{l<¥×Bñ1暎W1z‰ý’Oÿ; ôät[Èž]bè5$#ýï2´ñ¢ÖÕ+<&ØÏd•ƒÎ@+/¨î F;£[wÚ_Š rZ1¤`AÎ(‚ÂÁTù˜Öôƒ×ÅÍ*I¼Î™d`‡)·åþ‹Q»ç(è¼ñ0¤wnåeH‡ºÞƒWŒàvO»bð€ïŸ›öxÿþO{îûoäÖ]ÛôxpàãO]{|\Ýê¯ÔW×Õ¬ûæÃ5lO5>%œ#ÝQ(ŠrÙ°-Ø„|øMÁ>›LaÈf£[ël§›O_²Ö%ÅÊž±è§µpìïG?€¡p®ì傊'Œ‚d}rÒøÝ“éÝjE-ONÊÝIºï5¨/Ï¡ÿˆ1ûFeôc—Ylq“÷”qÆû­ýþŠÂu1üéúû©Þów4gYò ô'Àv< +úIåZ(Bí»®ßcÉóü•«¶Óø“¨DF8˜| ÚÄЏåh߈S`¤"¸¿O|¥<ß"à¡Èô<Û,ø£]ÒFô x7ZÀ¥ wH8Ú ÇVèw·§ÏfîƒyŽÛ‡´¯…ãc8Ö± Ž"8(œ:8vÀ±ŽÐö +›½cÓƒ€VNµÂ,dæ¢cÂZT-ÆÁÙ‚ŽñÑ11îytŒ»7× +àùtxþ9´i†sªæOég¡žù¢ZþÂÍëÂ9´Â”¾Dƒ„9ènxÖ çû(-g8e㣛—€®]üçhô=È— áü  =Ƚéµà@¹Tô&—zóÿŒ~-GésþSÖþ mG†Á}Wä&Q¨7¼ÛÃ~-G¹pîK¯ù4^ðLjۇyz¦ãƒÌ"Ñ»x^Š÷ãøÏœÂ©\ÌËF“Jøäû…óˆ_€ßl¿?ú+þIþüöÂÿ`@vÀ¦€/{nüsÐÈ Gƒö] . ^üEHtÈŒ#¡~¡#Cµ°~a¿·©‘êň÷"“"7GÐõM#9¨+*E&¶û{õ8¼“óƒ3ý[_A0éòêðzœì¹Æ¯]ð\sP¾÷\ƒW†žú5×}<×2qùžk)Ü"ϵŒì kýÚˆB ò\›Oužà¹¶ }'{®Áõ}ÑsmG|ßw`DÌ [^cä‡{®9$ão<מkžkùq‘žkpƒ=×"òåÊ=×2Šä÷\Q®ÉsmŽéCÂ<×TÚçšçÚ†üú®ó\Û‘Ü÷54¹Q%š…ªPû;o5HEQ!Šƒs2J‚O +\M†*J‡65¨Ž*TŒ +P9Ї§™¨Ú'ÀUšn…UÍîŠá\ }fÀw´T~¨½ZGÍ‘fÀXô¯–U@kŠGôùÇFWS¡_.š- +¡mƒVÌz0ŠT€Rß•Ðf2À-ƒv*ôwÃè줛ݕ³ªÊ¦”Ö¨ ãÔ䤤uò,5½¬¦º¦ª¸ <^ͬ(LPÓ¦MSGÓVÕêèâêâªÅE ʺö¢]s +f”OuWLQÓ JïÐqPñÔ‚ÜéjaiAÅ”âjµ ªX-«P+§OžVV¨¹Ë Ê*³Ž$ŽaVÃc½ó˜‚ +¸IbÜôù¥»Ýü´.?¥M.ãv5ðÈÍ8˜ ¨´¦¦²Obb1=¡Ú=½ª°¸Ä]5¥8¡¢^n‡WG¼zúCk ï¨Þ3Ý- r£™Ð–jê¿Fÿ(¤!ðf´)e=Ëà]%£«†é:åZëA­ƒBq 'o¥£Í¾¦w°¯;QC÷:ÝŽv] +àª=×~hé +êöO|”Ÿä=þõ>ëöòn£¹ Þ(쪆=¡ZXÎxýÌ:s<ØÑ¿ëfž¡Mí}Q~è * }Ǫ;´õÚJÇÚû€öýTFsÃ\a¾¹£®éÜÐcIÁÈÓÍ¢ ê‘}9;·ùŸ"‹‰hd-ðP”ÐS?Ö—òd–'¶è£Sž—0‹<š4éiUëSÊÓ¢v2o¯uÞZÀ"bóÓØÒJQÔʫ¢7¦tˆ«úH^ZÀ´G×]ï·ò§úïÒäÅRñPЦaLF?ƒŽãÜÊÛáï‘÷4Ö¯ìÞ\i•Nó³̯´Áõ>©nÕH¯½Ü=Š=~®˜Qái&£ªˆõ¼M<Œl¥ûÖ +¼óFÛÈvZ¦ÛLÖ-ñe2³ww;\§{ìÀ«'3àmÙm8VŒb|®ðXr%|ôèUÀQnk)¥Ìëì\íÁ±˜iÒôÄëën绋X$¨`roϯÛqUiǹö2ü¹¶Zͼ¦7V·Y›×’hæ0­5÷¨òôè±’iôð=Å#1=R­RZ½ê¿ÓSÝ™ªÉ©ñÄÃ’VN ElœQh$ÜÑqFÁ]yähö.ž©Ç†7¹pGÿû &—4ö†¾dÖ8®)ÄQh,ƒ¥Ã ßv<¡°UvOï†Cû‘‹öÍ@ãÙm `6 +®)ìð4 Ξv´Ç@x2îéõD³P}<úwàs˜íÐ~ÓxÞ6jG¬2Ùˆ^ÌFÀÝh€?Ôó–þÍùLâÏò#z=Òƒ§Î¹Ñ :å…LaŒ²Ø}:ÎÙÐn ãg£YÇv$£a0¼×iÉ`è’Ð1Èþ¶}kAÿê}ã)ÇÓ2žÉ‘Ò3ˆõ§£g­tÌFy¤L¯Û $xx©ãAùŸÛ:òF|TFû»úT6iß ×«;CŠ·Â¸1–Ñ—Æø0ŠÎÚQ.R~fµjÜèvRÈøEåF1ÄFJcs[J¼ÐÚKçvÚ¡´Ž0„Ñ—Á8•ÅZ>f@ûÌÖ'º>f2Zzx­ÃÔõ^׉¬vÜÈh¤’½FÍðèTã]G*¨œÆ1üÛ¨Ð%æùØŽgmÒé‘®Ÿ6rÎm¸2ŽÙbk•Æd=¦ÕF3ûáÁ|l«†µù€±ýÕŠYGþzíÈÛî§ø–wìŽÄô)˃á˜Vnè-”«û® ˆk…lžSÓê·;FîöYc[6Ú>ïŒoçkÛgºÂÚ–ßÒ®í©>[ÒcVÛ\§}îv»¶wv¬çòÞ¬·-ûÐ}·>'jŸõ±ü\Ï«[³7ËÝ­™ÉLö¶-¦Wzj'îó<:r‹ýñ­cycQ,=¯,`Ù­ú6ܼs„R~03¬dñ^e&»®ñd&”¾éž¶ôùì[fÃÞúÏe ÞV^Zn—9´ç“w¥g.UÆ8LóÉÜ*ä—µñ„r@¯»•ß"õ6í£Ðú [« +”SÚa^Äx­ ½†GÇT˜¿òÖ¸þï«NÿêšõS=HéPº5óú÷Õƒ”ÛÖƒÔÿp=HùIõ Ž™|a;œÚjÞ–?­‚z» +‹òVWRPWRþÿºR»ºR[…áÿ›u%¥C„ý¿«+)·™­ý7Ô•”ÛÖ•Ú(úÏÔ•”©ügêJ +úGëJm«NÿʺR›½u¬+Ý)úÞ¹º¤ÏÏõLâ¿­º¤ ŽÕ¥ÛW7þ3Õ%åG¸«¶ãàw•Ia:öÃlæ?_eRþ‹«LÊ-U¦¶¹î²Ê¤üÝ*“ú«2)ÿ@•Iý·U™ƃ\€:Œa«s; ÞÿçjGÊmeþU;R~P;RÿÏjGÊkGm5 íHùjG?÷ß[;òzÖ;G”V|”ŸQñi_¥ùWV|”ªâóÃ9ÛÏ«ø(í*>?VwøWThj~ß…Ú* +‡Þ% 4˜mТ[Õèf·Öýqjçêâburñ4÷̸õ'llKP‡L›UYZ­–•Wº«jŠ‹Ô’*w¹šVU<ó Ì;ÛH7]ßH×~Ei=·¸ª@ÕQkݧtûÑÊ÷íýä-ê-#—U+jMUAQqyAÕª»äV(Š’]\U^VÍ6Í•U«¥ÅUÅ0Ö”ª‚ + =h² p¬jJq¼ZãV *f©•ÅUÕÐÁ=¹8V,(P iZÖ”{ùTXè.¯„æ´AM)@.WT÷"K"ãX‘ZP]í.,+€ñ”"wáôò⊚‚ŠOIÙ4Rg +‘uPǸKjfû#ã&UÅ•Uî¢é…Å LQV6yzM1ÅAéÐ!Ä\8mzÅdfYM©{z S^戎P¥³ÀN¯†ö”œxµ¼˜R­0©.o7F<3Ñ]¥Vƒ u ê!ÿ–¡)r¶’2ºFÑYÇšY +ŠõƒT %Ó«*`ÀbֱȭV»ãÕêé“§ÖÐ'”¾÷4P6JP¡»¢¨ŒÒQÝGQr\Ád÷ŒbF®E V%¨p×€ªõ§T*•m ¿S«K ¦MS&{¸h€•t Ó]zQ¥–»«ŠoK¶Z3«²¸¤JÐ‘êø¶¼`X t/*+)£ŠV0­T.hAQ£\g5Ђ*Àkú´‚*…TT\]6¥‚¡1E·UèD5´ €TÓ^|ªo‰‚T`ư‚i·àéãÅ£  W1m–ZÖNÍJNU1ý߈³¶ô¢š2’ÊÅkÅ sÅU¬ÓLwUQµÙj‡‘tlï %’šm$cH&Ëc/“‹Á’(Ôé Ê“î²VÄŠª‹Q *+Á¼ +&O+¦/tÚ2½PÚ„RZP£–TÄâŠ<¡ZצÝEêôŠ"Âm¨* 9“jµ{µj&6*¤uõ`+Þ†•…LÂÀ+Ü +UÕL©:  P,žVB‘š¡52G3jpθ´Ñjæ5{ô¨ÜÌAƒÔÈ´1p¯ŽËÌ:jlŽ +-F§ÌÉSG VÓFæ©Ã3GŠW3ÆgÎ3F5ZÍ‘•™Ï2GÌ;(sä5ú•£feŽÈÌ 9£XW¨ÌŒ1؈ŒÑ‡ÂmZzfVfN^¼283g$ÀäF«ijvÚèœÌc³ÒF«ÙcGg“0Ø‘™#†Q2Fdhà¨ì¼Ñ™C†æÄC§x¯äŒN”1"môðx€’G«¬I` 0ÔŒ\ÚyÌд¬,5=3gLÎ茴´-åΑ£Fd(ƒG9(-'sÔH5=HIKÏÊÐqRf¥eŽˆW¥HBÉñB›éä´±C¡†dŒÌ–¯ŽÉΘI/€™£3æ°–À{àDCw਑c2î  wˆxeÜÐ 6ÿ d˜1òG¹NΨÑ9­¨ŒË“¯¦ÎC%2xô(@—ÊsÔ`¦cŸTx#=øRÑg?ÔhE{{”‘–ÇP4àÒ¡-hWÆC…Å•5T·=Æ­»FæFußÏ´Vw ÂC*ÀpõgìÂX‹:ºwk Ø4Çë®—¹ÐnˆDºë-šQ °šºw•â¦ÎdfY5³tån=æ©ÕÓ`0èE­ˆµ_Y0 ºU·¢ÙÁ o0¬¬*ƒ.3«ÊjÀ™¨ÓáiUÙlO®ò„)FÚF¥Í9èøWWWB”*›QšF–-5 +Ë|ÉR#©…µEd pjI Yl'hdÑB»°H# ídFækdžF\71w®ð ÌK."srœÂœ2[#³4ò…Ì4‘ +™®‘šë¤ú:©ºN¼N*5âÖH…F¦E42Õž.LCÊ4R:—L›k¤H#…™¬‘‚>$ÿ:™d"5r¯F&h$o¼"ä]'ã2ÎïÿUK&[i„A¶ù¬(ùùÓ¿Mƒ¢‚SÚÄÀG Κ¼@¿F–YºõYrNÞŒE%ÿd›“Úݺ·NÕ½§Jò­ÁWåÕo~íòRà9pò<ÅSÌc?/ʃá^¹»ur§Ü:¾(Ï ”~ÏI?O¯l¥ç¸±\+Wï\¾s¡œgêr>¤û‹³§Ê‰r܉ä8¦Ó¥Ñ>²Ò>ýrd9T”ýV,ûCZM'­˜æž‘¦cϰ[aÇÒØ6ÒP¶ [›F¶,›†zV6õ,ë jÕDj)ÕµHª k«+‰¬ž±’°œYI Ÿ•%e1dÁû\ˆ˜O™Rñ*)eˬOpV™2Ý¥äAI™J™ôIM*E?T,QPb%¯D^)ÞëD÷0å“bsE±JΫsEŒòÑ‘Uƽl\‹ù2êÉQÿ|%ãq¦NàQ‚ŸAúã-¨ý5ò¯øk•ÿYyLendstream +endobj +303 0 obj +<< +/Ascent 759.7656 /CapHeight 759.7656 /Descent -240.2344 /Flags 4 /FontBBox [ -1020.508 -462.8906 1793.457 1232.422 ] /FontFile2 302 0 R + /FontName /AAAAAA+DejaVuSans /ItalicAngle 0 /MissingWidth 600.0977 /StemV 87 /Type /FontDescriptor +>> +endobj +304 0 obj +<< +/BaseFont /AAAAAA+DejaVuSans /FirstChar 0 /FontDescriptor 303 0 R /LastChar 127 /Name /F2+0 /Subtype /TrueType + /ToUnicode 301 0 R /Type /Font /Widths [ 600.0977 1000 1000 518.0664 518.0664 500 317.8711 589.8438 1000 500 + 837.8906 837.8906 317.8711 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 317.8711 400.8789 459.9609 837.8906 636.2305 950.1953 779.7852 274.9023 + 390.1367 390.1367 500 837.8906 317.8711 360.8398 317.8711 336.9141 636.2305 636.2305 + 636.2305 636.2305 636.2305 636.2305 636.2305 636.2305 636.2305 636.2305 336.9141 336.9141 + 837.8906 837.8906 837.8906 530.7617 1000 684.082 686.0352 698.2422 770.0195 631.8359 + 575.1953 774.9023 751.9531 294.9219 294.9219 655.7617 557.1289 862.793 748.0469 787.1094 + 603.0273 787.1094 694.8242 634.7656 610.8398 731.9336 684.082 988.7695 685.0586 610.8398 + 685.0586 390.1367 336.9141 390.1367 837.8906 500 500 612.793 634.7656 549.8047 + 634.7656 615.2344 352.0508 634.7656 633.7891 277.832 277.832 579.1016 277.832 974.1211 + 633.7891 611.8164 634.7656 634.7656 411.1328 520.9961 392.0898 633.7891 591.7969 817.8711 + 591.7969 591.7969 524.9023 636.2305 336.9141 636.2305 837.8906 600.0977 ] +>> +endobj +305 0 obj +<< +/Filter [ /FlateDecode ] /Length 677 +>> +stream +xœ}ÕËjÛP…ṟBÖRœã}KÁri ƒ^hJçŽ}’ºÄ²íAÞ¾{y…ôPJöþ…tÐ7ÛÓ«ÛëÛ~sè¦_ÇÝꮺ‡M¿ë~wWµ»¯›~RfÝz³:¼ÜþWÛå0™æá»çý¡noû‡Ýd>ï¦ßòáþ0>wo.N×»ëúkùãx·ì÷ï/wOë·“é—q]ÇMÿøŸWîŽÃðT·µ?tg“Å¢[ׇüاåðy¹­Ýôßçþ¼õýy¨Ýìt_ˆ^íÖu?,Wu\öu2?;[tó¸YLj¿þëY™óÌýÃêçr|y÷,¯EvizÖ´4­M[ÓÞt4}Þô‡¦/š¾lúªéë¦?6}ó§Kã/¿4þÒøKã/¿4þÒøKã/¿4þÒøKã/¿4þÒøgôÏNMŽlúsdÓŸ#›þÙôçȦ?G6ý9²éÏ‘MŽlúsdÓŸ#›þÙôçȦ?G6ý9&s¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àú_6ÄË&À®ÀÚ{]D«ã8æŽ:íÆÓâÁÊÙôõu}»§ðû "‘£©endstream +endobj +306 0 obj +<< +/Filter [ /FlateDecode ] /Length 18175 /Length1 35128 +>> +stream +xœí½ \U8~Μ™¹3sßÀ½pyàQDÑð‘E$Äg>@¸ +\â¡™š¶½feššå#33×HÝ2S³¢¶‡nµi­™™›nm‘µýìE0þ¾ç̽<ÌÚ¶ÚÇÿÿù1ÎÌ™™s¾çûþ~ÏwD!dFËAã'¤¤M›¼æ^¸óì“J«KjåÉJ(BxìÎÒù êåO÷“âò`/*¯S}%7ï „øÐÇœª…å«~¿ín„x>4©Â[RÖknÞP„2¯†ç+à†åqÛpÝ×=*ª®þî@j\øËª|¥%—Ý7¡‘Eðüùê’«kŇže…ÁµZSRí­‹»ÿc¸¾ ¡aéµ¾ú† 'Ð4„®«¥Ïkë¼µ‹K6×w!dxa~· €Oa-Ì­ŸÉ;¨œ  +Œ!"ÏqüG¨æÂŸQû¥Gq"TSAyvR‘zá‚¢…àu†jüa1Â.\@úF˜MHÄ +k-C¿ö#¤À¾"2 ÉHAF˜ÁŒ,ÈŠlÈŽ‚P0 +AäD¡( ¹P8Š@‘( +E£À6Å¡xÔ¹QO”€z¡DÔõAI¨/JF)(õCi¨?€ÒÑ@4]†2Ð`4 EÃÐåh8ò L4DY(å Qh4ÊEcÐX”‡Æ¡|4 +P!š€ŠÐD4 MFSÐTàü•h:šf¢YŒ€rÜ—£ýèïІ6£V4q¨îÒó\„Zàùlèy#.‚s5¿qð|)@p¸?𮂖›ß‚÷£}è,Œ¾/F ÓhoÆ) +ëkáü…Áe )|5?ŒßÅ_Çï‚|9j‚c÷&¿ž_Ä¿Î/BS(f8î´Áñh·gaÎ⎠çþÃñ:Bs1Ç(ÒW8Ûhš œ99á˜bˆå˹oQ ¾žÛÊ}‹ã1[ŽnÎ$Gøbþü­ð¸ƒ9ÒŸÄpœN{Çð:Àâ´XŽB?º-‚yZ¸¸½@ãAtè‚Ù¹éÜ"n:‰wâ}€1B7â|±a6Ö‰ëø)èå z“;ü(`ü¸Ý.öC_ó"ú‚äáb~år Ï)ÄƈAh5c¸(AdZzŠÐ+ Ïéô’Ä(´šO î·$À7¼á2Èl´žm+ñ^´íEõ@žOD'FIª­‰sç–5y®˜¢¾<5¶oÒE—ªÍ 6¡‚&óBuï… Søaj“ÙDÜRïŽ?ýcO÷M[0EÝ‹{egùÁfgÁÍ S I¯à6ÜÏÎbÏè¬M‚þå7©¥êm¶ÛâßfóîK5][Í— [ÀŽ (f?âqO`ƒˆ{>‰%áVŽG)ÍG[ú!ÛÑ–£-©ÁöX»;Ö[Σ¶zÑvV[m°|ûe˜ÈtÇÀáfÐ{í3€*LwÎí@U¢püO ëéçð=‘¡†`«ñžh‡LÐ +»9&:*2"<,Ôd·Y-f“"Kº$ÃÅÕvôp(Å}è™´£Ca6ÿ1“Øàþ$ÖËöø`¶§Ç²ÃM²¢K%'K°C{2GjMÅ'‹µo‹OkÇ£s´³¸°hßæ»jKñuÚÒcÚ¹w´ëðRº¿ƒƒŽáëÚ·kç@IÑFí-ACÀgößX煮îˆ0{hHcT>L”“â9aµ¸*)tuتˆ•A!I+!fôÂQ)H°ˆ½zØe‚ }"‚ JCÕ4ÛÑæ¶£¡CÛB‡mk¶eÀ? °¹nŸo±}uΞ[Ph†þ((##Ç“dœ`Æ¡ö„dœ>`àpÜßWz74;BD±[àì ¶_z’ˆÍxÉ'¾úOÆ~þÊWâÍe/{៪9ÿÍæQó¿˜<¹@{ ÷’±8d8‰½÷<ºûù£¥¸­wŠ {=óÔÞ-døìô!#µ'´OðˆY#AÓF\øÐðø##D˜ˆ"ýQ©'=ÂîrÃÂ"ÜèSòqk{=.>`Z´&2|ýW¿>2‘²,YûI1rÉê`{¯¹È>÷5¥›rÂv&íüÐ3ìŽí\*X—Á&|F÷©q8Nû§  +Òœ@v|\O`¸¾þ±]®C»ôûÊ7ztÞ¸QûÞ~{ß¾wÞiwŸ$¿ÿýò}ÇŽí£ûèüüÑ£Çås‹êê-ª¯[´í/œ8q`ÿñ¶·DóñÞ}÷ÀãÛ×Õ/YR_·X×ëE>·ý=Q*è™jÂf£U–«$KI}“Sø>©VŒQ/œ˜`…xíNM0ÏûÊ!ˆ¾;ªï&K솨Ã×[ÖðrBoÔGÎS{D…ôîaŽ +å± ‡ö6ÛMýlm-ÍG¶Ø^:Ç”AW{Ó˜—Î|ý7`‘ Çt%4Cç–å3hvœ€wmMÛ%%`4Ãsƒˆ¬ÄÊ˲U±åcªAÄà]r”eŒLu£%Åèî9\nôÆ*ccSƦNÃÓ¸‰†iÊ4ãÄ^Ó“§§NI›•V†*¹9†bC¥Ri,ë¹,-I6* .cxBoc†1=25*5:=ÆÃ1æ&äöšŒ'sÓW&Ì!UFÇ ¼Ÿû;zô÷ÚÎcÄ×u%€A±>®¡ýî”vRç)¬SØ\ÙŸBÛ8:®4)z÷7iˆ… –еÀ…\ Ó±à§8Ð0a’ÒÌÄyþp*$ŸñœåýöœŽ}WMýÌì  +)~?ï –wXÉÇzëš0ä4…ŠÎ 0˜‰)èêDS±­'˜ÝF ÛP¬ŠììHvV.^w÷÷/p±¹9˧½}Íï´k° '^û*Ž€”"ŸqmVå²qyxtŸ¾-o]óÖŒÆ[/|ÈŸ{¡rÛ$‡»ˆ;8ÜÓÃMíöŒí\3CŠõÜÛT¨]2¬Ç@ šÆPÕ9ÍÉõÂO¹×‹çÏÚV°ãmĺ+Ïb—ö’ö­vR{7âì9Ïs§¯÷ÿpG´–¾}žÝ߯ŸvþøÚ)|+®Äuø•ê&Øß9ੈ²=¡‚‹#ï"„s ÀNi8ÁÈ`{OÏÁÁûuËÆvÆ3ðxK€?AÌ—Å£4ÊÓ+Ì”°‰;¢QS_°òè5©aÙDz„[{‡;zÊá $ÜÚ3¶G*ø8Ê¢ @Šx¾å Í|2ºsÌMÓ‡±º­êæ¡ö€„"8ÐØÉ½RÕÐP5¯®N[|Ëm8ÄlÅá·ß²æ~p‰ïûÞ¹ÿËÒéÓfÏž6½”[?¿¦¦±±Æ×¸4qûÒ/½xpéöÄÞîzÿÃß¿ëž8µ¸xêÔYÅTös6 È>L—½ÁµCuî*~èâÛzçw¤ÛŠâÂÍLöÍmmgtBš=H×ÃA ÅA2w\OÀ=2}[áŽCö¬uÓÎjá lÀ=°G»MÛWy/õ–ƒR”—Çâ> ï´4l|÷8N›¯­ÑîÔ¦Æp箿þw7Üð»ë¯gö|ù|1[ãEz,Ü6´›ß& +–ŸÁ°Ä‹¥ÕóèvWj÷Ñ/Ö–h[! 18×A¬¥pèúÓí – ÖÒ€»[!<[‚+ ‹ °…(¥G^?Èo!úR¸þÌ[t¸4/&œàÿºã± 4MÈÄ=ñš±¨ÙIˆÌ9p:N´ %Í­šÀ‘#ÚíÉVüz ~]Çñ8NN~ZÍèîIñ^0$–Þ°Å6K +l;.pR«FN|úÞg?ÄG ?ø?à=f @:ð NÇ!ƒ¼HŽ´jýZ´~­àÅ(.ËÁæbÀ¢P,íIˆ +8^tYÑѶ»­«L›‚—G߮ЂŒÃ]HæœÖÄEã8¶Ž?J£?€h¨Ûø ‚ÍÃbÙ²°[ÌJ£v`Å@ ÀjîEí¾s=éÅ“®Ø_¾ðåç1ÊŸéÉê­ËÛ¯péìÁ¦'çŒ9îÝëÏξjÆåcû>G«ºY[oð Á~Ç¡*Ï`“• HÍ×ßæ’#È^kÿTy¯Ë‘º×úÔ {O\‘ß@^¸fêŽzE'†÷Ê êÕ;qÀ°|š.@\M°iC_ +b‘Çn5Ÿ³ÏN³û—;ô˜Š»˜/¦ÖÑÓ¿HHﯯvzö 4³ì:ÔÉS# +ùø¸ zÞ=°ð‚®“ÁœâÉðÊiSçΛ6¥¯‹¾qêŽwþòØÔ£ß\|çà!³´·6vïÃó¼e˜¬¼¶mê¼%Úñ5Ok{—-»é–k¯Åã÷|€k§íÓÞIà"ݹ⚅˗kÓFÿîå—[ F]ß>6ø•ÊϽæ†aCfk¯üa•ö}Ùì93 6—̹~Éœ{Rã%‹oylÓì³×jÿÐŽR¾*‰ àƒ ÐÒ<% +†(Q”(I@h›‹â †]Š"ó¯ ºïr‡¦GË”e’R®,EKñRi©Ü Ü¦lPž…íuØN)¶ )RŽ1¹¥Þ²jÊås„QÒhy +™ÊO&‹¤’/æˆÅ¦´_Ã7 +‹¤ùVþ&á&éVy-¿ZX%­“Ÿ”ž–_C/â¹× ÏKGäãèmü6wÜpLzWNa‰‰Ål#|NûÖYÚ.¿Æ%jKڷᵇ±MûB8ÖÚ‡ss…Ô® Ú9É­À; V4**ð¸ƒÐÓÖƒâÓáËå§ÃDXLE›¬rXo Îh“Óa38]$üo-­ÙEÝœ`ËHõÈ(ΗWÇãØŸVÙƒ =ëJ„eˆž[]9sæ•ÇþÚÐØÐøWnôâ[´÷´·Û¯ãFàA8´œ¬,Èw…ÖÜ^?»´¤D[ȹz<¿ü/G…cû_¯^Ë|J9Äéà \hˆ'Üd“‘“˜! ÜIl;åõdMxp_û„Û`5HòµfÛ‹:Ö©{R"fEpx†›­Ÿõä@_9SK f LŸû·%ÚíZÞƒ—ümî¼7êÿÔÒò§ú7æº oÂ^\Ž7]6H{-7Kûöã´o³r™¾ŠŒ¯¡(Ùã@‡äåøS✠+úÚ’‘S&úʼO€{©» +\`»ßX1°(T_™ŠÀ1nØm-ß}ûYûWx5.ÂãT–—W^­5Á6—ßÕvÕ'§ÞÿÇ—4xµoyTûÆÛPBùxð§#Jõ„ð‡ÄƒÜ!´\:¤`A‰"bb¢lnÈ/õI›¹À\kD-ºíç­íuÜœöµÜ–ïO@wRûöít§ÏñÌ!£>{Çf(ú,ýaàÆZc7ðÀµ¥Ðí7ûeÛÊüüXÛî ˆ7üéÈGFG¸ú™ú£”à¾bŸh˜ R|'›Ùr6¿¨©´wÍŠöºÁ±ùeÍw—5M¤…Ö»´Wfß· +Û.ùfñß¾ûâäÐ9‰ŸrS|99Lè•øA*ôQ9Ú…Ï?Ó4« ÇÑ ŒÂ׌ߗ{‚¹CÒA-WDà„£v³Ä`èù¡ÀƒÖ’¬‚U´¬R¹x¿Á¬\Ä}|w­±¶¿LYÔþG.lx!0 ùç䆱5qÒSè9ŽÎëç±MÖ¢±M¶¢+a™‡ç²©ú‚?iÁ”{tåÏXø ¸©IøïÚ½Ú¶óÚ6m¥p¬í4‰iíÃgµ îï÷wµ-öDÒ7p!qS⟓¸~¼¡ŸîGXaB§:£ËZ˜g´hmv€""£K¼ ÂcÄIxªX`œƒ+Äbã>ü¤hqqá†Ë¹þ†\ Þc˜ÈM3”s•#=fït(Ú;¹\­¶Ÿf#ƨÍÿ>|’Xî¯'¥y‚ÅAh§i}К0¹¯uéëèsQ=ÉcD®Ð`›+Å5Ü%€o÷;Zu$ÚIyÓóÏ7=ñüóOà +¼F«€ÇZm^Ë×ÚZ>ÕÚ0ÿi æq¨V¦­ÒVkex=ž‹çáõXÃb¸‚‚Q?Ó´Ó`܉–ËÁf–l}y£†¢;ž@´aE¦]È)é4ó`«±‘g?·Gá~° øP£owŽÕ^sM-˜À'Ÿ¶··òµYÕeeU:?´cŒVf{ÂÍÎȶӰ­±DZlvõU÷åûD‚ËUuï Æœ$™P”Ëd‹J@)Q“LÌå¦bóÓð‚fƒiªqªyjÈ”°¹ÆJóÕ& ä$Äw0-„¾?~~®ˆÖVhåx-ž¬YõΫ¸¯v·öaÓó÷c#ð\EY̽»M»wºÖ$òÀÜ/.è¼õÇlÄbv$ºÙ“ÎEº¢œ¡Î°¨ÐP§ÛåT‚ÑNYÜiZª8ƒÃˆ-Ò%"Þ ¾=Ô&œF¥;+ 24Ãož”Ïì T—…¿þ.!,’ÚfLDX„+<<""¿K ƒ¶`‡Ú,ƒ…C–52Z$9• ÿ‚:¼IÚyêòSŸô×oÖó'}Ygõ[Ä™…·Þ¶¢íó°¾zF{Ü×uOoØð4YÚvö‚ö*ˆ‡é4±õ’ˆ®÷$Z3æ¢0'ÐâD$â(èà¦X «t,°åùžÒþe8àKÇìIBœS¹&Ž÷CBq ãC…I•F¡Qx4Íg cÄÉx +Y!ÙAÇ*øz|¾_ßþŽ–¡rŸßÚG³Ã’VÒx‰«=cÄ(ˆ— ñRhT{y ànÈN(+„p\áLÐè0¹H*DF%“ÖyúWF2SùÉET†WŽm2Òƒ©3±9¯i—Ž®—¢ü0Q@{ˆ`lŠ[Qa ¡v°0\è¯¤ÂæaÛLn¦P¢ìåžPš` 8¼, FÑääÂH(ïRˆn 7%p½HßK€%£)Å2òƒ4>MHS ©Rš<ènÉ%9üh![c¤ë¿iÜ42‘Ÿ(Š…†Biš<Ñ8ÃäC>ìãjH_aðJò<¥ÆXiò™ÒÕò|ãÕ¦[ 7J·šžâö‘'ù½ÂÒAÓØ€d˜pà0 _ÁázØ9\£mÔ²`ý}^ËY½À£;ÄIß÷wë2KâX.ù…'›¢Šr ‹r#2ÊQ’ŒèI‘!ØKQƒzHè—YQ¹aŽxÅðYË`ÄÁ07+•”½ÓæšßkîÈ…ü²ê²4ï&²NQM‘ˆ•ˆ2’cˆMN!=eUJú˳H¼‚,“Ÿ%M²]æDÈBe%‚ áC¤¥‚u3ßCtK}@®é|º4X)0{Ñ\Ñg~šûÿiŸlÓ™¦ÿ#mœ¨ù´‡´­Z5´,øÈLrð½\k»ˆ5ãZ¹Z >Myv¡|Í)Vyzu·KÂIH¢ú,1»9?Xp¢Á9 ÅfÊ+c‹Á~ž¥ºA½R9WÀƒÕ*°@…(Nܸ7IǃIª2^ñ)T¹ˆ?9¶‡à‡!µ+Â[ÛÿÈ2ã.¤mcûí\£.{úžã*–ÓAF@¨½J {Î-÷$‡Èi!ÜŠ +¸”Ñ*‚K v¬»¯à‰ˆQrpÉM¥$CnÌ!£¥ñÆId–Ñg´ês{l:Ž­ç?j;E.ký”ĶAJù÷¶AÚÉ¿“Wtœè»æ(1„~-ä±ñÍâ®=kTbG6ú9Îá4öNÓ_À¤ïH›¸jí#ìj_)†œnÝxú¢wÓww¾{pÌYÚ¿<ÉYŠ+¿«Öç½½‡Ñ¿‚9·ÀÜIÈ3C9ÌŠlZL_ÈÂÎ}Û¾»´`ö/O ÅtÞ×É6á,ó×a…ð-7,€K>¬O –q<üÃägû‡V®Íù’ƒPÓþd@oq})H‹â‘Fë·–=”ý\ +]%=œŠ±é4½ÕN<ýfò縕b9ôË +¿„^ä1¬%Ù _ ÒU¼ˆ¿púó°y.³ºl*¦/qO8N¸=¼‡7k+Ärí|µžOO¹ð!ÿ"¿b’=àé-‹ÈcD}]Ü`yîŽy-rCü«ö5&JÂ̲Ù8,†˜C†ô¤¯ ЦÙõ„Vô·s_ŸËðç!y) ÃÔa±ÃÆ©ãbg¨3bk á¸V½6¶6áõŽØÔb¯þ>ö€z Ö‘32Ú3!º ¦4º8æÆèe1+£ïŠÙ½1fWtSŒæ÷$ðå/£öˆíßíu!·¹öª+¯ðÞF+£÷\·ó8¶â¸·nº³þ¥‰õ7àlÆßæÉwwuâÍí×m-ŸñÚæ÷FNŸœŒí‘QŸ3žì€¼`&èŒòÐË<â&ÓV´É±ÆújÄCao¸Èˆ O˜I6‡ÛX ‰[l²wtçR÷Œ.ަ%­.ï<{Y œ@µ› ú… ëë-Z4fOãëXѾ~½qÏm.ÿhÛÆÛݰáQîØìÚSZ;lO͘½Y Ñõ™Ê+äåBƒ<áè0>Ê[›*›ìü¦PS¸a„… é^o;Îö 6+b)­µuæq¬æÒ¥òƧ箿ú‘GVÝç)úýdíMm;¬S¦<ÆÓÞKK}üO마‰Áƒ°¶A1ºM§ïŽA›Î¯ÐMè ‹}“ð†´Æò*~ˆ„ðÈÌyÂG‡P~Q¡®æÌù3-¶3übë?&UŠMÿ4'éÊÀ­ô„1»_×¾ÆÊë »7SþÕ-\HösS¾kÙ\:çb[W(éÐoAáéWšh¤'9þ‚åÃÒQaƒ ¿¶!èUÓšÈ'9Ì(‹3[‡D2 ›»¼¤£Ü;G+•Qã(šŽXý%W'ãT$è((mϘ×ßqÕgK–B®ù†ö8‹ã°„‡iw-(®øë_~íµ#³´–Ô~8‡â ‰mŽ _0oÛ†öw¹Ñûæ=ú@ûq¾xˬâZ>f3@ßÅ6³ûÒ6sæŸÛŒãŸØÌýwlðÐMÆïÇZ÷ëë 7¡M¦WiÝo„5Œp ¹¨î÷ä àá®QtÞõûŠ®%?î¹K–,h\¼¸Ô$–ë§´÷µ§ñ(²è±M›£;FÚËZ l/ãËpl—é¸ìÐ& 3ï†x¢:ãÝ«–5ø}²; +b‡E½.‚íÌ™‹Cž»ƒþÜ ¸ ª\åK—ô` ý¸ÂÞæö=¢²¥Kr@Ñ|E<ê¯'1mÔqóç.a 2×îˆ÷­k¢Xæâ¦K4àÖ%}éʵà.Q™úêXn„Þ:ÀeaGHÖ}ó² }ÛûtåQà–¿ûºCŸH ðÎù‹Ónå'"‡Œ÷Z^•wÑŒ$[5Ž`jû8ŽyCúåX³ÿeúŪJZRf¤Ü¶Šò+{÷’ Þ½HŠÓñÄÃím|ñÞ/è¼s!oš ó& ÷<³‰³FÇD ¢A’^íÖëo,§ +9ì8¶ÁÎop¿ÚYƒ›Qh 1ÄëÅ>jn9Ó½ +÷Ͳ‚.õ 3ýrÙH«3eYVd£Ñd4ËV!>Ün·„Y“¤d9YI6&›’͉j†4D¢ 16 6•Ç(cŒcL£YMvŸ´OÞ§ì3î3í3»-¢Å`‘,²E1™‡'ÎJ”iý¡K‘Žw^üy^¤£_ð¡õoÏ*/[2Ô¾ÕZ}Ÿ-™wº¡rnnõðÏo+}r¾/RSû§÷I6ÊñÛ½'>Û œ‘šb–¢7?¼kG4åkÈs‹ð ä,Õžp‹ YÉ&;Þ-mBŠd”9ŒÀd) +éòêŠÆ6±ÜÅÂr—æÎÜ¥¹…þN{—å ކÓ_"V›Õ^ÀG1WLtBi0 q¬hJcçúã«´—Oß«9úÄ®]ƒÚóæÎt=qŸÀ_Îtp#ø‘/fï¬# ¡Á¦!o„®±áÝA"‡‚­fû(ðm¶pÝ>t·|&­Ó³E,£yt0è_­~‡êìíÈ_{nÄœÅ+þPýükx·£öJí³ä›DÄ÷ܱ–Kü~ãfæÝ0‚4Zp>"zëâz éV<Õ­¸áçÕÓß"³±J­2LâʹeÜ]ÜF®É¿‚í Ø>`Û°¹Q(` Á8’„ó=Q܇$òÑœA2øT)å`ˆÞ|±tµx3¾…Ü,Ü"®F«ñZ²–_%¬·‘'ñÓ¤Gg}1Âp(vâ9Ú(m_ÜÖJÄï7ê>a4ÄÐ( ß„wQ}q€^_@ë‹h}qÀÖO]²¾H V¿EiñoEUÒ¹t!UÅŠˆ³”*e©Â(À¦…7•7`¡ð¹êäB`Ý$ØD€ ’C§±'îÁõÅÞR¹‡ÑmJ±¤£t<€" I—™†[Fqz9q”4Ú4•Lâ'IWÊEÆ©¦Y–r®˜Ÿ-‹Å†b©L)66rµ|½P+Öj¥¥ÖBon•o2ÞnZa¹Wºßt—e+÷(ÙÊ?*<,=*o5n7HÄ'¥§M/áfò*ÿŠxŒ;NÞáßÎJ—ÿfüÄt%W¦ÿp¬ÇŽÆÓ÷À}`Ÿ®×د-ѵ¾çÚ¾ßH¸6ͯ¿ +Èψzr~¬¾ØµÙYk y|J1° ØØ½ØD!kæ£?¨8þ ¾ nD¶‘Y%)r2TIòäB2]žMªäzr|YF–Ë+äûÐ]ä.rŸ¸^Þ o$ËM„nÏÊt;D‘×å×å7Èä”|Jþ€|@>—?—¿A_oÄ r(&o’ˆÌEò‘R°c¡ó}¤ +š" PRÍYÜX~¬”¥øÌ7¡eÜ +þVq…´L¹­âÖñkÅuÒj屉{–V¢zõ:ÿºtHy½Áâÿ"ž’ÞP>BpŸó‹ŸK(ß¡¯ÅÁzÑ3‚e¼ 5üG­ +/:û1^çmÚmßi7røxmÎk?Õþž­=HíÎk» ›ôŒ´F!+X™ÉL,ôëõ‹Ýz¹YÝ…³ HÙ ½KÐn›Õb”%ê“ÀÓÛBë"/ý#…ÐŒÀöË??&1˜Õ–N—EË¢^®» œÖ½Ìmíéâ¶ŽúÝVe" aArb‡à†B±‡ÚØed 8Ðaɰf£l<†Œá³…lqçoânno1ßb¹[ Kµæµ–mÜv²Ôò¨õx?ÙË7ÉMʳƧÍO[^æ^3¿fyÅú6w–Ëèpm¬¯–†cø¸¾fʵÄQî½öÊ ¡`í÷í÷ŸYõéà±1ú,Ÿ|—Ñ­žpVšeEZ7DÍÝd­Ïò¸[}öpÚÖg³¦r“ •\¹a!7ßpƒa¥A5—l¸G`#ØŒ=I¢”b¤…Û1Ò,ãÒƒÒä€j, +K’ÉÅH ”qÉ›í»¸ü6'—ßþ_ÜÚ¾îjåÊت¶‹ßë¤h¦§ŸËê0Šn9Üæˆ2 +j,Ar³ñ4jÆ8šƒrG¹L²"ôps!HuÙ«à¶zâ|Í™hæ¤ÿ +ý*‰¾½Ìп eEÁQtü>´ñ0Øû×(úA%¿ëø»/WÝ9lØU/¿{<{ùÄi5¾©—WŸnzîÐêÆS «<÷øéÉwn}àNWÄë·®˜Lyy- +$FÑßuy’UI9Þ¦ÿ¦êaý÷£>úPŒúI¨½Åž´w„Ód•"Œ—•T‚"š]@oüÖfûCîHW¸ä°âø …Ç8 U6òŒà´¶¡‡õ4‘ØbýóPFuªà'sP2î ÛIÉ줻 ”sÇÄi>ß´‰wät¥½zòŠ­ëïŒpÝùÀÖ;'Ÿ~ü¹#«N5®>ô\ýæïNBXhÆ{@+"L(˜$ÂôHp4²Fë¿t´å½Û!Æxì  LBOºÑee:M°Ct…swÏ[a‘ ÖûgNÙ1{Þ.¸²èÕ¤GIáù‚ìÁ"!â°q:šcYíq3÷î¼íñý³¬C¿B1û”ãƒÖ73çoêÚvZÈs ¯„¸Ž¿€ Õø[ì7uߥYøÿAçO1•s¯@ºÇð}Ð ât»p7Ú(ñh„ØŠqõè² 5Á¾…OA‰ðü8QP5ðÃç79úÆçn4ö`ß û­°O‡ýAØ—ú¯—À>—ôGga¿ŽÂìüú;Åèf±RDÚ/œBåâ8/Ðwñn¸Þ…ös­t¿°\ì÷¡Ÿákx÷EÀ_8©ŸÅDxöZ)4¬¥p`Jo¡ab” ¼v¡E8‚¦SZ(Îp¾æ&À>S(GS„}h§ Õh +µkïB;¸ƒt¿°_(ÔÛÒ$´Þ–êãh?ò%Œè| EÀ³Â c(C£…Dm¿Â‚ Ý‚yz¦ôÃô=a+@ èm\Œ?à^åþJld(¹ æŸÈßHÂOâo࿦ ë„OÄ,ññcÃC¹áÃ^ûMš&‹ò…W +”{•—”/yÆ›ŒÛgL#L3MgÌCÍ7™›-½-­¬³­Z¿¶½aŸkÿSPXÐà ¢ ùAk‚v ® Þ"†…l9ï˜æxÐñ²“w–;opž½,ôÚÐÃa‘aS\¼k¦kkxÏ1⯑F¶FMª‹:íÔõ “|Ô]L }6䡿/|ÆÅ™²8Ö Ý[ƒÓüm ù·þ6û;› ˆ[þ6En‚¿- w­¿-"+÷˜¿-!;wÊß6Ò¿”ào›ƒèu³¿mA†¬ð·Á· yÏß¶#~È—ˆ¾Ïƒt §²Ùi#'~Íßæ„?÷· R±æoóHåúùÛ +ãÊümEs·ûÛŠãžñ·h0÷ÛìLÆúÛT1ÄíoÛsH³¿mGÒÑHäCµh!ªC•hªMQQ/TŠᜆRaë­ÙÐCE# Oª‡½yQ ªFIp7Õ@ÿdhe¢*ØTTØ«ž]yáì…1óáX=•Ÿ1ëÀŽY‹`¦ù0×\S½)%0æ_›1 ZsaÜ$Ô=J¡o ƒæe#JE*@©c-ô™ p+¡Ÿ +ã}0{ {¦ 4ÒW»°®rNEƒÚ«4QMƒ%¸:{¡:¢²¡¾¡Î[R¤æÖ”&«™UUj!íU¯zë½uó½eÉʆ¤C‹JæWÏõÕÌQG”TüÈÀ,ïÜ’IjiEIÍo½ZRçU+kÔÚÆÙU•¥j™¯º¤²0ëNâF`=ÜÖO(©‹@L„FøªÊ~lˆÚÙ­Ë`õ™ÄdQô1þ¦Dú£AðÀ[W_é«QÓ’ûê9·ïÅp)ؾ—¤œ× Á¯ž\Ê}5ÀÏbJÒ"ŒR`+óØ0’a¬Îu v/ƒWÇ$àza ªhh¨œ’R@ç7&×ûëJ½å¾º9Þä/<Îé‚A@¡JýCӡϨ’z™¢{FZ}©Zÿ6ÊJ!‚' ¡OY Ïj] Ì0(×êØjJêü‹8y1ÆØØÍŒ¶KÑ®«D ´ºrí‡nA øå›ò³\Íoïà.-ïNš+á‰ÂZ ìÕÂjÆëypÏøg¸PÊ +¼j­Ó¸*Nì™×O×6K_êI~¹ëÒÒgÓuL×÷$†—I¿†¯õ°>ƒ 6øu¬Ò¯% †Îiųaq±>•²~Tuè´·Ž»®Ë^fÿºîÅuÑ’8&9:¶Œë^¥0¦ÄOŸÂ¬ 4´šAi`Oü)‡V•ß’zuàØ9õiÿÐ_]ûéŒ<¡wj™Õ”Á ¥lt›2FAÓµÙð´=ÕçP~b†$¿5—f ŠÎ“L*˜Wjðs¦šÝëJQ€†ºnZ©cÛÈx˜ÔE:´]Íä©ËZéâAêatÒБÔAg +ó *ƒ¬Ûƒ»ÒÏÕîÒÿiªœÓ±­íÐè†W§ÖuR´€ñ£úgͰ†ræÕküz»ÌXÆŽtŽ$v¦œ˜ =J<½O@~å,éž- ¡R6wøÒé`fE~ìJ¢y†NtõEø¡'¨þ ~k¨ïÖ7`+ë꺎SÍ% s…ùæîº¦sC%%?!O‹‚ª_öÕìÜé?~Ž,X$¢‘µÄOQr7NýÔXÊ“…þØ¢ÏNy^Îp,ókRÓÓºŽ;:¦”§e]dÞUë´„EÄJæ3ªØ•ÒAQÔʫ¦ 7æt‹«úLZ´G×ÝÀó§þŸÒÀRñSЩa%LF?ƒîó\ÌKá–ä—wWù#Þ\éNó³%̯t ܩïÐÈ€½\=¼~?çeTfZÀ¨*cãã.ã:è¾x„ÏÑ6®‹–é6“wQ|™ÍìÝ××F¿ôd><­¼Ǽ°$mðÇÚ»6=z•0êíÑUî:Î;Ê%-¥‚yx•ëý8z™&ý˜ž|Ý¥|w‹5Lî]ùu)®*]8×U†¿ÔVëýù»ê§$`mK¢™CUGîQçÑb-Óèypœã—˜©V)^õßé©~œªÙ~iðÇÃòNFÙlžñ(®è<ãáªM†<²=Ë…{*äq…ðd\Ñ?y™Åä’ÉžÐçqÌ'C›B&2X:ŒB8RØSá…­²kz5úç,:6Masd´ €ÙxhSØãànœ³ýý舑pg"\Óö(D³P}>ú‡7‹˜íÐqÓ"¸ß9kw¬rÙŒÌÆÁU!ÀíJÿÈg.ƒGñObùmçûñÔ9WÈ SQÈæHÀ(]Ñ»á\ý&0~f2šuló 9ð\§%›a KBÇh$ûc¢SYúgF‹èLEþžILŽ”ž,6žÎ:–õÒ1ï—2mwBIöóRǃòRÇÌýy°©Œþ"ö‡L©l2~n@wF1o…qc"£/“ña<›aëG¹Hù™×¡q…]¤2’ñ‹ÊbžÅfÊd™pIJкJçRÚ¡tÌ0ŠÑ—Í8•ÇzO>fCÿÜŽ;º>æ2ZGúy­ÃÔõ^׉¼.ÜÉh¤’½fÍöëT&ã]w*¨œ&3ü;©Ð%é?Žì³Néçû¥À§ˆÍ\t ®Lf¶˜Íze2YOè°‘f¿ãü˜OìаN0ѯŸã;0ëÎ߀úýߡà +ÌÝ]‚YLŸòüNèà†ÞCù ¸ºïʆ¸VÊÖ9 ~»{äîš5vf£]óΤ.¾¶k& {áQ¬oõEý:ïê«%=fu®uºæn—ZaVÇz.Èz;³Ýwëk¢®YoËÏõ°¾#+ñ±<Ðב™,`O;cz­¿vâë¶Î£3—°ØŸÔ1W uÂÒóÊ–-ÐÙê/ÁÍPÊV†µ,Þë³,`ífBékô÷¥÷¯¹h5¨ÿüPê%e åR™CWþ×1y×ú×R•ŒÃ4ŸLöíCuY'O(ôº[õERïÔ> +m0º¸ª@y0§ æeŒ× +ÒkxtN…ù«@ë¿_uú­ ÜÿKõ ¥[=èâÌëßWR.YRÿÃõ ågÕƒºgò¥]pê¬uzþ¼ +ê¥*,Ê­®¤þ ®¤ü¿ºR—ºRg…áÿ›u%¥[„ýïÕ•”K¬ÖþêJÊ%ëJýgêJÊOÔ þ3u%ý«u¥Î·N¿e]©Ó޺ו~,úþxuI_Ÿë™ÄÿZuIAÝ«K—®nügªKÊOpWíÂÁÿí*“Âtì‡Ù̾ʤüW™”‹ªLkÝÿd•Iù§U&õ?VeRþ…*“úo«2)Œ“ꆭÎíLxþŸ«)—”ù«v¤ü v¤þ×jGÊÖŽ:k@ÿþÚ‘ò/ÔŽ~ +î¿·vð¬?Q~XñQ~Aŧk•æ·¬ø(¿ªâóÃ5Û/«ø(]*>?Uwø-*4 ?€ïA•…ÍC¯’ÊahÑïÚè—qÓ©½ê½^u¶·Ê· 1Yý_Á%«£ªÖVÔ«•Õµ¾ºo™Z^ç«V3ë¼óýæ`_Ý5ê_ÝuFQ:gŸä­+QuÔ:>ÝSúþäòÃü~ö÷êE3WÖ+%jC]I™·º¤nžê+¿Š¢xëª+ëÙ7t•õj…·Î sÍ©+©Ò“€v †Çêæx“ÔŸZR³P­õÖÕÃßìàX%° D-¤èÙPá ð©´ÔW] Ýi‡† +€\öÖÔ÷âKâX™ZR_ï+­,ù”2_icµ·¦¡¤âS^YBêE!²ê_yÃ`\"äÎ[[ç+k,õ20e•@XåìÆ/ÅAé6 Ä\ZÕXF1YPÙPákldª+ýÑêtVØÆzèOÉIR«½”j…)H}ER—9’èœ)¾:µÞ r€Þ•€ªŸü‹¦¦ÈØZÊèEg›hA(ÖP1”7ÖÕÀ„^6°Ì§Öû’ÔúÆÙs½¥ ô¥¯ÜWÊF *õÕ”UR:ê+J€+™í›ïeèZÄèP‚_ˆ¡^¿K¥RÛ©ú3µ¾¢¤ªJ™íõs Ð+)éF§¯ô¢N­öÕy/I¶Ú°°Ö[^%ëHuZ]²¬†—U–WRE+©jÕƒ-)+c”묣ZRx5V•Ô)t¢2o}圆ÆÝVaÕÐ’RROGð©¿x& +R ÃJª. À?&€G'4@¯¦j¡ZÙEÍJN—þެ/mÔSFR¹Ìà :ç­cƒøêÊêÕ¸;Œ£s(qÔlãË@2y~{™íK¢PA”'ó}•ˆy¯n‹QKjkÁ¼JfWyév€LJ§P*JÔŠ’z€è­éƪuÚ]¦6Ö”ùîDUaÈéþ”Të}UÔª™Ø¨JÔ*ê=ÀVkKJç•ÌÂÀk| +UÕM©ºM PôV•S¤Fg«9ãó‹Ô ãsŠ&gf«¹Ô‚Âñ“r³²³Ô¸Ì p—¤NÎ-=~b‘ += +3󋦪ãsÔÌü©êØÜü¬$5{JAaö„ ÊøB5w\A^n6ÜËÍ™71+7”:Æå/RórÇåТñl¨Tnö +l\váÈÑp™9"7/·hj’’“[”0¹B5S-È,,Ê91/³P-˜XX0~B6ÀȰù¹ù9…0Kö¸l _0µ0wÔè¢$T7“”¢Â̬ìq™…c“T6H.TY—dÀ`¨Ù“èà £3óòÔ¹EŠ +³3ÇѾ”;£òÇËVrÆOÌÏÊ,ÊŸ¯ŽÈR2Gäeë¸)#ó2sÇ%©Y™ã2GQr“Ðn:9ìPè€QÙùÙ…™yIꄂ쑹´|Ì-ÌYÄzïy Ý‘ãó'd_1n@¿ÀIÊäÑÙl + þd˜1òó\ +§h|aQ*“s'd'©™…¹¨Dr +ǺTžãs˜L~Ráåûñ¥2¢÷~¨ЋŽö˜•™'P4à†Ò­/hWöÕ¥ÞÚªÛ~ãÖ]#s£ºïLbZ«;PáQ5`¸ú=Ö„°–Å¢ŽîÝ:6 ÇIºëeî´"‘îzËæ{ÁÖSWâ«S|Ô™,¨¬g–!°Ú§Ç<µ¾¤ +&ƒQÔŠX/ð•%U0¬¾Ín¥‚am]% YPWÙÎD-i„»u•×øÃp?L1 +ÔN +è,ÎAÇ¿Î[_ Qªr¾·ja2ô­£±ŒaRYSî««ö“ÎØWÚ08*4¨sð2_ƒâ«›“¬* +˸~uêôs?â·Éƒ=RI¤tæAê/̃”æA~'_Ê ÕbÆ%Ô΄Eù5¹’È•”ÿ\IÑåðoË•Ý`U®¤ü†¹’Ò™+©¿0WRºå¿ WR~,WR~®¤tÉ•ºšo·t â98‰ß*]Rüé’ú«Ò%¥ºlÝø[§LJOýÕ)“ò›¦LŠ?eRyʤ\œ2©¿$eR.™2©ÿJʤeN7f!p\÷ŒXA=á}Bàè{ôÁ2)±£™Ø‘ŽwÝ—,dc‘Ñ%°#ÏzFÇî`vDž Kȅˉ¦‘¶ï“„6|ŸDZ5òÝ·£„ï–oG‘oZÉ×ùJ#ç5òž!_jäùB#ŸG“sù¬E>ÓH‹BZ<ü§Ÿ(§iä…ü½•||·SøX#µ’¿µ’³pqV#g4ò¡Fþª‘Óù@#§4ò~+9ù^˜p²Œ¼FNlŒN”‘w»…w[Éq7ùË›ná/­ä·C„wœäíc6áírÌF޾eŽªä-#ù3ôøs+yà¿é&oÜkÞˆ'¯ÿ)Dx½'ùÓ‘ áO!äH9 G‘×BÈ«¯<#¼ª‘W^ž!¼ò yeÿ²çÂÝÂË3ÈËþnò’F^,#ÍwÙ„f¼Iž×Ès9ôì`áP+yö÷³ƒÉÁáÂÁ4r`¿]8Nö?cöÛÉ3ûLÂ3V²ÏDž†ÉžÖÈ^<å O‘?hdFvkdW(yÂEšœäq€óx+Ù §­ä÷Ðÿ÷dœv,!id{Oò¨F¶iälÕÈà +Ù¢‘‡6[„‡4²ÙB6{øMÀ¨M­d# ÙM6ÀiC+yˆ0’< ‘õ÷?#¬×Èýëf÷?Cî_Ư[áÖÍ ë<üZ¬íX£‘û’Éj¸:Ús¬‚¡«Tr¯‰¬„[+Ç’{àtFî>Üí$wÙÈ +7¹S#Ë5r‡Fn×Èm¹U#·ÜìnÑÈÍnr“FnÔÈ iäúÕäw¹N#Ë\d©B®ÕÈ,ÖÈ¢VrM+Y¨‘ó· + 42+ilˆ[IC©o%uKÈU©õ% ¾$RÓJª[IU+™§‘¹©ÔHE©I¨H#s4RžF¼eŠàÕH™BÊ<|élE(5‘Ù +))v%«I1¶ Å2K!352C#ÓázºF®œ!\©‘ip5-‚LÕÈ”V2Y#“àÚsa’F&j¤(šL!…W¸„ÂVr<¸ÂE +Æ»„‚V2>ß.Œw‘|;MòƆy2vŒ]BÆäZ„1v’k!£[ɨœa”ƒä„ìV’5Ò"dYÉH ‘éF´’L€™é&žáVÁ£‘á—[„áVr¹… j†9ÉP3RFk$#„\¦‘AÁd`z¸0ÐMÒ„éá$ý?@1 BÈ€e|ÿ4“Ð?„ô÷ði&Ò/u«ÐO#©?u+I1‘ä`Ò7i°Ð·•$9ÜBÒ`Ò§Œô.#‰éå ¡v!!šôT‰;šôˆôéMâí$™…¸Vk%±^ !1 +‰Ž&Q‘.!ÊM"­ÁB¤‹DîŸq7a&á®±Bøâ‚I]cI˜FBíÄ ³9[‰î9Ü$¤ŒÛIFìpm׈­ŒX-6ÁL¬‡x‹X–ñfxbn%¦4bÒŒNb\Æ+f¢xxY#’F E5"(Dðð|+!e„ƒQœÞË,`;Af‚÷â²—ã>ÿÿøAÿmþ?QèÿƒÑmendstream +endobj +307 0 obj +<< +/Ascent 759.7656 /CapHeight 759.7656 /Descent -240.2344 /Flags 262148 /FontBBox [ -1069.336 -415.0391 1975.098 1175.293 ] /FontFile2 306 0 R + /FontName /AAAAAA+DejaVuSans-Bold /ItalicAngle 0 /MissingWidth 600.0977 /StemV 165 /Type /FontDescriptor +>> +endobj +308 0 obj +<< +/BaseFont /AAAAAA+DejaVuSans-Bold /FirstChar 0 /FontDescriptor 307 0 R /LastChar 127 /Name /F5+0 /Subtype /TrueType + /ToUnicode 305 0 R /Type /Font /Widths [ 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 348.1445 456.0547 520.9961 837.8906 695.8008 1001.953 872.0703 306.1523 + 457.0312 457.0312 522.9492 837.8906 379.8828 415.0391 379.8828 365.2344 695.8008 695.8008 + 695.8008 695.8008 695.8008 695.8008 695.8008 695.8008 695.8008 695.8008 399.9023 399.9023 + 837.8906 837.8906 837.8906 580.0781 1000 773.9258 762.207 733.8867 830.0781 683.1055 + 683.1055 820.8008 836.9141 372.0703 372.0703 774.9023 637.207 995.1172 836.9141 850.0977 + 732.9102 850.0977 770.0195 720.2148 682.1289 812.0117 773.9258 1103.027 770.9961 724.1211 + 725.0977 457.0312 365.2344 457.0312 837.8906 500 500 674.8047 715.8203 592.7734 + 715.8203 678.2227 435.0586 715.8203 711.9141 342.7734 342.7734 665.0391 342.7734 1041.992 + 711.9141 687.0117 715.8203 715.8203 493.1641 595.2148 478.0273 711.9141 651.8555 923.8281 + 645.0195 651.8555 582.0312 711.9141 365.2344 711.9141 837.8906 600.0977 ] +>> +endobj +309 0 obj +<< +/Filter [ /FlateDecode ] /Length 680 +>> +stream +xœ}ÕMkQÆñ}>Å,‘ôæ¼UÚZèB+FܧÉm4“8Iýöž'Oi/‚$ç?Ì\æ·;ãË›«›~}èÆ_‡ír^Ýýº_ u¿=ËÚÝÕ‡u?*“nµ^žïNÿËÍb7çáùÓþP77ýýv4vãoùpžº7§ëÝUýµøqœ/úýûÛ»Çõïc};ß«:¬û‡ÿ¿5?îvuSûCw6šÍºU½ÏO~^ì¾,6µÿóèë‹ßŸvµ›œî õËíªîw‹eýCMÏÎfÝ4®g£Ú¯þzV&ç> +stream +xœì½ |TÕµ?¾÷yÏ™÷Lžä5“wÈ BHD$ I!„H !“$™˜! 3É$µj{ûÿ|þ 'sæœ}ö^¯½Öw­½'A!¤G‹Š‹&§ô¿û·•på]8Êjš«[5‹e„ðT8üjVwØî;ÜSŸ›êZë›·ò/±ýÐ~g}Swݺßo…—…P‘³¡¶Ú]’ÿB Âýi pA÷ˆŸŸ#š;ÖüÇc!ÿP)ýmlrÕT7>¹X‡Ð¢Ûáþ/š«×´²§ ’ Ÿm-Õ͵§¦¼1 >7 tÊVW{Ç•×QB{^!÷[Ûj[°› ‡Ï€œß…x„øTþš†CÉû•lö-TÇX€ ­À²,§e¸ß!Õ4|EŽ´Ù9d“Šë²#Û•+‚âƒŠÍøÃ*„Ÿ{ÿ‘ú#LßuHÀ2=Ûˆ¾í?ŒÐô +HDÒ ia=2 #2!3² +òA¾Èù£ˆ&¡ ŒBP( +C6dGá(E¢(bP,ŠC“QÈUÑ×:°£.tˆ«ÂÇtŠÐ +WŠÒ:ô,]èU|=ÞÂnÊÖ +øóèUq—¬R%Þʦ?ŽÇQJd÷Áó”¡bê`¬Ï’WÙ,ÐgjG`hò=!ðË`”`3õ2QyÎ^Ç‚rÛ/—ØÆ}´™D[/*îÕwÛú¯\).ç‚ø%½|p/%õrQ\ëæ‰ ùÅå¶ÞŸdg¹{ͮʂk Ëá”|‚Ëp=;+ØEuÊ>®ŽìWD“:îI$<‰%>‹áPòÀ¹ ×!Ó¹ ç.L±šíæ(»Ù^Ç¡¡v6hè#eŸhøâ³6!úx:ÚÒáÁò78=žáyÄc2%^#hD^Ðd²B.Ê‘8AY–Üc¤‘Mç/sÝ«üùᛕ{qíp¿Bx>Åc>z Ï¡#ó8B˜³—$ÓÐл„ï‹3.¤LÁæà<ÂŒ]ǘÈcÐÏy&žˆô³ !!™? s9 :¦û‹, +äq¨¯¦ÀjÔšõa¡!ÁA“üý|­³ÉhÐë´²FU |l¦s¯øÆgœ›1chù9r!ÆeíÖTÖ.Úéa¥Gº.²›pdcO=.ŽQzKpi¤òHCO½òNý ʉ(<¯TéÁÅuxºò^)ŽâO*Õø°R}Pé; ¬À÷“ã.<ˆ¿¬ô/,ÊRÎr»`൒Q•#CÐ[#9›1ßPdçÇO¶å‡E ù‰“Ã&Å$ÄOÒq%Z¬‘“ƒ%™5XÉO–b¦Pƒyþy3èÔ­Õ)C¦OL/}’òù' Î)Q3qÚÔè«ûúøùc1 ǰ¡pΉÀ(|HÅþðž65rNC1S¾óÁºvÓíû–71ááe?**íAùퟮw^ßñÊÂð¥¿\YþóǰõƒdgBÅ£»…Mõ¥K­L…a£3§ÈŠ/ü4)…Q…<¶í‰tÊYœÈ&OÅá_FÞ½þÈÿX>þ˜…ÈÖûÌ•…÷Á¿hÁKG€RQ‘#!hñdßŨĸX°/ž\r]ä‚)BÙÔ)‰)6TŽÑÉš9F“h‘5v*°¼_F?1[2à?˜Ñ`ÊŃpés¸8‡G§Mµ¤¦øùú0Ö©Ñá°Ÿš2 {Ÿ{·¿Œ?¸ãðïŸ#·p~ëúù«nc—Ìwm€³WÐ÷ÿî™;îûݽ—“z¸³Ì‹Oîÿ/¬ûÉ_)Ãï4®üÅ—:OŸ\µòÅW:O*/öÁ]mï·”¿ +ú¿Ã6\uåCþCàYÜ^v”² Ãa>Ù,iñ†d‹™±šàÝÈ É™Sôˆqr m’Å +ktÔ’¸Šð2ëÙ0%9šM0ÈQȤçRq`‚!8•—ƒ9ƒŸ~ªéâð$¦O22@ó– 5øÁÏ?ùü?ANä3ü§ÓžÌwŽLw‘NzÑÄ©s¿O2b\é(ךñ$Ódœ©ËCøü}ó>t·æYgDZƒl”Mš(Mô$€ýô~¦À¨Àèx‡'GMŽž¦ÏˆÊˆž–œòð*´w&oÇ;Œ;L÷£ƒÆƒ¦°JT©ÁÔ‰üF´FÄpDx$("Xá´ô™4âvªêÉÏÄéìËJY÷­~ÙSLKâžú7†V®¼¸å=åïOŸ IŠÏ~}Máý "òïιåÞèl|OþÊ`Ì1aå/Õo88›¹é°òE“òÙÎ…‹þûøAléHZ>-kmJIEdä‰äë69Êʇ¿(zu-E௰‹ú+_‡ŒóPGg¢nêuRà TïíïW>cW +˜É!½€–DtÍdƒéÜÐ@FµKð´XÂf‹D4“fB˜]©ü¯r¹  gi¶b?Á¢ütøÇÊ…û•ÓÃÙ‡p3Gz®lR>c +iÿv‡ K Œ G²Æ0¸`!ZvÁÂS™˜T?‹/Œ±˘Ë'C(T.(Ÿá|f!ö¹§ø±ròò«áŸ*¿QÕ Ì<öE°K3xÖ“^ ¨P#r '±•¼YW)cC™EÒɬF0³˜eÑÈi5F½`1›1˜ò.à|¨©™.žO>p!Eµ3¬ÇbŒIÄé¼9Õž¨Àõ™Jo›Ò›‰ë•™¸¸ sïÿâÅg”m¸ûÌŠQsw+ÛÎPÚÞgñÏŸˆp˜‘W‰,Ϙ8d‘“¤3½;®T“|Þ@;v_Bìiv3û8“8|öèðY&‘ç†Ï#'Ç]a$\±àH/´ÕèAùLvÊ0ÁT€h9ð<ì¯09.“žJ\qÿYû‡›îRSžÇÊûRü>³ŸÙ ¶8Ë¡ajÇ€åpÆ22(V˜ýH¥Cœ‡ýrf9ëb\ìf+ J+D¶[™ °á™ÍѾ»àÇ:‹8|žE,Al&Êe‚ÚÉ8ƒ¦ç‰û¸0â/ภ+L0<°qôìug…Úו•WndWÒ9•ä°2d(ˆ©þOiÀô<5â‹nHr†: C¥â¦¿oèÉ'ùóo¦8 îʇ\/µÕ”å°95'*Ó,ˆ´Kl€¤³FÂ$¥Pjð⼈ÕRb#)±‘(ÒbR,àüEâ‚â‚fbÿpAÕA¥O³@ìd gÞª½©òòþ_Õ/]ú¿[ ¶§/mîl+¾#ý¦Uü§ÊgÃÏ+[öí»›.ŸÄ·Þ½ïPÏ‚¢Û•Î×~dAÑf¼ó‡¡2w³€dT꘬‘l <ÏãLÖÉ1N>GDNFı¬,b t!kMçTù˜É,Ë I!Lz«Å?eI8À>=*ƒ®·àyï+;#pûEÁy¶¡ãnÍ}<÷Òùc„ ò¾ý Ãt¯#Œ]àý?Ä Ÿ) l¬5-Ñ9­eØÉ²ŒŸQÀ¬ qÉ J.˜3(Ô d\„œz¡aÄm"2±Ë@*ë@è±ËXð9 %ÔJݨ“_#thylÁ¾BŒ0/Â5x5–h”FÒ1/D5,øúlïð:ætÄPºypÛÍw´,ÿýíQ·ü¶jàÏmÌÃo=ÆÜØùdeõÚ¬Ù¿¼.åõGÚ^£²çÀnöÏñhŸ#^æ´ûåû–:-ñù¨<ÖÉ–YØëì¶0?N8ß`I% ¾ „e`šà+0ª ƒ„e`dLñ°™@Ù–<&•ß«/%SŒÎDî¨-C„¡ÕáºÉÕñœò‹g€]ìaO$ØL M°X!H §«'D8«å5çK¿êõ«Û›Jï^ö|MԲ眧þvh÷ãaýGž»sCÕé&¾×fëÝ»ûõèÈý·®y³ 3#*깩©gl¸ù¡ý·-«‹£ó) +la µI=ú¥ãz(H ~ò:-°ž E0dÎÔ"‰sê`ê +z(q-sX`k ¤ÉŃ$XF¡†úß$‘Ã3»ó{u¥DT7¹eet ÌãW Tjx¹¥™©Å°§x4Çi3Ñtœ¡¦«Óéî@[tÑ>‰È2•$%ƃ±7+Ï·ŸŸs:kxY ›>\‡þüPêQ¥W½È2Cûˆû8r°¡íŽiÀˇÙÂÞf ƒ\*,Ø)Xó-E~N#v¢ã,ÅÊp ‡ ´ @Ic±Sy^Å¢îh80è6eÀKîéêfßËJì”_;¼T~1¤##Váe‘n3°rG†÷å&-¿îµO×Ý\ü»íÏÖG-}zÙ‹èÊö=Z±›?TÙe4üúä–‹KÙ6Û³‰‰è½í®¨(æ¢ûMÀó³ƒOF?wDX5¬ÓOã ó[eÒçëL+cËÂÄ[!KÑ!)P­ñÔ¹’|#Cõ°ÀÞGN†x:âQ¼gÎW­åÖñÌ<Ý<ý<üɺ +}…¡bòJÝJýJÃÊÉkukõk Ý–µ“wèvèwvL> ; ?`80ùÝ#úG L~Z÷´þiÃÓ“OëNëONO~[÷¶þmÃÛ“?Ö}¬ÿØðñäD¢poLO#3VrÔµO‹„+ìc·µÞ{vSÛ¡7°)†ã+ø~ ×ÍK•à0!‡±cU,CÇ~ÍÆ #þ|Ù¥Méι’Q#"2€uújœ¡Fçä²Ð¾õS",H +²H1A:)QbìSÆEìký‘n×¾ëþ‡ÎÜ{û}§ëïìØþäÎö}Ûo^£xôÓcãh/rÄ UHêù* Ua U¼5Sà!-ä¹ÜL3ôÄϸ0†x7Õê!õÖ4Lȧc†øô„’|DI>Uœ?væó–…hØãȱðV«V§7M‚læ}|¬˜øZËdúû˜ œ>ÀÂåÛ C+D ÊËp€¿Õb6@®!ë9&ˆ—íL8Dh7€UëŸ&åâ'òq¼{#Y™øáÔÂQ¸Ç´º}‡¡ÃØaâ+£Äˆô‘xcÄlÄ0g‚W¬\wæ:KÝ™•<³ª»¡tº%ðd˜¥Ðµ²4«¹ªøTƒ¢(ܦŒéû6Õ_Qzã²¥ë.Û¼tIÎÊŒ‚²¹7Ryt)÷‹ù4©p¤êPš1Ü>õz£‹m³‡º¢Ò\zM›¯+*iöL£AÏéìáSo™î/Xü¯&ù_;<ùZ.sƒYø<4p°Éð')§L/‘ÜbJ”Ûý RIOeIúƒìáQ« Ù!!òwdCf5œÂ‰ tàqëÀ,÷Qö‹÷¼€ã^^º;&h×®'•Ê¥>åø†ÝâJlØti{É6å凕“Ê¢7žÝ‰~Žg㇕Fe`Ó÷NµÕ~ÿùöOæ¼´ÿ]œWÁ ÷æë?Ù}Ë-ÊO”÷‡ÿ‚KWâì§^Ú¼ûÕrœøÀO×nåϺï®7ü‚ï¿ýåszø½£V71uÊÌ;o©^¢Ö¥Ê®|Èï†:&™Ìú +\‹:¸Š2Ÿ66#9ùëm¦‹dRBÒO~õÒwd6RWn£QØ/5 +÷¹ì`‹'r¢»™N€âˆe§øñ»âªÞºõQexÙŒYgOïĨ;¹ë¯k•”Ÿ¯k_‹3^ßø¿â|õ²‡Ž6ýeí+¿Z²äÏ©Sñtìׇ˿Ï]sÉœ%úyô£E>(Û¡çµH«ã‘6SúÐWÈ>kÖk±ŒÌD Ú _ªsƨ +.zŸˆØ@ØAæ„Úh;®À}pTo]Ý¥üà’’_ýKx!îøô +X‡òŽòþsÊáî[ÖòÉ—»ðV\óÖi¡M¦´-q\'ÊD  "¨P+(Tp P)Upí²¤•5"]J“Èú«6Ht¾B-eÄLÆ'z*µî58pÛk,cNÛ‡Ó˜/ìC7ðç)9dž?¤5  ‡ëz4¨Ô‘³AÄG GCèÑxèáÚ%ÉM‹@hÑÉchùJRÀ0ÙrûðÍeL±}¸wçiBDî±átZo{{ì-mt@\ÁØ((aÞ.Á³ö +É·Ö§# Â\&-ˆ$kK +á:4X«§Å§ ³ê_UcT†èúÄ„E’Q ¤IªSÔB£Èê;âjIláÜñ‘Ú·¬P9ÆX6mQ^:¬üNyñ…c«”{ïĸ3®â³í‡?[¾ òWçV¾ô>ÞvdØ•WDŒówø‹ÍûÖ²šî MÊ;ûß8›¨Î5b¯R{¸Õ‘ ?OOjC,?db!°L-Dª`Ú9”# ÜÖj$d! áC<&rqê…f‹à8S†.\­š/êq,©d5ÓU8\q¦‹y¯ëŒòÞ™Ã;Ïðç‡1ÎKñÌËÃnš™(ZWLqø{×0s@ªp“ †µ¶˜<8.õ‚A`蛸U"¥W©cìTùŽh™ –42+jI°ÈÔ2H¬àW£( Ãk% :º¦hV+ð€6',†¥bFPË7é¸ìŒò—´Ë¿-¶ÏÚv'~iò—¿;xrT/ÓZÌ_sN$‹—/‘5\Ž”$0`_NÃ$•xQB2CP—Lx–3%€C,ʤ,CØX 0‘ƒ´Þ³ƒêG¤ Ñÿ&ï&y±Ä’ص’gDÞ‰!GHÓ˜t>MÊåËùUL#¿†_#dL2«hàϰB€ÍG QbŒ”ÎLgÓù„b;—Ëá—°åüRá&±©ãW+ĵìn ¿^X/na·qÛù½l¤Z´Áv (…*~kÂú¡íÌöχûÑüN¦s8}è"óÖpœ[Vì%ºžáòÊ ¨“`xƒU0|@ùW—a’Çgß´VŠËÏ\AÌ[ö¡e ³GÕ¾…^è[‡C @¬ŒFÖ`V–5, :!zˆÄ?â-§åuH'ó!]¦ ÷%P„¤…ÄiuEÀÄÏp¡j鯚ƒ)foìGIcÎGPÅmŒh”ŒÆG “Â4qb:3EžÇ̑׈;™mâ6y?s˜Ù+Þ-ÿ˜y‚ñã±ÈX_ìÏ„²&C4ŽccAKqšœÆL•Ò5iržËÌcçHyš¹-as lÔ%íÄÛ˜mìâ6Í6y>ÀìeˆGð1æ‡ìd¢1²>SCØáäÇ™qºòÞ€2|âÔIå~ÈÂ~r)žb8h?EýüY‡ƒÈüfEQ e†§6îöÿ‚HÐô§Õ¯}x霃ƒÄ%·ß{@ù¹òñcÊñm‡ö»ñ ÷õWV´Ü‘ ÐFÖgq<¸):¢&¤Æª×i%Þ¤‘a"˜‚©gòuÆ,ixE„¾'| {p"{ˆ–Mõ`L|ö•_)Êo•Î3øÕ&å±þ[oæÏŸ|êså“á£|òðk½5v¨u Rå *oÖ;¬väc23F‹!ŸLc ­`}+teVvAŒÕׂؠÈ`£-(XÐÇE\UåEnEÀä¥î€ÂeOr`}Sbƒe–µh­•­ä½q¦šAY#¬W©ùP ü^ènºgüœèR©ÏÏüÖ¬ƒÀšgã áâ­)g]½ò¡t¬U9‘8/¡`ô}Ç Ìûù"ÞD 'VÞ×Ï7Ó¨Ók01G¿½Ÿ¯~’Õb4Ht$?Ý$<Éñ +ñ(ÐLW¦<ÞŒ¨1Ã’‘1¡*ËéÞßrãÓóË*ã*Ó*ó:ã:Ó:³\9’DXɆ”6 Ç“‰¤f W‡¹?ßÐÕéX4}ÃùÎÄÕkß_wÜÎ˯½÷,¨ú“Gîhî¾ß¸ºWiÀö:‡·óçÖ{høÝ•¹³ û´×‘®Ãz-ªÁ’̇……fÊÚÐ0¦[˜£%\ET™yAl¨¦ olƒÃcGvÚdŒf¤¶?âºÝ +7¸·Qi0ªtØ´HÐMÂVŸ8È’2PNµ¦ú¤ú¦úé|4Û}·û±•žD„¤Ða˜æSvï…ŸdLê£zûqgcj_®ü鯊ê~µG¹Oùu×§Ýq®÷]?¼i_÷k÷àÅØºêMþ¡SÓÒïßœ;?!4í‡{~« &Oy=;çè¶ŠÊè°ißÛð>‰¦X£üpÍÛvŽ@<  ‰§K€4†"‘x!`€›<-÷¹ý¬:US<êõOñÓ¢Ú(x€Y‘ógý¹(ÅÌD3Å"T$ŸL®bž@OˆºMJÕÝÜŸmü‚Î`4ü)@ñKçÕZ5F]à}þè'Ž:^oÐkdFË zŠþy‰áý‘?‰ùþÍü Hª0/Сœä£ÓB–Oð|°O(Îôðå² ‰ØPç E¡;vû3»sh©ò¦ò›^ˆXœ¢Ê¢tõ)]c¸ÝMuÅÚÈÇ’,ˆÇe²ÎAA>‚EÏ^7ˆ†£ŒJÞOd‰}N &£Xn:7—+”s‹…z¶‘«º¹n¾K¸ƒ;†a޲õaØ­ì#,z´ƒtÂG”K/½ÍœwèNC;—Ÿ#åÊsµeR™¶žid„zm½®›]#ÜN·UÚ¢¹]¾‡9ÀÞËcò?’®\`%³ÒÚŽ85ˤ~0I|u¸AÉ~Cl sŸ“ãR<÷Þå²gâ(輇ä"x¾#Ï[`Þ8‰OëÎÇ /ÐÒ¼œ Á–áh: •D’ IÇɲÎ4`zÞSÇJ™s^-мô ŽÑH Üx)N›ÉL—®×æñó¤<í¾Bj`øm7¿‘¹SÚÇÜÃåýy$`\C¦R ŠÁ 6–âÄhM¬v:“ÉfrÓøiRàÏéZ²,2—™ËgÍÕ.–ˆåR©¦T.×Ö¡:\Ëåë…z±NªÕÔÊuÚµìZn­Ð-vj:åmh+ÞÉîbwqÛÅmÒmš»Ñ=øþ€°_:¬9,?„Žò +Ohžóh–¡!ÿ#H¢_Á¿vaøÙ(qvøý?2Ï@¦ñD3Îá,ÑÚäö•z‘>^þ’ˆü%oeÈ* ¤ˆ"â$ ‰Ó¹"{‹g ÇÈÄœXúªC… šI`o,“Áæ¡<\Á.áÖ±ÝÜìæ¤Ì<"™DÆR´4¦ŒYÉofö0?àÄáJ:˜ªVÂ-Q릷ýû|8|ÓŽÿ€À8|©úl8ƒy™îÏ9ë¶5 *v„Hê&0>Êa+xDÜŒ cA)ƒ^¿’Ù·†’«»Ý.ý$Îu‰˜Ä6»¨nã°c·7äò:¶ßvù†nËPþàe×±ƒÜnêãJ”ÏØ¥‚Ä£XAÒ‹˜É¹Æ…–Jàñ8‘%.? œ0ýꥑŸCãöÃAwÕ¾;9JDN…ˆs ù³rþÏ“”rÁg×߇v©þõñ+72´Ž0ÅáƒÉ²YUÀ$²$*^½AÉ4²C p +.áè‹dƒ’°GÝïÔ§|Æ—ÿKyl ˆñîS6H¨ƒ¶«N}_îMi¤ñ+Sº¦®s´¬Å‘bžõÒ‘U¶±pz¢MlY( /BõÌjf+#TQ3óÞ88$Ô);ðj‚éבïØP<v8é¶oÞÇÂÕêYïáL¤Þã&3±ñVÆ×¢øÈȈL»EÇøê +Bø®\…4öK$2DFEDFeDàÊÁsÇMÏ0 ò,îR0MŽ¿D ãtPC²Ìã=1c÷~qÃÍÄžíYŒ{#ñ(ìç[:Ýž9gsûâ½Ó3r†²^m}ìLWKî³õÍ=)ö~œÜÏ<¶û¡…E ßiÞqß‚¢’ÿÆÍKÿÒƒÊë« K5äË0`”Ë@FóÀŽÃP¾#Øœo*Ôø |\È„¬õ¬tøØ•blÊEºÅÑR:Á"‡;­eBt›€ZNöãæEÝxwqùáieËî9XðheDñ#KÞÿôÖu¥¿ÝZxK;1eÊ̌Ÿõž½II'ìö÷_{Gu—D€N^>H·‚N8 +ÂB[o1N +âüƒáŒ'ŠàÝÊ[`N‰ÞÀ茼ÙbÎ4"[ο€] [kZj ±BnD>–(³A§*ÈürˆËÍFó,3[9¨¦˜*Õ­JÓXuz”z¢ +´ÙÃâʨ±¼û“3öè6Í£m¶|Þ¬;úâêÖyÏÔ5=œ¸æi姇¿¹£lÏôë³@ÉûÒ2³V;?zùNì*ZØPͬøûÜ»ûÁ… Jß]µóPÉü…ïªòávƒ>5(ÝçˆD.Ù7ß§Ð\ ß"Ø×,ˆ‘#µÄ||ÙˆHCDPx á;ˆò­ý4òõÈÑË#gEò•2ÔÜZM8‰¾IÁ§•Ô°#Ážšìòm’\—ìÒº"]Q®èÖ>¬|Œ»¬»|vùîò;h=è#ìÓ7M§¡ù4öõØžØY±\åè5o¿–m9o³ì®uÛߜƦ ¸G­ùÕÒˆš³Í_¾µ»ð­î“蘽¦póÚïÿøð®èÓÎýÓ¦žˆ}ÿÕ'pâêyŃÏ'5Ò8µ!ö,µ›~Ç ™×´µ ÙÁóF£6“7aäð­h©AX/ëdƒžÓZMÇjŒÈ¨õ1»­—,Òßtí„O#-Ä”%Ç[UF¦ò•ã=¨1•fw剬L˜>I±\eV#…â=¾Å¾_ÈÖ‰§4bõ‹Bé©DDìÙ^çM~>89HyãäÓÏõNê5F?÷ªàs ¨±f×P2ûƮ—zÀ.nTʸStž'¡ŽòpAbSb“`#±Élb,F¸ Ó3Z‰Oað!8„ +%w3¡Ø`ó%m*¡€Û€Ö[laHò5C!fÇox2±%_\9 F'uâ Òµ¨ód-*cÂùãõMqŽqŠê§lœŽ‘沞òÄ,$ô' “ŸûIiùÒNÝ‹ãÚ J«WjïØ›9µìã[vìÅ7€8râ¶-¢'ó‰mp<Ùn§v=o4ÈZF£ã FƒÕÂø˜á‚ ÌË©cÍk +|Ö—ê±ÞnÕ¤X†7qŒ¿1Ê9‰ˆÏ@£ÊrÿYþ4ªx•·.Œ/Œ*žž4®ZÓé}ü® YADˆÖ‘‰Ç’@â™h\çæ§7kä•Ë÷¾më&á£+ç¿×ìý¡†yhx)s_r®oé²_Þ#ø ZÖܳ;²„ÆX“ ² û|Z“'ÞÃUƒ–r눺dûC,ŠâÄH‰° +0}ÀÁ·b*/’}S*£ƒCרÕÕçÐlPlÕTiŠ50#RÅ1¸èêOìé§_ðùøÌå²#P)cÒ.² W³Í¼Åh!Z°P}±‚†!Û0ݦÀJŒ‘‘¹Æ(ß*Y—ZÍØd”­“QÒj,±Fd5sè[QL^šqN¨<7˜””dzþøFÃF LhOvåÞ¤HmÝ2Q¥ÇSç‰3&£Ùh 3†™ÂÌa–dc²)Ùœl©2V™ªÌU—Ñer™]p›¬.ërk‘•#呹gUìc‹ëGMIüñêìÐ䦛Âp*ˆƒ«º|'ÿÜÜ9¹‡*z„Ø‘Ûn?™ÈèG>Žä•qî5>ï%>yÌßz´T#h$™xC¬£ –Œ,ò©&:8®D"ݧ2ºÊG2$ÇLžñc¦1 ™R¾Tjà¥DÊ:Vâ2…LÑ¡+gËùÅâb©‘oVŠÝl7dÝ[˜ï³Û…âAÖ‡Ä7ÑR¢…—¤²4ׯÔ:z™®?;æ}%|%ÉP'~kø÷ÃýLÄð{ª\Ü6h’ +t‚&óAA“2Œ:– ‰B}5ѶµAëõ8pRp€Åhàƒô,! 8¬‹ ÒÇDà šIˆI0厷U+‹¾×¯»â\qEqBå… çŽ?QAýŽ9ÃmÒfÏ—vgÌ$ß•¥[¸Æ¹ 1Æ=ÎÖÝiF"Y†p$VAfÀBòp%ñýÄç S ¹‚ r…¢DÍxß4æSé{ëîºO¢Îëþø™~ÎÓ ïbîšxS-9åª_Q¿±mõ-GöÛ‹G?Ü6ÐO z|"¿nð8ökûõõøõÉ_é×'öëW •ÂÊ)àÆï +bÇøöñ‚s kŒSÿ•}]ÿÛýQóÿ«k¬O?õÈ𮪷z¥Û¥»1$‘ƒü\¶Ãâ[€‚ +4kÑ{€Ag1°áv÷dĨ9¬Û [®›¥ã*½¾øÔ×N±^xOø†ðYá\%?º­ØÇPlA¿Œt꣢Y5>÷ÇÂY+.*Ý6=ý×ÛÖ7<èHN ùë›k‡Ë¶nüäù-Ê‹·nÄgö߃ç|±{ouͼ“Ä×_ls‚Ò…>v]•ç}E¢ÇB–¹ÞÚÐá"×G‡GGiÐhªý¾øA6–Ì +0pjvhq#spð+Óñ0C.Ñs8æ¦'æ.È+6Äp_#O¤áý‹kJîÎÌÈÚ¸zñÝi'¾4ïôºÇN®vÍ{¦¾ëÁØö~œØÏôí9TZ\ô›–;ï)),ù5n¬v~pæˆrÒŒšà 2± ¥‚{dlEáèÇŽ‚`DÓ.ÆÉ­¾4ë +”+¤@·–]`Ù`ZAÒ.äcœäλ"È'øpÏ¢æ¹>“¦Ššx™'ØU©×xÜIvW÷DnŒd®J¿Ò=éרõ±eóN¯=öB—+÷gõ·ôDÚžVÎ?­dÞÒùøçºï…$›$^§ÄIk +KWÔýû[²÷^<ÿ“»,(,y×ã;ØãÔ÷;ôZ œYÔ˜5R¤çã°ðßj.63•µjãÐ Z™ÙÃZÍz1x<÷ +å±7³¸Ü1Iä%Ä‘U ^#éuŒA ï2¯ldLYIHµ:;‡Áat˜f‡µØª©ì;l/¡æä^ΔÝœ·çÝÈ›‰ÛÌ;Óšž0šÞÿÙð)pÕ €oþàð‹žrÌ2裎·…¹·'…ÙÂ2Cõº0‡}óýŠ ,(qÑk- âBµ:,B2a0Š|D™Mdg¾ÅóÅ ÷|ù]Þ†¿}bñEQê"§{;bmȆ“}“ýâüË5år¹¶\WZ¦õ¬vÎCóð´„~Ÿod™Óß½ÎÉyÁ-™ñ| „Cy–íþÑ/Œâõýë”>Z2°:²v bÛ#¿¶Îºó6¬ÿÝM?æf=[³¨&c,©­Õ}¿ <9uZÇ’ªëyST÷²Ÿ¼Jd¶ìa?Ä’ƒŽ™4ä™å-þ†÷·ÂÀûû[2-¬ÆŠ]þ¾h½æV­çwH%(²u2Nã$ÓÀøÊ¥–ÊÍHäfz~dûáŒÎc¿½#p5m `­èöëä+É—¼¨ÿ¸žä–ÔE“ÄҚʲ+‚•Ý]‚0Ñ3a½= ]‡BÌì¿«Ú5Üž¤wdÜx›º©Ú…g*¿Pãg'ØÉ ® +x>ï("=ÁSG>\tFè=^oô Øh€:ék»`Š?‘Jßþ$<_n˜e`ðl0º ù&A׳óé«×bGgX ¶Aã–VG +›Ó¶C”•5 Ë÷þ—e ßêÞ󌟅8[s]Žÿ¢Ê_>Â4]îé]±ê»B+ˆ¬Èñó +°F Oðw¼º®O¶— +8!¬å¬'K® æ9$zÄ ‘€a\èA„ù¯\}åéæð5Ìf#ʌĂ«üø(>IãóØ<®ž©ã;˜v¾SØÆlgwò2…ŽO‘î’u9Gìyñ!&àÅáÍ_@æœËöCÒ…Ñã@ò÷€'nvÌâI~Oó ™ÕÊÀ!¨œç¼c ¯d6Þkvý °Ä]ÖG\†Ÿ_¶,{5ët]všœÍgÉ ùR¹žqò«ùù6~›¼QÞ#ß%4àp9Iв~¼ä«M`£¥XM¤:Ó˜Øt~š4]NÕNÓ°¹ü¹^ò]†1GØ£Üø‡äG´'˜§øRŸü”N7~…÷ñÉøÁó3ñ÷úôVºRú6(h»…—{Øž¡*uè Ù‹ºÒâõŽ\.žç0¤æñ Hœ¬¤Ëñ“7­ ZÔÄKD•‚(pñ,'ŠB¦,r˜“À6É7D¢(I‡ÈŽÔ±ßô˜¥@ÕõµG–Ñ!ID•Žsd5WäxÞûq>üdðïÑL ÅG qRh&ž~w—Ƨ éb†|½vÊÃs¹l>[˜+æÉó´e¨ /âË„rq±†¬éÖ1ul-_'Ôkêä:íjÔ‰Ws`î]òjívv·‰¿]Ø&nÕlÝöáýì=Üø}Â=òÃèaíSìSÂ< §yã¹81Zž†§sÓÅirÎãòĹ7â±AîâÖÈwrÛÅ-òA|w@Ü'÷‹¿ÃÈúdß]z\©aˆÖ4Dܫà +¾þìŸçûLŒòÎÙ·pW5üÎð)ü¬’Å2e¾t÷*àݳ ;#žäÈ!:BFA”X^óÈhˆ‡ ¡×éy3èa¶éâɺ¼^«1!-+»Á ]o¢¿¡H@¬däMFÎäq-z²*¶ÄÑÈjÚ’WÜƽ£Åœa¹æ$.UÝ£S­O×çèWk@Òú­ú=zI@<–Éà‡|q€!EâH&ΡÉ3 Óyš<9WŸkèB]¸‹Y£Y#“)Ômè6îvj¶Ë·ko×m×ßaØJÚÇì“÷j÷êöéÉQÞS"™…Y:9†6>pëî°•ó›æd|ñâ3 óBÃ[«–®]Å¥a‰O¾r æD9ÈUƒÚ!Rú ÈWâHÀ½2ÿŽea©¶nWÞILæùEÕ…!sŸ}PyÅÍ7ϼkƒ-yÒÜô¤[Ÿ]Eû†zvûZêónŒ¾–0+ã®æu¶¬¨?²š”ÄEù{šï¹¸nWLxñ«˜SBðãBb‘å)Œ6’ßàÙè/!¿ˆ‡üŸÇ !_߃A1“Aéc?!úðé31-ÎfòÓÄÃbüx!‰Å1®8ìBq Ó¥ L4N›ÐqÌäØQ}ª ¦!õ—6©@[­Ù-uZGDJq’ûû†TÒê7}DÁ£ UE– !…w, Mµ¯qå‹b²Àäƒæý쇢D%?3dæ]ëCÒ&å¤%ïØ¢ôÝE%ïc­+tDÿ5<%Б±«i]XV HþºÄ²y{Zö]\ÿ½èðP²Ÿåßa¯C!(Âa¦ ²rUAÖ*](2†šÎ Cd;ýª!ö%È-&š¼HN”Fò!?ò®øwRÝò-/ï]VþØ +òiÌK†{—•=Â^×W”u=ù% 3ç/ì+ÊžAOɯÛ$µ“eýËwZžZnœñ7¦ªø7—ÞÈð¼ÿïo†Sõ9ÒNȧ%8<¿U‰Í +X~ËÿþæïOës²lïUÜ«¨Ž!+XièAöqtJ8Žvñþ(K<ˆžn@UL*:ņ¡ûÙ°+›8„n€ûoC{ù-…÷.¦ïÊJh_‡ Ž(8âàØÇIÒLZ CúðÜ,4ÜtñÝWöð·¢t~*âàÝ€Ò¹ƒ(]HEK¥3KÉ¡ôò³àz'\ÿ´‚÷TÂ¥ï¥ü.¸æƒt܇¨œ‡ô‰zÄÑ)~Ý•£pm¼Ÿ^J€†ÇŸ>:>ºrCx ÷;xÞÍãêÐ2x_Æ]@˘7ÐN8¿‘· \&ƒW¹Õsñ Ê%×¹Hû+—È3ì<øüÙ´îurÏ¢=ÂNô8÷ì•3p¼Ê¥B;ôÓ‡‰Œ d|ÐA8¼Ö¢û1‡·2ˆy“ù;‰-`²\—ÃÝÂ=È#>‹?)ÌþSÌÿ 9¤g4ó4{5Còt¹BÞ§ ÕžÐÅêöë¾€è÷}ýë†BÃQÃëF“ñˆñœé6³`n0Ÿ´ÜhyÖšcÝçìÓæëçû®_ß÷ý^ñ»èŸà_èïô?°6Pè ¼8ic +:œ¼&„ ù~È/BæÖb³Ùzí¡öþð”ð_F¤DJ‘ÿõƒ¨ÁèãѯĴÆô©v†ªØB¬Ï„H›20z°F04 ÏtÿÖT„öã÷9Œø¡ûœA"¾â>g‘•IvŸsp¾Ê}Î#sÀ}. óŠû\Bf–uŸkQ»À}®·Üû¨ûÜ€¦^ÿ÷9€•È}nFâŒ`s¸4…ŽNÎ1òÃgÜç 2à?»ÏY͈îsÎç¸ÏyÀlpŸ (’9â>—P8ó¾û\‹2Y_÷¹>*“u¹Ï ¨áú<÷¹ ù]ÿ{÷¹fèÐäB­¨µ¡FTî@6‹jP¼§ÐßJ‘ +g+ … Ým:P;m¨U£f”WóP ´¿f£&xÙPÉH_íôS-¼×Â3«á§ZÊ_cÔi#£–ÂH«a¬•ðL ´&tTÃ3ÿ؈Yp¶ž+CТÚVÓÞjéÕ”#ôÒ?[¡Í +è·ÚÙàyŒ^MïÉÍqµv·5Ö7tØbkâl)S¦¤ÚVtÛnlìhïh«­nN°åµÔ$Ùf75ÙJH«v[Im{mÛêZg’|Õ£ÓÈ£¥Õ«›WºZêm7V7\ãÁ¬Ú•Õe¶š†ê–úÚv[u[­­±ÅÖÚ¹¢©±Ææt5W7¶ecY\Hl‡Ëêà «[àC‘›±›¡M-|„nֳ¶ñí½ú²}'Ï–Q5µC+} +(+•|᢬¶­½ÑÕbKIJM;„g€Ä‰ ý'^ƒ¶::Šj$nöÐUçj™w€ +5¤0ƒL” /§»H·€ºvè£h®«¤¿6jDIÐo-<ƒ::Z3““ÐéêΤvWg[Mm«­¾6©¥nçxQà1:á_=½È=bȵt2Ô³.Ôm‰é7Mzš wº¡M}²îµR¾:èä!Rk£OéFz]=N’ãù°c&쵸‘á5ïªmTÙ·Ô®v2˜Â7É_Ë}÷Npb}òÜwdzÖA¯+l¦²^×\ ¯¢…pVLûk¦½Î²FJS½Wëæ«žŽÒâÖz‚[襁ÔÑTSí=Òå¢Úo¡Ï·ºg²:‚ zípÛX£Û +ªiª¤ewŸ”ŠñöTCÛ;T{÷ô@Z«´«¶\Kj{á^VN5GžuÒ÷vJW ¨}7º¥:Vû_εGr*µ­#ÝA鵺QŽº¨<š¿ÖžÙPG½z‹›ÃZ¯ô'#¾I¬„5´?µGÄŽ›ÜžÍ£¡:¶“RÜè¦4“ÎÎR7uÕУ‹z†Qxû¢Q \í Z }‡{6´ië™+£óöÞÏÙ(ÏÕ”r™úæ±¶¦JC%Õ_¢O‚6·î›éû¨ÿø:ºè ‘ˆDÖj7GIc$õeÏ™t»c‹::‘y¥Ñ鶤&j§m#WTJ‰L^:÷¶:O­¦±‘úŒ&úIáÈI)%újñ’Fý˜¸ªŽäñ¡ÕÔzTÛõŒ1^>í_É“‡JÙÍÁ¨…US}} +ÆŽ3^Ñ–àÖw}®ñÞ\ÑNõ³ÕÔ¯Œöë¹Ò>b‘žù2>zÔºý\-åÂ3RåÊIŸŸ †ð=þ îy¢m¸—•©s¦`\|YAç»Ë‹ÖN÷<ðØÉj¸Û8ÄjÑ*ç÷Ln…—½ª©G­yÂ[ï*Íž+ò„3¥zx}owÓXK-éZvâñuùn'-TïÞòšHª²—ä¼uøMçj;õšžX=:Û<3‰ ‡¦ìÑæ~bl­Ô¢WÁÏz·ÆÔxH¬JñªÿLOum®V¸çH‡;ÖH*eÓqŠP!|"ãÁ§R´pd ½—×l€ãJàN|"F%‹êe6½Cî‡ÓÙ¸ÎIEhíKí£~’¾— íÛF?“Oùоú"Ïf£r:F6ô¶(+‚sÒ÷|¸ZïÙîvä‰9pe|&çsA¡êxä¹”Ò¹Cž#´¨”–ÂõÑQÇR•GGôP6>•@ÿ¹î»äÇäÑþý ‘óB7ªäJhïDF¤gÒç ¨€~"WÁ{1´[Hå9›ò¬R[HyÈû*/Ù”U*Esè¨YB[?]SJ¥@F*u·L z$üdÑçɨù´•JY‘[Ëä|´—$·,U:ˆüËFF^Hù/€—ò_Jÿ8ÑÍlèßÓ¯ÇvæÒÝ2•Æ"Êßl*‡":´‘"‘gÁˆÅ•xie•Ñ¡<‹Ž4›JdᄜxzóÖÎDÖ!Œ0—ò—M%U@[/9fCû¼‘+ª=æQ^ç¸e­ö©Ú½j^ÒCy$š]£f»mj6•ÝX.ˆžSúG¹P50ÛýsŽ—ÌFµ_èÖ®‡žR:réRYLçb6m5›êzáÈÉ¡ów¾›òE#6ê¹í³h„²±òõÌ#O»¯ã;Ô¾Ò[&_U 2¨÷¢ÜIe-#µ†GÆ”©¿òÔ¸þï«Nßuüß©$©G^ÿ¼zò·ªø\³}³ŠìUñù²ºÃwQ¡é¸ª­4Ètò) ¡ºA‹ì}#»çF6ÜÙbÛkkm+j›\]qI¶¯±S.É6·©»µ¡ÝÖØÜêjë¨uÚêÚ\ͶÙmµ«Ý›À9ñKÿÉWoüÚ{mãFnl—«mmÕÎÚæê¶U6WÝø^d¹¸¶­¹±n¦kl·5Ô¶ÕÂXõmÕ-ÀzðlÁc ±¶úÚ[‡ËVÝÒmk­mk‡\+:@b ‚j[ -CËŽ†Zœjj\ͭМ4èh€ÞAʵ-í ½p*’ð8èÌi«nowÕ4VÃx²ÓUÓÙ\ÛÒQÝAè©kl%Å’é¶…®ºŽ.x¥¤­¶µÍå쬩¥Ý8±Æµ„yÌ  æš¦N'¡¤«±£ÁÕÙÄ47º"#´©¢„n;Û¡=a'ÁÖ\K¸–©´7$x‘@ÆLvµÙÚkAкHu³?nhBtÛJÝ!«¢£u5€a]õQC]g[ XKtºlí®[{犕µ5ä +á¯ÎÕÆFªqµ8 홲\ +ÝU¯p­®¥¨VD 1‚W¨¡]½J´Ò:jê=[{CuS“¼¢Ö-5 fIõ>]-`m¶fW[í„lÛ:º[këªa $•¨±w›«»a¶ÀãÎÆºFbhÕM`zpV;”sUtd‚V·]MÕm2ÈYÛÞXßBɨWç*iK¤3·°h~¶œS´¨0kvi^Q¡íÆl`eöÙ*mÀÊœ‚ÙyólY³çÏžKØñ Bš©ìŒŠC&ÌÍ.Ì.™]`[Xœ='œ€óJ²ç”Ò– {D%wNQáÂì‹à´ó ‘ /ÎͦC³áÿJe¿Ø%ý”•”޲8oav‚mvIÞB¢‘œ’" —è³(‡ZÀ"'Q^¡›^¢#ríjë€Väi7ƒYÙ³  Ã…„ ¸ i Ö•½¦¦¶µƒØ¶{r«®‘ºQÕw&P«U˜ðܘ¸ê5z +a f:ªw Ø$'¨®—º°nˆDªëu®®ØN\‰«MvgÒÕØNg:„Àf—ólíÕM0¶¯|d +|Æb‡¯4žö +ä$xKú6ßL¦u»Up$ÓÚ™“®ê%ÑõÕV¸6vµðË¿a˜ÜÕ¸ª1¹œÕš¤Ö†Öd·Çüv_%ßZFWþÇ¡ +4Á¿~f£ãÊ“ +ÛÅ>‘Â>¾ý±}¬ÝÀ?–Â>ª°Ç¢ØG ìÑ(öGûØ#—؇/±)ì3Ùö¶çðB¾g{xþlþðBöþö>öÐ>ö^™=¨°,ìþ[Ù{~ÆîSؽÐbïÿ«íL–š +ƒ(lê»Mi†K1$" +E5*j¤45Î$¨@Äá:à8‘¨Wq*gE¬ÊÂ'¡*—>S­ù.èM÷©>}úô´Çeé{^–<¾çYü–”Eå[’¯Êå³òIùø!%•)Þ§y§¼‰ñZy¥¼T”ÊsÅqÄsy¦<ð¤R“'J¥\’JÊ‚Užw¤\¢œµæ+ª«GúrìQv+»2¤ëì²]éQ¶)ݦݽ“­aºKW;[Rlî´e³K§ã󋓦#—vÉH»²É M56þÆ$mÒÖHÛòŸ_ÙŸÖ†©5¤²Vk˜Co©’¬²>áÈz—D<* ‡x”æ˜#ÍÄÖ)MÊÚ:ÑHB¢JĨF„•FÅ6 +v•Yò‚ŒâWV›Öê* †Þ ˆ¹B2XY=æß§ì8>?¾¬µªß²Ï}ûÅ×½²±j…õÿ7ZÿÌP°endstream +endobj +311 0 obj +<< +/Ascent 759.7656 /CapHeight 759.7656 /Descent -240.2344 /Flags 68 /FontBBox [ -1015.625 -350.0977 1659.18 1067.871 ] /FontFile2 310 0 R + /FontName /AAAAAA+DejaVuSans-Oblique /ItalicAngle -11 /MissingWidth 600.0977 /StemV 87 /Type /FontDescriptor +>> +endobj +312 0 obj +<< +/BaseFont /AAAAAA+DejaVuSans-Oblique /FirstChar 0 /FontDescriptor 311 0 R /LastChar 127 /Name /F8+0 /Subtype /TrueType + /ToUnicode 309 0 R /Type /Font /Widths [ 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 317.8711 400.8789 459.9609 837.8906 636.2305 950.1953 779.7852 274.9023 + 390.1367 390.1367 500 837.8906 317.8711 360.8398 317.8711 336.9141 636.2305 636.2305 + 636.2305 636.2305 636.2305 636.2305 636.2305 636.2305 636.2305 636.2305 336.9141 336.9141 + 837.8906 837.8906 837.8906 530.7617 1000 684.082 686.0352 698.2422 770.0195 631.8359 + 575.1953 774.9023 751.9531 294.9219 294.9219 655.7617 557.1289 862.793 748.0469 787.1094 + 603.0273 787.1094 694.8242 634.7656 610.8398 731.9336 684.082 988.7695 685.0586 610.8398 + 685.0586 390.1367 336.9141 390.1367 837.8906 500 500 612.793 634.7656 549.8047 + 634.7656 615.2344 352.0508 634.7656 633.7891 277.832 277.832 579.1016 277.832 974.1211 + 633.7891 611.8164 634.7656 634.7656 411.1328 520.9961 392.0898 633.7891 591.7969 817.8711 + 591.7969 591.7969 524.9023 636.2305 336.9141 636.2305 837.8906 600.0977 ] +>> +endobj +313 0 obj +<< +/Outlines 315 0 R /PageLabels 439 0 R /PageMode /UseNone /Pages 397 0 R /Type /Catalog +>> +endobj +314 0 obj +<< +/Author () /CreationDate (D:20260121160837+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260121160837+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (Procedure Call Standard for the Arm\256 Architecture) /Trapped /False +>> +endobj +315 0 obj +<< +/Count 90 /First 316 0 R /Last 393 0 R /Type /Outlines +>> +endobj +316 0 obj +<< +/Count 8 /Dest [ 7 0 R /XYZ 57.02362 765.0236 0 ] /First 317 0 R /Last 324 0 R /Next 325 0 R /Parent 315 0 R + /Title (\376\377\0001\000\240\000\240\000\240\000P\000r\000e\000a\000m\000b\000l\000e) +>> +endobj +317 0 obj +<< +/Dest [ 7 0 R /XYZ 57.02362 716.2236 0 ] /Next 318 0 R /Parent 316 0 R /Title (\376\377\0001\000.\0001\000\240\000\240\000\240\000A\000b\000s\000t\000r\000a\000c\000t) +>> +endobj +318 0 obj +<< +/Dest [ 7 0 R /XYZ 57.02362 650.6236 0 ] /Next 319 0 R /Parent 316 0 R /Prev 317 0 R /Title (\376\377\0001\000.\0002\000\240\000\240\000\240\000K\000e\000y\000w\000o\000r\000d\000s) +>> +endobj +319 0 obj +<< +/Dest [ 7 0 R /XYZ 57.02362 595.8236 0 ] /Next 320 0 R /Parent 316 0 R /Prev 318 0 R /Title (\376\377\0001\000.\0003\000\240\000\240\000\240\000L\000a\000t\000e\000s\000t\000 \000r\000e\000l\000e\000a\000s\000e\000 \000a\000n\000d\000 \000d\000e\000f\000e\000c\000t\000s\000 \000r\000e\000p\000o\000r\000t) +>> +endobj +320 0 obj +<< +/Dest [ 13 0 R /XYZ 57.02362 765.0236 0 ] /Next 321 0 R /Parent 316 0 R /Prev 319 0 R /Title (\376\377\0001\000.\0004\000\240\000\240\000\240\000L\000i\000c\000e\000n\000c\000e) +>> +endobj +321 0 obj +<< +/Dest [ 13 0 R /XYZ 57.02362 574.6236 0 ] /Next 322 0 R /Parent 316 0 R /Prev 320 0 R /Title (\376\377\0001\000.\0005\000\240\000\240\000\240\000A\000b\000o\000u\000t\000 \000t\000h\000e\000 \000l\000i\000c\000e\000n\000s\000e) +>> +endobj +322 0 obj +<< +/Dest [ 13 0 R /XYZ 57.02362 399.8236 0 ] /Next 323 0 R /Parent 316 0 R /Prev 321 0 R /Title (\376\377\0001\000.\0006\000\240\000\240\000\240\000C\000o\000n\000t\000r\000i\000b\000u\000t\000i\000o\000n\000s) +>> +endobj +323 0 obj +<< +/Dest [ 13 0 R /XYZ 57.02362 334.2236 0 ] /Next 324 0 R /Parent 316 0 R /Prev 322 0 R /Title (\376\377\0001\000.\0007\000\240\000\240\000\240\000T\000r\000a\000d\000e\000m\000a\000r\000k\000 \000n\000o\000t\000i\000c\000e) +>> +endobj +324 0 obj +<< +/Dest [ 13 0 R /XYZ 57.02362 236.2236 0 ] /Parent 316 0 R /Prev 323 0 R /Title (\376\377\0001\000.\0008\000\240\000\240\000\240\000C\000o\000p\000y\000r\000i\000g\000h\000t) +>> +endobj +325 0 obj +<< +/Count 6 /Dest [ 158 0 R /XYZ 57.02362 765.0236 0 ] /First 326 0 R /Last 331 0 R /Next 332 0 R /Parent 315 0 R + /Prev 316 0 R /Title (\376\377\0002\000\240\000\240\000\240\000A\000b\000o\000u\000t\000 \000T\000h\000i\000s\000 \000D\000o\000c\000u\000m\000e\000n\000t) +>> +endobj +326 0 obj +<< +/Count 2 /Dest [ 158 0 R /XYZ 57.02362 716.2236 0 ] /First 327 0 R /Last 328 0 R /Next 329 0 R /Parent 325 0 R + /Title (\376\377\0002\000.\0001\000\240\000\240\000\240\000C\000h\000a\000n\000g\000e\000 \000C\000o\000n\000t\000r\000o\000l) +>> +endobj +327 0 obj +<< +/Dest [ 158 0 R /XYZ 57.02362 678.2236 0 ] /Next 328 0 R /Parent 326 0 R /Title (\376\377\0002\000.\0001\000.\0001\000\240\000\240\000\240\000C\000u\000r\000r\000e\000n\000t\000 \000S\000t\000a\000t\000u\000s\000 \000a\000n\000d\000 \000A\000n\000t\000i\000c\000i\000p\000a\000t\000e\000d\000 \000C\000h\000a\000n\000g\000e\000s) +>> +endobj +328 0 obj +<< +/Dest [ 158 0 R /XYZ 57.02362 437.4236 0 ] /Parent 326 0 R /Prev 327 0 R /Title (\376\377\0002\000.\0001\000.\0002\000\240\000\240\000\240\000C\000h\000a\000n\000g\000e\000 \000H\000i\000s\000t\000o\000r\000y) +>> +endobj +329 0 obj +<< +/Dest [ 216 0 R /XYZ 57.02362 573.8236 0 ] /Next 330 0 R /Parent 325 0 R /Prev 326 0 R /Title (\376\377\0002\000.\0002\000\240\000\240\000\240\000R\000e\000f\000e\000r\000e\000n\000c\000e\000s) +>> +endobj +330 0 obj +<< +/Dest [ 216 0 R /XYZ 57.02362 243.4236 0 ] /Next 331 0 R /Parent 325 0 R /Prev 329 0 R /Title (\376\377\0002\000.\0003\000\240\000\240\000\240\000T\000e\000r\000m\000s\000 \000a\000n\000d\000 \000A\000b\000b\000r\000e\000v\000i\000a\000t\000i\000o\000n\000s) +>> +endobj +331 0 obj +<< +/Dest [ 221 0 R /XYZ 57.02362 574.6236 0 ] /Parent 325 0 R /Prev 330 0 R /Title (\376\377\0002\000.\0004\000\240\000\240\000\240\000A\000c\000k\000n\000o\000w\000l\000e\000d\000g\000e\000m\000e\000n\000t\000s) +>> +endobj +332 0 obj +<< +/Dest [ 223 0 R /XYZ 57.02362 765.0236 0 ] /Next 333 0 R /Parent 315 0 R /Prev 325 0 R /Title (\376\377\0003\000\240\000\240\000\240\000S\000c\000o\000p\000e) +>> +endobj +333 0 obj +<< +/Count 2 /Dest [ 228 0 R /XYZ 57.02362 765.0236 0 ] /First 334 0 R /Last 335 0 R /Next 336 0 R /Parent 315 0 R + /Prev 332 0 R /Title (\376\377\0004\000\240\000\240\000\240\000I\000n\000t\000r\000o\000d\000u\000c\000t\000i\000o\000n) +>> +endobj +334 0 obj +<< +/Dest [ 228 0 R /XYZ 57.02362 688.6236 0 ] /Next 335 0 R /Parent 333 0 R /Title (\376\377\0004\000.\0001\000\240\000\240\000\240\000D\000e\000s\000i\000g\000n\000 \000G\000o\000a\000l\000s) +>> +endobj +335 0 obj +<< +/Dest [ 228 0 R /XYZ 57.02362 539.8236 0 ] /Parent 333 0 R /Prev 334 0 R /Title (\376\377\0004\000.\0002\000\240\000\240\000\240\000C\000o\000n\000f\000o\000r\000m\000a\000n\000c\000e) +>> +endobj +336 0 obj +<< +/Count 10 /Dest [ 232 0 R /XYZ 57.02362 765.0236 0 ] /First 337 0 R /Last 341 0 R /Next 347 0 R /Parent 315 0 R + /Prev 333 0 R /Title (\376\377\0005\000\240\000\240\000\240\000D\000a\000t\000a\000 \000T\000y\000p\000e\000s\000 \000a\000n\000d\000 \000A\000l\000i\000g\000n\000m\000e\000n\000t) +>> +endobj +337 0 obj +<< +/Count 2 /Dest [ 232 0 R /XYZ 57.02362 716.2236 0 ] /First 338 0 R /Last 339 0 R /Next 340 0 R /Parent 336 0 R + /Title (\376\377\0005\000.\0001\000\240\000\240\000\240\000F\000u\000n\000d\000a\000m\000e\000n\000t\000a\000l\000 \000D\000a\000t\000a\000 \000T\000y\000p\000e\000s) +>> +endobj +338 0 obj +<< +/Dest [ 232 0 R /XYZ 57.02362 213.4236 0 ] /Next 339 0 R /Parent 337 0 R /Title (\376\377\0005\000.\0001\000.\0001\000\240\000\240\000\240\000H\000a\000l\000f\000-\000p\000r\000e\000c\000i\000s\000i\000o\000n\000 \000F\000l\000o\000a\000t\000i\000n\000g\000 \000P\000o\000i\000n\000t) +>> +endobj +339 0 obj +<< +/Dest [ 234 0 R /XYZ 57.02362 723.4236 0 ] /Parent 337 0 R /Prev 338 0 R /Title (\376\377\0005\000.\0001\000.\0002\000\240\000\240\000\240\000C\000o\000n\000t\000a\000i\000n\000e\000r\000i\000z\000e\000d\000 \000V\000e\000c\000t\000o\000r\000s) +>> +endobj +340 0 obj +<< +/Dest [ 234 0 R /XYZ 57.02362 639.2236 0 ] /Next 341 0 R /Parent 336 0 R /Prev 337 0 R /Title (\376\377\0005\000.\0002\000\240\000\240\000\240\000E\000n\000d\000i\000a\000n\000n\000e\000s\000s\000 \000a\000n\000d\000 \000B\000y\000t\000e\000 \000O\000r\000d\000e\000r\000i\000n\000g) +>> +endobj +341 0 obj +<< +/Count 5 /Dest [ 237 0 R /XYZ 57.02362 765.0236 0 ] /First 342 0 R /Last 346 0 R /Parent 336 0 R /Prev 340 0 R + /Title (\376\377\0005\000.\0003\000\240\000\240\000\240\000C\000o\000m\000p\000o\000s\000i\000t\000e\000 \000T\000y\000p\000e\000s) +>> +endobj +342 0 obj +<< +/Dest [ 237 0 R /XYZ 57.02362 546.2236 0 ] /Next 343 0 R /Parent 341 0 R /Title (\376\377\0005\000.\0003\000.\0001\000\240\000\240\000\240\000A\000g\000g\000r\000e\000g\000a\000t\000e\000s) +>> +endobj +343 0 obj +<< +/Dest [ 237 0 R /XYZ 57.02362 450.8236 0 ] /Next 344 0 R /Parent 341 0 R /Prev 342 0 R /Title (\376\377\0005\000.\0003\000.\0002\000\240\000\240\000\240\000U\000n\000i\000o\000n\000s) +>> +endobj +344 0 obj +<< +/Dest [ 237 0 R /XYZ 57.02362 355.4236 0 ] /Next 345 0 R /Parent 341 0 R /Prev 343 0 R /Title (\376\377\0005\000.\0003\000.\0003\000\240\000\240\000\240\000A\000r\000r\000a\000y\000s) +>> +endobj +345 0 obj +<< +/Dest [ 237 0 R /XYZ 57.02362 260.0236 0 ] /Next 346 0 R /Parent 341 0 R /Prev 344 0 R /Title (\376\377\0005\000.\0003\000.\0004\000\240\000\240\000\240\000B\000i\000t\000-\000f\000i\000e\000l\000d\000s) +>> +endobj +346 0 obj +<< +/Dest [ 238 0 R /XYZ 57.02362 765.0236 0 ] /Parent 341 0 R /Prev 345 0 R /Title (\376\377\0005\000.\0003\000.\0005\000\240\000\240\000\240\000H\000o\000m\000o\000g\000e\000n\000e\000o\000u\000s\000 \000A\000g\000g\000r\000e\000g\000a\000t\000e\000s) +>> +endobj +347 0 obj +<< +/Count 10 /Dest [ 239 0 R /XYZ 57.02362 765.0236 0 ] /First 348 0 R /Last 363 0 R /Next 364 0 R /Parent 315 0 R + /Prev 336 0 R /Title (\376\377\0006\000\240\000\240\000\240\000T\000h\000e\000 \000B\000a\000s\000e\000 \000P\000r\000o\000c\000e\000d\000u\000r\000e\000 \000C\000a\000l\000l\000 \000S\000t\000a\000n\000d\000a\000r\000d) +>> +endobj +348 0 obj +<< +/Count 2 /Dest [ 239 0 R /XYZ 57.02362 677.8236 0 ] /First 349 0 R /Last 351 0 R /Next 353 0 R /Parent 347 0 R + /Title (\376\377\0006\000.\0001\000\240\000\240\000\240\000M\000a\000c\000h\000i\000n\000e\000 \000R\000e\000g\000i\000s\000t\000e\000r\000s) +>> +endobj +349 0 obj +<< +/Count -1 /Dest [ 239 0 R /XYZ 57.02362 601.4236 0 ] /First 350 0 R /Last 350 0 R /Next 351 0 R /Parent 348 0 R + /Title (\376\377\0006\000.\0001\000.\0001\000\240\000\240\000\240\000C\000o\000r\000e\000 \000r\000e\000g\000i\000s\000t\000e\000r\000s) +>> +endobj +350 0 obj +<< +/Dest [ 241 0 R /XYZ 57.02362 285.0236 0 ] /Parent 349 0 R /Title (\376\377\0006\000.\0001\000.\0001\000.\0001\000\240\000\240\000\240\000H\000a\000n\000d\000l\000i\000n\000g\000 \000v\000a\000l\000u\000e\000s\000 \000l\000a\000r\000g\000e\000r\000 \000t\000h\000a\000n\000 \0003\0002\000 \000b\000i\000t\000s) +>> +endobj +351 0 obj +<< +/Count -1 /Dest [ 242 0 R /XYZ 57.02362 765.0236 0 ] /First 352 0 R /Last 352 0 R /Parent 348 0 R /Prev 349 0 R + /Title (\376\377\0006\000.\0001\000.\0002\000\240\000\240\000\240\000C\000o\000-\000p\000r\000o\000c\000e\000s\000s\000o\000r\000 \000R\000e\000g\000i\000s\000t\000e\000r\000s) +>> +endobj +352 0 obj +<< +/Dest [ 242 0 R /XYZ 57.02362 596.4236 0 ] /Parent 351 0 R /Title (\376\377\0006\000.\0001\000.\0002\000.\0001\000\240\000\240\000\240\000V\000F\000P\000 \000r\000e\000g\000i\000s\000t\000e\000r\000 \000u\000s\000a\000g\000e\000 \000c\000o\000n\000v\000e\000n\000t\000i\000o\000n\000s) +>> +endobj +353 0 obj +<< +/Count 1 /Dest [ 242 0 R /XYZ 57.02362 180.8236 0 ] /First 354 0 R /Last 354 0 R /Next 359 0 R /Parent 347 0 R + /Prev 348 0 R /Title (\376\377\0006\000.\0002\000\240\000\240\000\240\000P\000r\000o\000c\000e\000s\000s\000e\000s\000,\000 \000M\000e\000m\000o\000r\000y\000 \000a\000n\000d\000 \000t\000h\000e\000 \000S\000t\000a\000c\000k) +>> +endobj +354 0 obj +<< +/Count -4 /Dest [ 243 0 R /XYZ 57.02362 525.0236 0 ] /First 355 0 R /Last 358 0 R /Parent 353 0 R /Title (\376\377\0006\000.\0002\000.\0001\000\240\000\240\000\240\000T\000h\000e\000 \000S\000t\000a\000c\000k) +>> +endobj +355 0 obj +<< +/Dest [ 243 0 R /XYZ 57.02362 379.6236 0 ] /Next 356 0 R /Parent 354 0 R /Title (\376\377\0006\000.\0002\000.\0001\000.\0001\000\240\000\240\000\240\000U\000n\000i\000v\000e\000r\000s\000a\000l\000 \000s\000t\000a\000c\000k\000 \000c\000o\000n\000s\000t\000r\000a\000i\000n\000t\000s) +>> +endobj +356 0 obj +<< +/Dest [ 244 0 R /XYZ 57.02362 765.0236 0 ] /Next 357 0 R /Parent 354 0 R /Prev 355 0 R /Title (\376\377\0006\000.\0002\000.\0001\000.\0002\000\240\000\240\000\240\000S\000t\000a\000c\000k\000 \000c\000o\000n\000s\000t\000r\000a\000i\000n\000t\000s\000 \000a\000t\000 \000a\000 \000p\000u\000b\000l\000i\000c\000 \000i\000n\000t\000e\000r\000f\000a\000c\000e) +>> +endobj +357 0 obj +<< +/Dest [ 244 0 R /XYZ 57.02362 689.4236 0 ] /Next 358 0 R /Parent 354 0 R /Prev 356 0 R /Title (\376\377\0006\000.\0002\000.\0001\000.\0003\000\240\000\240\000\240\000S\000t\000a\000c\000k\000 \000p\000r\000o\000b\000i\000n\000g) +>> +endobj +358 0 obj +<< +/Dest [ 244 0 R /XYZ 57.02362 597.4236 0 ] /Parent 354 0 R /Prev 357 0 R /Title (\376\377\0006\000.\0002\000.\0001\000.\0004\000\240\000\240\000\240\000T\000h\000e\000 \000F\000r\000a\000m\000e\000 \000P\000o\000i\000n\000t\000e\000r) +>> +endobj +359 0 obj +<< +/Count 1 /Dest [ 248 0 R /XYZ 57.02362 707.4236 0 ] /First 360 0 R /Last 360 0 R /Next 361 0 R /Parent 347 0 R + /Prev 353 0 R /Title (\376\377\0006\000.\0003\000\240\000\240\000\240\000S\000u\000b\000r\000o\000u\000t\000i\000n\000e\000 \000C\000a\000l\000l\000s) +>> +endobj +360 0 obj +<< +/Dest [ 248 0 R /XYZ 57.02362 275.8236 0 ] /Parent 359 0 R /Title (\376\377\0006\000.\0003\000.\0001\000\240\000\240\000\240\000U\000s\000e\000 \000o\000f\000 \000I\000P\000 \000b\000y\000 \000t\000h\000e\000 \000l\000i\000n\000k\000e\000r) +>> +endobj +361 0 obj +<< +/Dest [ 253 0 R /XYZ 57.02362 765.0236 0 ] /Next 362 0 R /Parent 347 0 R /Prev 359 0 R /Title (\376\377\0006\000.\0004\000\240\000\240\000\240\000R\000e\000s\000u\000l\000t\000 \000R\000e\000t\000u\000r\000n) +>> +endobj +362 0 obj +<< +/Dest [ 253 0 R /XYZ 57.02362 490.2236 0 ] /Next 363 0 R /Parent 347 0 R /Prev 361 0 R /Title (\376\377\0006\000.\0005\000\240\000\240\000\240\000P\000a\000r\000a\000m\000e\000t\000e\000r\000 \000P\000a\000s\000s\000i\000n\000g) +>> +endobj +363 0 obj +<< +/Dest [ 255 0 R /XYZ 57.02362 154.6236 0 ] /Parent 347 0 R /Prev 362 0 R /Title (\376\377\0006\000.\0006\000\240\000\240\000\240\000I\000n\000t\000e\000r\000w\000o\000r\000k\000i\000n\000g) +>> +endobj +364 0 obj +<< +/Count 10 /Dest [ 260 0 R /XYZ 57.02362 765.0236 0 ] /First 365 0 R /Last 373 0 R /Next 378 0 R /Parent 315 0 R + /Prev 347 0 R /Title (\376\377\0007\000\240\000\240\000\240\000T\000h\000e\000 \000S\000t\000a\000n\000d\000a\000r\000d\000 \000V\000a\000r\000i\000a\000n\000t\000s) +>> +endobj +365 0 obj +<< +/Count 2 /Dest [ 260 0 R /XYZ 57.02362 688.6236 0 ] /First 366 0 R /Last 367 0 R /Next 371 0 R /Parent 364 0 R + /Title (\376\377\0007\000.\0001\000\240\000\240\000\240\000V\000F\000P\000 \000a\000n\000d\000 \000S\000I\000M\000D\000 \000v\000e\000c\000t\000o\000r\000 \000R\000e\000g\000i\000s\000t\000e\000r\000 \000A\000r\000g\000u\000m\000e\000n\000t\000s) +>> +endobj +366 0 obj +<< +/Dest [ 260 0 R /XYZ 57.02362 612.2236 0 ] /Next 367 0 R /Parent 365 0 R /Title (\376\377\0007\000.\0001\000.\0001\000\240\000\240\000\240\000M\000a\000p\000p\000i\000n\000g\000 \000b\000e\000t\000w\000e\000e\000n\000 \000r\000e\000g\000i\000s\000t\000e\000r\000s\000 \000a\000n\000d\000 \000m\000e\000m\000o\000r\000y\000 \000f\000o\000r\000m\000a\000t) +>> +endobj +367 0 obj +<< +/Count -3 /Dest [ 260 0 R /XYZ 57.02362 401.6236 0 ] /First 368 0 R /Last 370 0 R /Parent 365 0 R /Prev 366 0 R + /Title (\376\377\0007\000.\0001\000.\0002\000\240\000\240\000\240\000P\000r\000o\000c\000e\000d\000u\000r\000e\000 \000C\000a\000l\000l\000i\000n\000g) +>> +endobj +368 0 obj +<< +/Dest [ 260 0 R /XYZ 57.02362 349.8236 0 ] /Next 369 0 R /Parent 367 0 R /Title (\376\377\0007\000.\0001\000.\0002\000.\0001\000\240\000\240\000\240\000V\000F\000P\000 \000c\000o\000-\000p\000r\000o\000c\000e\000s\000s\000o\000r\000 \000r\000e\000g\000i\000s\000t\000e\000r\000 \000c\000a\000n\000d\000i\000d\000a\000t\000e\000s) +>> +endobj +369 0 obj +<< +/Dest [ 263 0 R /XYZ 57.02362 765.0236 0 ] /Next 370 0 R /Parent 367 0 R /Prev 368 0 R /Title (\376\377\0007\000.\0001\000.\0002\000.\0002\000\240\000\240\000\240\000R\000e\000s\000u\000l\000t\000 \000r\000e\000t\000u\000r\000n) +>> +endobj +370 0 obj +<< +/Dest [ 263 0 R /XYZ 57.02362 688.6236 0 ] /Parent 367 0 R /Prev 369 0 R /Title (\376\377\0007\000.\0001\000.\0002\000.\0003\000\240\000\240\000\240\000P\000a\000r\000a\000m\000e\000t\000e\000r\000 \000p\000a\000s\000s\000i\000n\000g) +>> +endobj +371 0 obj +<< +/Dest [ 263 0 R /XYZ 57.02362 434.6236 0 ] /Next 372 0 R /Parent 364 0 R /Prev 365 0 R /Title (\376\377\0007\000.\0002\000\240\000\240\000\240\000A\000r\000m\000 \000A\000l\000t\000e\000r\000n\000a\000t\000i\000v\000e\000 \000F\000o\000r\000m\000a\000t\000 \000H\000a\000l\000f\000-\000p\000r\000e\000c\000i\000s\000i\000o\000n\000 \000F\000l\000o\000a\000t\000i\000n\000g\000 \000P\000o\000i\000n\000t\000 \000v\000a\000l\000u\000e\000s) +>> +endobj +372 0 obj +<< +/Dest [ 263 0 R /XYZ 57.02362 351.0236 0 ] /Next 373 0 R /Parent 364 0 R /Prev 371 0 R /Title (\376\377\0007\000.\0003\000\240\000\240\000\240\000R\000e\000a\000d\000-\000W\000r\000i\000t\000e\000 \000P\000o\000s\000i\000t\000i\000o\000n\000 \000I\000n\000d\000e\000p\000e\000n\000d\000e\000n\000c\000e\000 \000\(\000R\000W\000P\000I\000\)) +>> +endobj +373 0 obj +<< +/Count 4 /Dest [ 263 0 R /XYZ 57.02362 263.8236 0 ] /First 374 0 R /Last 377 0 R /Parent 364 0 R /Prev 372 0 R + /Title (\376\377\0007\000.\0004\000\240\000\240\000\240\000V\000a\000r\000i\000a\000n\000t\000 \000C\000o\000m\000p\000a\000t\000i\000b\000i\000l\000i\000t\000y) +>> +endobj +374 0 obj +<< +/Dest [ 263 0 R /XYZ 57.02362 165.8236 0 ] /Next 375 0 R /Parent 373 0 R /Title (\376\377\0007\000.\0004\000.\0001\000\240\000\240\000\240\000V\000F\000P\000 \000a\000n\000d\000 \000B\000a\000s\000e\000 \000S\000t\000a\000n\000d\000a\000r\000d\000 \000C\000o\000m\000p\000a\000t\000i\000b\000i\000l\000i\000t\000y) +>> +endobj +375 0 obj +<< +/Dest [ 264 0 R /XYZ 57.02362 765.0236 0 ] /Next 376 0 R /Parent 373 0 R /Prev 374 0 R /Title (\376\377\0007\000.\0004\000.\0002\000\240\000\240\000\240\000R\000W\000P\000I\000 \000a\000n\000d\000 \000B\000a\000s\000e\000 \000S\000t\000a\000n\000d\000a\000r\000d\000 \000C\000o\000m\000p\000a\000t\000i\000b\000i\000l\000i\000t\000y) +>> +endobj +376 0 obj +<< +/Dest [ 264 0 R /XYZ 57.02362 702.4236 0 ] /Next 377 0 R /Parent 373 0 R /Prev 375 0 R /Title (\376\377\0007\000.\0004\000.\0003\000\240\000\240\000\240\000V\000F\000P\000 \000a\000n\000d\000 \000R\000W\000P\000I\000 \000S\000t\000a\000n\000d\000a\000r\000d\000 \000C\000o\000m\000p\000a\000t\000i\000b\000i\000l\000i\000t\000y) +>> +endobj +377 0 obj +<< +/Dest [ 264 0 R /XYZ 57.02362 639.8236 0 ] /Parent 373 0 R /Prev 376 0 R /Title (\376\377\0007\000.\0004\000.\0004\000\240\000\240\000\240\000H\000a\000l\000f\000-\000p\000r\000e\000c\000i\000s\000i\000o\000n\000 \000F\000o\000r\000m\000a\000t\000 \000C\000o\000m\000p\000a\000t\000i\000b\000i\000l\000i\000t\000y) +>> +endobj +378 0 obj +<< +/Count 9 /Dest [ 267 0 R /XYZ 57.02362 765.0236 0 ] /First 379 0 R /Last 392 0 R /Next 393 0 R /Parent 315 0 R + /Prev 364 0 R /Title (\376\377\0008\000\240\000\240\000\240\000A\000r\000m\000 \000C\000 \000a\000n\000d\000 \000C\000+\000+\000 \000L\000a\000n\000g\000u\000a\000g\000e\000 \000M\000a\000p\000p\000i\000n\000g\000s) +>> +endobj +379 0 obj +<< +/Count 7 /Dest [ 267 0 R /XYZ 57.02362 677.8236 0 ] /First 380 0 R /Last 386 0 R /Next 392 0 R /Parent 378 0 R + /Title (\376\377\0008\000.\0001\000\240\000\240\000\240\000D\000a\000t\000a\000 \000T\000y\000p\000e\000s) +>> +endobj +380 0 obj +<< +/Dest [ 267 0 R /XYZ 57.02362 639.8236 0 ] /Next 381 0 R /Parent 379 0 R /Title (\376\377\0008\000.\0001\000.\0001\000\240\000\240\000\240\000A\000r\000i\000t\000h\000m\000e\000t\000i\000c\000 \000T\000y\000p\000e\000s) +>> +endobj +381 0 obj +<< +/Dest [ 270 0 R /XYZ 57.02362 765.0236 0 ] /Next 382 0 R /Parent 379 0 R /Prev 380 0 R /Title (\376\377\0008\000.\0001\000.\0002\000\240\000\240\000\240\000P\000o\000i\000n\000t\000e\000r\000 \000T\000y\000p\000e\000s) +>> +endobj +382 0 obj +<< +/Dest [ 270 0 R /XYZ 57.02362 580.4236 0 ] /Next 383 0 R /Parent 379 0 R /Prev 381 0 R /Title (\376\377\0008\000.\0001\000.\0003\000\240\000\240\000\240\000E\000n\000u\000m\000e\000r\000a\000t\000e\000d\000 \000T\000y\000p\000e\000s) +>> +endobj +383 0 obj +<< +/Dest [ 274 0 R /XYZ 57.02362 765.0236 0 ] /Next 384 0 R /Parent 379 0 R /Prev 382 0 R /Title (\376\377\0008\000.\0001\000.\0004\000\240\000\240\000\240\000A\000d\000d\000i\000t\000i\000o\000n\000a\000l\000 \000T\000y\000p\000e\000s) +>> +endobj +384 0 obj +<< +/Dest [ 274 0 R /XYZ 57.02362 514.8236 0 ] /Next 385 0 R /Parent 379 0 R /Prev 383 0 R /Title (\376\377\0008\000.\0001\000.\0005\000\240\000\240\000\240\000V\000o\000l\000a\000t\000i\000l\000e\000 \000D\000a\000t\000a\000 \000T\000y\000p\000e\000s) +>> +endobj +385 0 obj +<< +/Dest [ 274 0 R /XYZ 57.02362 267.4236 0 ] /Next 386 0 R /Parent 379 0 R /Prev 384 0 R /Title (\376\377\0008\000.\0001\000.\0006\000\240\000\240\000\240\000S\000t\000r\000u\000c\000t\000u\000r\000e\000,\000 \000U\000n\000i\000o\000n\000 \000a\000n\000d\000 \000C\000l\000a\000s\000s\000 \000L\000a\000y\000o\000u\000t) +>> +endobj +386 0 obj +<< +/Count -5 /Dest [ 274 0 R /XYZ 57.02362 194.0236 0 ] /First 387 0 R /Last 391 0 R /Parent 379 0 R /Prev 385 0 R + /Title (\376\377\0008\000.\0001\000.\0007\000\240\000\240\000\240\000B\000i\000t\000-\000f\000i\000e\000l\000d\000s) +>> +endobj +387 0 obj +<< +/Dest [ 276 0 R /XYZ 57.02362 567.8236 0 ] /Next 388 0 R /Parent 386 0 R /Title (\376\377\0008\000.\0001\000.\0007\000.\0001\000\240\000\240\000\240\000B\000i\000t\000-\000f\000i\000e\000l\000d\000s\000 \000n\000o\000 \000l\000a\000r\000g\000e\000r\000 \000t\000h\000a\000n\000 \000t\000h\000e\000i\000r\000 \000c\000o\000n\000t\000a\000i\000n\000e\000r) +>> +endobj +388 0 obj +<< +/Dest [ 280 0 R /XYZ 57.02362 441.8236 0 ] /Next 389 0 R /Parent 386 0 R /Prev 387 0 R /Title (\376\377\0008\000.\0001\000.\0007\000.\0002\000\240\000\240\000\240\000B\000i\000t\000-\000f\000i\000e\000l\000d\000 \000e\000x\000t\000r\000a\000c\000t\000i\000o\000n\000 \000e\000x\000p\000r\000e\000s\000s\000i\000o\000n\000s) +>> +endobj +389 0 obj +<< +/Dest [ 280 0 R /XYZ 57.02362 266.6236 0 ] /Next 390 0 R /Parent 386 0 R /Prev 388 0 R /Title (\376\377\0008\000.\0001\000.\0007\000.\0003\000\240\000\240\000\240\000O\000v\000e\000r\000-\000s\000i\000z\000e\000d\000 \000b\000i\000t\000-\000f\000i\000e\000l\000d\000s) +>> +endobj +390 0 obj +<< +/Dest [ 283 0 R /XYZ 57.02362 645.0236 0 ] /Next 391 0 R /Parent 386 0 R /Prev 389 0 R /Title (\376\377\0008\000.\0001\000.\0007\000.\0004\000\240\000\240\000\240\000C\000o\000m\000b\000i\000n\000i\000n\000g\000 \000b\000i\000t\000-\000f\000i\000e\000l\000d\000 \000a\000n\000d\000 \000n\000o\000n\000-\000b\000i\000t\000-\000f\000i\000e\000l\000d\000 \000m\000e\000m\000b\000e\000r\000s) +>> +endobj +391 0 obj +<< +/Dest [ 283 0 R /XYZ 57.02362 389.0236 0 ] /Parent 386 0 R /Prev 390 0 R /Title (\376\377\0008\000.\0001\000.\0007\000.\0005\000\240\000\240\000\240\000V\000o\000l\000a\000t\000i\000l\000e\000 \000b\000i\000t\000-\000f\000i\000e\000l\000d\000s\000 \023\000 \000p\000r\000e\000s\000e\000r\000v\000i\000n\000g\000 \000n\000u\000m\000b\000e\000r\000 \000a\000n\000d\000 \000w\000i\000d\000t\000h\000 \000o\000f\000 \000c\000o\000n\000t\000a\000i\000n\000e\000r\000 \000a\000c\000c\000e\000s\000s\000e\000s) +>> +endobj +392 0 obj +<< +/Dest [ 287 0 R /XYZ 57.02362 606.6236 0 ] /Parent 378 0 R /Prev 379 0 R /Title (\376\377\0008\000.\0002\000\240\000\240\000\240\000A\000r\000g\000u\000m\000e\000n\000t\000 \000P\000a\000s\000s\000i\000n\000g\000 \000C\000o\000n\000v\000e\000n\000t\000i\000o\000n\000s) +>> +endobj +393 0 obj +<< +/Count 3 /Dest [ 292 0 R /XYZ 57.02362 765.0236 0 ] /First 394 0 R /Last 395 0 R /Parent 315 0 R /Prev 378 0 R + /Title (\376\377\0009\000\240\000\240\000\240\000A\000P\000P\000E\000N\000D\000I\000X\000:\000 \000S\000u\000p\000p\000o\000r\000t\000 \000f\000o\000r\000 \000A\000d\000v\000a\000n\000c\000e\000d\000 \000S\000I\000M\000D\000 \000E\000x\000t\000e\000n\000s\000i\000o\000n\000s\000 \000a\000n\000d\000 \000M\000V\000E) +>> +endobj +394 0 obj +<< +/Dest [ 292 0 R /XYZ 57.02362 687.4236 0 ] /Next 395 0 R /Parent 393 0 R /Title (\376\377\0009\000.\0001\000\240\000\240\000\240\000I\000n\000t\000r\000o\000d\000u\000c\000t\000i\000o\000n) +>> +endobj +395 0 obj +<< +/Count 1 /Dest [ 292 0 R /XYZ 57.02362 589.4236 0 ] /First 396 0 R /Last 396 0 R /Parent 393 0 R /Prev 394 0 R + /Title (\376\377\0009\000.\0002\000\240\000\240\000\240\000S\000I\000M\000D\000 \000v\000e\000c\000t\000o\000r\000 \000d\000a\000t\000a\000 \000t\000y\000p\000e\000s) +>> +endobj +396 0 obj +<< +/Dest [ 294 0 R /XYZ 57.02362 516.2236 0 ] /Parent 395 0 R /Title (\376\377\0009\000.\0002\000.\0001\000\240\000\240\000\240\000C\000+\000+\000 \000M\000a\000n\000g\000l\000i\000n\000g) +>> +endobj +397 0 obj +<< +/Count 41 /Kids [ 4 0 R 7 0 R 13 0 R 97 0 R 150 0 R 158 0 R 195 0 R 216 0 R 220 0 R 221 0 R + 223 0 R 228 0 R 232 0 R 234 0 R 237 0 R 238 0 R 239 0 R 241 0 R 242 0 R 243 0 R + 244 0 R 248 0 R 253 0 R 254 0 R 255 0 R 258 0 R 260 0 R 263 0 R 264 0 R 267 0 R + 269 0 R 270 0 R 274 0 R 276 0 R 280 0 R 283 0 R 287 0 R 292 0 R 293 0 R 294 0 R + 300 0 R ] /Type /Pages +>> +endobj +398 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1126 +>> +stream +Gas1^9og5P%))O>kdX?kMF?a9\"mku"?gj2hi\t0ahApg_mV?ZVNN>HRtLGqRITkQdJTMbMV6jUROnc:(^o(]Em[PH36=XYlg"67EB*U-6fJe`]W9ZEE;.uK0Ur\^Ek,e$Y-'StLq1@bJtS@oR@k:=(@$I`bqAWf^'NOfZF_S.[>*si7?6A%Ql3"nI>:]KQ7c,,ZG[tqs*A#l!PQNc+BmOkJN@8=G[4i"],(^[^8(Nma]?0*p\pMrU/Wi2h",8.4q)mZ(6#A<`s[#P\b;L,"8!d,i#T#FQ4)5nLN2*_K_.1Qd(:]=PNS`2LDXa1'f<\l>q>uofj=kKkkrJ[q9VS3T;F*Mram#.U8YCJED.40Zqg:ZngQWFR.R2k%^r!lM5@VRO?3?jl!A`50@dd+Ks,-:j%XD(\Nas\VS^e@Oc1-r7HmnPHu8=^hHuKW9eck?#:@<1DNE$LH#;^fHojWW`s^NJ=#lK_j'-%T!@0,@aJS@0'FH`lj4mPCF63>RM@UJnqVZA'djnEP6ZImujqsK]OI_M8HquFTeI;2c]kjl\%a2/.)LDr8Uf0;9dgaB&O4+k:4/N@<=-IQ:qq$Zu/i?/_J60"*endstream +endobj +399 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 851 +>> +stream +GatU19lldX&A@7.m*TMf+OI*a,&j\^:chYPP$b"e='106c3@#dq*:NA[>fIT5riCpOOKX55@@Z?\O`;<2"@s.[QGh^I#fAl)i)'Im2bk1ir,>--(R;Oar&<[Y!r>^*N\H9qoFqB>nC^Iu9_N2gYXq*P/R%V-p1L$GPf.oj_H@Z''Q=Io3$qV%.:O;1A":Jg)aPYS_sIEk\7@`;\e?8pfbTH7GAC-(-qp\4?'lE*b+M"Z2FZd;go.6)?XD9:EWpQeJ]5(T1>?UpX-L.g%G/$hD@2a7XN&rB*][U'-Z4>#**9iDRP$6=0uV51J-$HQXOjs1$KeAeL9l#b-'=/OMbRa[U+Q'l3FC"0!nP*PZL\\D:>%a*18'J(r'E2JU5S+2m!pkQj">M'ZtXDUZ+:BM!NMRmtj6_?H)pK2Y7Q:+Lcuh1;5*)1<#Y.7-CF'(B*m;NYtMB0(=h_E7&b](M(]L?#%=LI4kDRdAq7%Rk/*S@.`m5E%aM7NY/E89@26_/A_jCaeAYY.!c_6G"u+QD7Enku2q(=kgBUJaer:2\%`\M5f+rebX4?i^NBDWPcOV2Z>]b@>L#YWF^?#80V3(abup7nWDh]iSOSPO+\]Pj;SBl\q24Rb#%5aKPm7'f%K0-".GJWVc6>WPZJ^-E6EJJAKTA43Q4E.:P6VpDV++?U2Oh*V,q5B.i[8:@"uFHrp[U@3Q:Fp9;7!a`0o2d#?-^S=h#`/Mendstream +endobj +400 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2546 +>> +stream +Gau0D>E@OK)1Bi7+St0+[Og]UWB_Pq6+.%QgSPhV/f'fj>,H.SAB?-oEW#9P5#UrB^*HJ%71`T?nT)A8*I\fEk2RPmf6kWk]*4]'`#0*<@a0/Y7;VM8hCma`pC2%2`\YpO_B`NqaPC8G?=P0DXVJ.\Nq&@rqTOk]1^RdK[/9UUUaNZKd\b-4705;E'OK[%RQCW_plXPQSCTV\U;$Z?%/u4ol,++RjYC\"a`c%ns4?g)jJ*`eLZ>`p?,3E$+FkZ)pbD-&I@j`+bJ'_%u;V;(X$=J_FJjU-eN=kCU-&GkU\NA:>*Dt^@1$l:;5R/L8IAM$m'IYD';p+U,^j@@%RrUMf>^@(MNMg?GZNH?S'/0cT/H;PZ:K^tS$s/AFi'?)%^/TGuTc;V(I6fp6W[I/ds42V1\aK'?XaHboC1[KCoXQlo.s05_dYMF4UEBr0ppGh>5`[uO\OWf8<[ppaR^29M$J?@=AH"7iqC^AV\<-&3K"c8HTU&:DsNIb$lEKW\P)`^)Pi?oL=/3>(^*CWgI-Yk:g7;4>\Wa%(_Z3tHiIEdFRV?Y<\K.efk"-119o38YIpcC).E$mhXTrihrG(NOJUu#k"^(cZqn\@qjp5LV2N4Z+G^=)],&eA`R4t'SR*11kq&0JNjq(i"aAVhP4Qt\fm#=n.TKp$CX=WEu..jKf\E.32_$m;pcjFpFBcFoMM35@.nnk-S1TEVrm17,udXg#Mhf34FY@)@.r<16d-$4kH-"270#PnGH[!$^pL1euaIJB9d]9B@@Q;qpuQ%]YfM"W:k:@B0C]ik@0JQlunu<)H;@BpLi7o=jeJLUG[%.S[&1VO"FRY1:PN',s6@iN"3@.odZ7**#=Y/!*'E9NT5-/?'=\EXE.O]4^>!$ud=6J'2[G8!OdM*S!SgQ"9lknuW^VdsVFt#!f?/0Q-(gFEr\B?;(@:U3Xf:Bu8m5/%O@3ZFW7+1=A)IY.BD"W;K%sD'U!m\G'i5Wm@'`2234[smm0<<*"n"ZF#k4S)MN",VJE(d]SA\lKqt=5b^6S&1b+MtTeh[U6@g0nHcAEpOdZf9OT4Q^,P-_rTYBPGe50ap*_7)gA_ZGnqb)aA6HW^);cmb=>CI0F";h+LX_LBuEJap=pX*iD>[[gFXY\M]YuKl3m[n,-s<5o0"joS,a:!]\1IJ>[hSj*4%#*3+N,*p;TD!>-87GL$bTUT"E`GNBpQc3#k%hK+ZZ8t28Ihi$!W*[r%J"B?j]1WlA`X:T78YfC"O)`Z3!K6G7rmPmobmQB`7.](le'P^R\-5RJ4WhS5"__KLf6L34B0i>6-*2L(6`m,8*gX!SUre@Ek^X4a^@6]'`&a!cfo@nUs__WtEB%M-U"DC.rNc+Xs6:TommW\t+.X/M_9+j`=6\.T2[Ir=tEIQ&0#3ELnF)Z:$ARuXT1O$Xhg8/BE$e^3C!p=uEF>JYarRsZ$b\Ynkcm'O#`LJ%XZ^HHA0Gac5:>a3'%s]X+iTM1L5J!lLps:LZ3gHR1kqLUV='AiE*NhfB6QKUXHg:CrPa3Gg4nNItEsI@8>.Y8q#?VNc&1ZN2JpCG4dGWHskO"P$"5cK4=5aO'VbBlGAVSpX*qoJCY_51jVSZn$P$"]DOJ2I^mqW/N7Gs,:c@O",Bpb9c&BMA]Bucj8/`Tg,7<64U](c3`L".&,A9+q(+r:4DDR##YBQp:AaJW<10Vd%"caJ`79r#0_`1k$>`_corHWo0H*Yhj'N-cW)[AqPDLqP5.gZAZFPX4M#CFH1@C5)Z1'=hP/ag*VKG,ua`.@=/*D,&QCOnsc8YE+AOYgS!nq;s_0ml\P<.K8dUL8XnU/-ErbeY')f-Ybq`?&[@3d^usBZfO<:J:A3\,Nt.d(\%(sHKnBIMt]_7h:K,+a7(+API_<"NN(6B(CDN4O@B_0#=uX?]#GCg1:lRu]uKc7dP^Oa1.]CeC"Zu'.>TkX2?ef(^-[RJp`u2E1Oh*/T0BK**1UQsa:Hsb3[tKM=j6+`Mq(gZ_@GR93;*u)#:KqlpcAIu%RUntiL&C'eFT'r#Cj>&!d(h!8+Uendstream +endobj +401 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1964 +>> +stream +GauaA?#uJp'Sc)J.s.38D,_c+!LHC4Cce-3VCgW)]tkg)2LK`QDTDY?pIM`F;EM!Oj^C9(4Z8s7O,mTf*CN^(04KreK@!VU?=":5-q]h^0[*@>KAND:s.5A/R68=e%0]0t-np?NE#nib(QsQ#0cZsq5r@0]2t."5+r3"%3+@P/!iZg$Tt^be1>U=V`:I&9432U/ImN<`./d/TquL+o=slUhA%$;=0L)W/Cp-&WqWWS-!!s9GH:0R*4Og)=9T5:(f*k)ME.&rNB[n('%NL61-)Lq1"3Zp;bRHCClhjISa02r"t)=@fcu#*=6H([bZ9sb9IkTl,RI*#t3,HBMbAlLYR:E<:P&QO#/C*F4(U.Mo2gto/&e,EK^j;+-;Ynb^,,O[QoOOb'`F3]2bb6K#Z4t-RP-e(7tR?e!R.?[^h$UfID9d#UdCn)>q2I=`@juh0M?(SOBl_d^rXKBU^?30NU[k_+"Pk%BWpj0[Zju4l"UGCUDDaL+XTS]$UN&=')(of[SYOI6#`Jm6T2#mPFPmdi$?h[ZBet?be\+4R<-f.`Dgdn5X&l/0neCarQqO'!u4JSS5]d5Z%'*VL<2s,AX1Qr>b].LigWI)c6H$@CB&Q12C\rM>cHMT5E\K2Iq"K^2Q&tnXq6@fgMp;tM]r*&eofqRB9XEGHkQ5&fJJPI$m'\4B**L(DRfreV(Wl^d2!Oe.oM$*l%Ws1duPirIilYOX=\lm/2<)Q)F\7P2r&AJ;oF<3+m+di)/!cTr'Y1INQBbX]e;>N!`>EnkI$/G];7KQNg.4X4$oP_'^H*L7&YBhm)FBs*PJ$^N5m4+IdZ.7)k8Iuj]N[Gp5FqulE<@_)B+09M^Ou_Up/hpn9nkOs7TYiaLrS&taCu2NfQ*ma\\(5W.)'o&W'u6h$*=uB%AjmC<3'%)6cpU[9lM:jt\9cIL`]FhtJI1;&ql_:GPjLiBZ;e-Z:HKPP=Aia"&d10VKc>eZ/:DElepb&d3H:,t*:afP)k)>!WDp!Ep0hnE9O.FCe)%Q>e!75(h7B4h-^H7NE5Vl=@=j3214ao6Q/5)SV&pW5W\0XW>"S?9X(u"CKd9b8%U^hVI[:/!o5]@XL7/(A^+bbrq^fL76k4X0A^q=>-&],&G`S/(6F:DlrlnL19D]`_Mjf18Ou"JOK]0_]_ha)l+'(lcR/4Nq;hE"ejW.+U%O[-REe9EAc"8S\7J8C]HeELUA(#k?#m'B>^]-$_;;7Uo^l9Q<3A)F0K.;#a@0uJKK2*h#ZP`:4R'.rKY8=3C&))O+]]AVdBN4"4b*6W]jJ/VlhX*Yn1X+M-'"tnGMp6,U:O=a[96p7tesphQXKV1:1<_j5m+:n1Do5a=eGh*LhMFoS8,;'+,tu%"S#NKO0E].h^3;'24CaSA*uZ68;8gS*06D^N8]9K_.=?lL'&N;qB6]I=*lge#D7<(mYXqTG/M8dTQ\4,&kcs@CptWdSdendstream +endobj +402 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1497 +>> +stream +Gaua@?$"^Z'Sc)J/'__;8Q8?gCjGIT8S4A^3](9o]uZ1k"nAb#=X!#"n+Xmg&;m(]PSA6.'CX44a6V.r$:ciZq4e?M-NHBCV:A4)kO[@"a9kN?]ba\NJdB!NEk5n)4HdC1o(+O(t32ct8rGa7G.�IPBh/;3sm"S"UM=t>2^CIeM^djZ%='p&e'Z-:"`o?9KR@5TFb+XERo>`N_hTL]KEOe,7jV::=S\ip4f%$su*d"I)'Q*,N?K>_OJ;p>'u]1](lc>D]JFU4S2LGO>QuF\ptN^/[E\(6tZ"ekbkNp#Y/D%b8$.=j"jCFR/R`ha*/Jl)IRQ)m.D9kmQC/>l55,?K\>.rfjIB-fB,8E/N(bB:AD__4%``sKLXb'su5k]W.%komoFgA*O0%-IK(e9[30`2214Q!kKbE;$J)jZJi@Eo2RT+!)ik1;,CXtk)b8>Sm*qFufjdlF.>6)D9;57i1Mg]nge%9KE@V=:@]9f1Y;$c`3J>Db![+0_8OfU4r5MX]HMi:D/M%R+si=[:RHE1YK'g;FJUT/!M.]81!Nmr9JG4\X=qg-qUEn@06/TPAct.`!VO^pNXcG+,&&%"rL[+HV&(U+>:`GZNhsT&(-)15ZIR5f~>endstream +endobj +403 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2792 +>> +stream +Gb!#^8UK2]'#!U4e<.k/#T=YubXn,eN:Y^YEQmL>9+tup*J"3LKaR"p:K9>S$5=X$L\YJ%5qc5Dp$-["2]8"Xa4+n/#C*_/T0=r:(2P1&No@e9'>34-ICis2:NpB;QL.hZZ9?+B:H/iRE?K&[EXX$JHa%mDs+CmkfCM.@"ie\nCumt^J7>U0Qs_p"W9Lgr!8qIY<[DR*Z@KPDW:hDaIt5dNYkaCqq1=)@b'7_%S2*"%_Cd:S>d`Qp%>1>u[l^0D#e3Tm1/Hp:3dC?Y*A#*.>.Bp+:`Z=B,O(mZWU_%R!+/B*rJqf:u33@pi`R^XF"29s_pqALhha_Oh0ts"!h1(agK3='kl88N"Y[f*\M!a0DrmFf=\DWN@]ES53`lTlh)ZD!;4;m^$"lS7soe_p_?b45D2!JWL-/>Z!pL?M:,@,l%^Ce+Y8.SmYEQU-p,/PCS;,AOPD;,tRr8TLt%"TJV0q'8hsr\:?C[5SO9O>a@@&/$+CJ'1k`V-Z-,8&b&m6n5"^A^?IU\G`^?/rg)ZNSeM5H^aZ8b=Y:1>`SeQcK`W2-HQL5Z:oNL-SI=)nLE252O1=n%PsfK;6k%!h"1ca%/L>RR]PH.DH0nNMkPbLh\)*MQ;qC5ULN!*J-/eBkX2KQmNF1O9C5[U]H>S`OPE>iZ0HAr!8/+:tkM2lJ09J"&0XB;(7Et_T+&Wa!d#>FKZqu[=8@]*6]T(q-\=YB=eJ[E`PpgCNp;k(iG$`i%UmYXnlhq?-U8s.GSIFQ"QT15)7%:C4@Vd'Z`t8`g$9r4.tF%G?Ril3Dg'%&T>2B\fOP%#*'bkHme\Q!FtG"RSW=Boop+pA,@:YN-8U;fs;NM55q7('(VUZKB&^O/Dl1.o?[oEHh?@KQlgZrHWXVu]8XsiAW^UZ24q1ST'hMGTTL\$ecR"NP0_n"WCQMW.nlGH[_NqkRCS^DHDZKHNhBCq]UKB%E_Yi;282n$$4QI+]_Dp5o3&.8\J>oot[_7G9^O1jR%V&3ARRFq`PGf.#B(/q1BNE/PJ=KR=jjA`qG#A>chZ%.1+00H(V\1H]j7=,lV&..J&\P@])J`*gtoVthO:Fj2Ap$'Ss7:cT>)Zcm\[FON>1d4!7Kj>tr9EZ%F\LBU]B(nT81[R[nAi)O#g=N.miZMVTm_EZ5j&4OiPDl1(15;12?Qf6eXr')F:cWoknhIWL0X7;_[q_i@eF:nWIZkEba13k.H'@Ypc.In)_T0aV;+irk4\]V/1`_qUfQ$S-E@9IO1fIIZT"`4-7#cd5UrmojmO_BkQ5h8S-l?MnUl$3JJU",!i&R'!67q0Q'MWY=RX3::,eT?:OR*:E`F@O&?Gi6Z<+aj7`q$u7?jB!/%!6Y4qi)&@!^_$Ms:O%Hpuk\:1h7nW>[FKin(W\p4a7H#>Of9d'i?\4DQoMI;^#E2Uk3EDjNf9kc]ZRlUJpu@B5C;Z;K)r>[J_-g=n8d]0;PKceWB20kTlGcNtg@T&&2+ju5c,_%;Cf2dt-TCh)Q^HEmiI&_aP^EnL,3LpQf,`Gj_-:m3J>1:>,tjK(\WBm._N%hS/'do+5Sf;^*!ildcS1/#.ONBR_U\#6cP*FB[)S!')n$6FXXZ5X_u%A`qT"pM0hU+Kh.\p=U?dg?,L'7/i!^AcSZ&2`[U\W5:r]B0NGZ8)eo$*t6+&:*i7(M?dPPSY@J0rMI\M\U$'j%Oc,/OjY9o9=41_7!lID`#m<;1/9uTs]ZR]"Rajc**e9'flb?_;NEMq;1J>8!KoN/95KcQ@tiN8hi'3fSZs,oVAGH=u.o+l6s%Tp'k@%5_FeUD.7%I:m8WE:iI$\>RWDi)EHOATeMD&%$(k!99aW]CA9Z8'2/oQh:-8QO_3NJAW[LV_sRr#G`_Lc6D2p7*:068o;3-"I>[H@60m)i""6NIC:_Qd\+Nb0jD9[q;FhS]-#r&O1#p)WW-uRr/E&"0GRT3Q&/LI%bp(7i6nFc_PR6?l/h5PY3"(ld^7PaH74%/$J72:pA"m`A@lP4K-DNFo4I2iNa-eK21h40RiSbaXT=gA6p.+KAqaY5ddNFc3VqcM!5^@YUP\YmR6?6@?K't82`FOJBHk68l7c>Ud2rkC]tb:baFQJa,;2L5L8jeJ^Sq76kqGJ4seu3i'%AaTC"cTZ'E`mVZ6,;?K0t7=*Yt!("$h]AC>E/V%_mK?pMBK?'?!]`CL&+9(`2g+HW_H+9E_1PbE1`2F/CW-kRR:OEV<3obV7d=\pS6t'1]EIL!L#>BYnceHEDK$K5JB@ZqFJWJUC4t,<#=Oq6"RZ/HHkAr+BmN"^N:E]d-C/f(<4;Q4P@qa>@-sg?/iRd8$]@.@7.*Geb!VD5?P#@3-%<'(:o]Yuh&,TT&.VeTE)?TXXK).H*VUi;HYMK2AN?Z!4a0g/\'p-$+5`6!M`T=RW"-^-bn.Y+#$Dor"=/&_FWs8cNZ>UFN/toKP*:eX_)N"Ql`u*i&S?b$qM6'q^T?P+aN_@4r73`toBFl-?I+,i$-G9K1_?oCn.+72(^_[ZUR]:oOiq\qKf0U`M2h_*mQ)+c"e,XH/-gTUSIUKA7B?g3PB?CZ'0B-)"]Ba]j4GfPsiRk@#^XN=jlEo#%eQ+8&"!HZU]V-3aRU6"uu9]\5qbs3dK>5J;'/QnMEQH$(4\R&C.@2[%(hdsbrAYD?jNogBR$`&sT7C/W2a!S_!jfn?D.0pe5DuVEh*D+:0T=)`+XT#'F09VXY;*#,LVn22-g%e$>P/1\3F46Smeh0JLTWC^kE5>D:1*M)NF0+G5O[tpAa\s_)]X#Ci[OJ_q2M")E\EZK1c;NR+r]D\ofVA;ORf+ga82GL[qG+f?\a),P`2_mTZ0#lYU(\:gXisO28l:^mhJ[CPWsssP4@8Z_sHSBKX?l<%B<&a>CZ,0+2luEWtr?&\*h$pK(%='ja>ish0rM85Q3[?:mrNr),4g]UY]W/V+TWQ`R[;9WfAtg5dK)4o7gr/%'+JGR!OMO'#7Tdri5(Z[4/O7d1rt*n8LG^a9mL8fE!-]L]7id,!/kZS>Ye<2%K05?lsMDV/U\qU2D,WFuq]PW!*hM/-dlgtV-g0lQB0eck+u/H!JG+qTDRn@Fp@-mfPWX."fa3<0?+Y2=hMZgVU:FSOmd#Qs+Xj_/Xg&9ru]3a*V]Rp_2[t1?7E.L0k)u>hs$7*+ZpkP*$>sErm_p>;*?&Z(6VRajV7#_86#ad.IsWM7]hCsK7o[7(sT\2E:^@TW%OR'D?&]_H.a#Z`gW7U5^?MAG@iK5b0/L`a0ls90AB]KgZED'e7QXfENK;<2P\fe.,W'[lJDq\)/HW+aQh%pDQW7HNK&X+"iK.Ka(rXEARe`f&?kPL18(+k1:B"XRJEs(H!EL.fp7&WF@q<^?*W8F:s%lkpCl?IHQAP%lPCTn6L~>endstream +endobj +405 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2775 +>> +stream +Gb!;fl]\&6'*$g<7=K?%'W[,cI'`5)\\@:`l\E(g(XWg2Lh0f&#TdAnG@VbH[EDRVbl9>On3\mM8W0@+=O75drSMp7nG9mN0Jb[mWYuTc6Gsfbu_X?O=5727Wmj2Rl>A/cPmYhdQ5B<:jL6=X\(H//tF+*R/Vlaa06;b"J4XU6_K5`&-1AKf^0R?V=*p?MJmr'i@b=]-uSKoaL]t)g&0AQHH#R(j.Fi!<+Y5_>e@]3mK2&0Z(oEdD9gLY'!t3q__#Gc^cQ2L1\:RfhpB?X3`,kU*4l@l5jo`ID2C[%$m!iJs&AD1?Y$T5h-k1AQ7e6(u,IjLOPOl>.,K"KB[K"q6/3+A7T?q"/aaW`6B9"]BV?m\#4Pqc9;*WFI*DZjG"P/=N&4=?dJ7J_YmG%@3S(8c&^=%9Ofg7.cR5[*4G8mMnK:$Hgr0h7]GSONZiZ++u^L()E1:!H&or9.d[]gHO#]1$3p3ZDa28[]r/o3ZTZbY"-g+OVcgSlEgf]\YmBDfeuV;K:r>?)'%fL][P/$#==i@gFlfGB;HRuN_7GS7nDLeA4.+kf9YlXZZc&J*0^%@aEK%+3hK**1?6Gh,^mj;$&t4j&r>.Rjp9PO)UiWrplkFt@NoAntD4Lq8"Zf[l-@l3U.j2#!r%J?Ar=!P5Gah>s7;$Mh<8JVjFin0O-,&Y(]@O+P.7W_^;SY1ZDm:hkrT.WIpF^_7D`e;Y;,-$aOF%j>4u9YWk)]uo]25F42SfokEB+aGX?ZZJ49W#I0?b=)tlDPY4piqPO$P;nk?;O=u.%SG*9bo)`_Os=b)M5$l-K6aXrRD-GpE>X!&r;(Be-lbTq(^.\)+rn$a%^Z$IDC3fiN56.Um9M9mSK48tQqZqFO#lUb\q4;8Z.HfHbf[QJ4t>a-3:clSMf,&2=?\mFfL'rS6Ij@:bU;qlW/i=2^Ci!HbXgSk,87CsMp?FbpY5!LGO00Eicr1)OD.jGkbpC^N2!./9C9pCalQ55[4*0mVq0?L:dD1CHGe4idRsr?#<>PN^9h%]eGHJjo^8l_6-T>JK[[,3U!4cuHL%XU[koWY$9iT\Gf/sXW'Z2V&.;[bF"Z%:U>j>T,'iH)O*P`kDR\*S`E7m6QL;*D()$,B&82F8%[9m2U0eMO)`Sfma]aQ.$g$S]D,G$TAr5(h*R4#dIZrr3s.t2](2SH4Qp1BRJV%rQX_Yn;t]-LY\AJ_GH&VlsQ-RDNooCX[Hol*DB,3)uF=YWK'0J)oXK7F$aI0lpe6r<#:OAa`VcTYA7+0=5V>p,8Ii6\P)H=lejQ+k)dfHP+q_LVT:ju15F"mbtMb77OjVQ%uBonNK^%+1npQAA)HKi&u5WFN5t60bI$kdb[6$i[`8>R(uf5[s)'Bp9mC\4qFM&l]s=RW#!oWee"Mt:2*U^d*I&3s&XU&!hXsqEg9,&R`28^nN^kNA&c$b"h!h2n,Ht?$^'VQ>X_bGsjkD6[`Z^3U4I3o'%@?h%@^!r/l9Rl`?-^>(&P&N'3t8?t"6FIXH5#daoW"K$?e_!1K3Jq*hi@H2FaAHEeAG7Ai%WA<3"D8HP6&Sj/GO=_*(LH""Z=fd74`MntU;:Z_JrZis;N>jr3C9:,qPY_f-of#MYZ`b#?f(!Msf^go"4pX8QfrN7#94XUJcO<_79.,/VIF9ZR6ZCl-Tli,rK^k;.i"W?DT+/GnR%5cj]M~>endstream +endobj +406 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2192 +>> +stream +Gb!;f968iG&AJ$Ci97N\9n;6(2q^ndT(0X:1qggf5U7V[+[H83S`nsT(SkBGBT"X0EQ/Yq+i3!RIf&mU1*nRN:jYnD67sYHiWCKkOi`R/OY\2F)tJGup?des/,dh7R27OiHEK&U:[fDfG3rcs5?gd"XT[4f%sR8-'r+Mh-D55Ha1cBPid2^-"XIgrkE2H>Mqc]V=oADE4O9">OA*B@Hm_86%\n%%7ek^KU&]XtXrg)?$(/R.F@g<9BNCapKcjl*^qXEB)3L:Y$1dm4RJ]PpQdRSW$2Dl(]U7(HKq)N+hMs7m4]\b#f67.)5Z%;_-&3ft5gACJR?`YO=*ZahV")S["D>=0R.He`Wjl6G6N>T\p#V;O3,@=Mj@B"@B!3RZT31heZg7$mmZ\Hg.H.KEp[L(Hm4LpY2^n5;'OUM2AsYgE4&LM,I1O`qZ>5*>h,2n0CCf*D@S%D0RjtuN\#.*.f%!ZS<0M>l6noEj$4lRNF]f!oCeYP6kA\VUi8TCh7-%#J6+#T+c/cN?oi1J[42R?-4A33+8uZ/mV6+D/^jbPTcJ,Bq8"_l:4$)8(U>2b(&Lj$S.fb?P6K`ETG&\u>*7eU/)B\-C8p@ZA/oGNE/GIM#1L15M/A-Ja!Gqdg1*0mhBoZIBb4NaX85YnV/>L*U=[#%W#C;9B@:i:g(cp=W%+b0"6mJF@V<)DE/+&(TV'H/f)t+5.!A+a(f/1_VYIehT*+gmWcG7eJP#?ZOX1Bc8/bLj^cs-5$=X^9H7"kChR_WWJ:g0d(/(T*5WhVM1bi6b*=sNi3D7%E-rL:EU=S,k[:E*fDcq[m#WDlCAPsII,o@BDp[c&qNl&'uj"dO'#2efUF_J5e'"c$V#2)D-F-W..lr65=0_WYu1JU*f8thBCUL*m8iM;;7'SQ1e>#Ogeh%1te+Di'(`sHS>=dNZ-r>)AT;*?*jkaO2&isiR:gZoCtZ@n:MapiXSGpPLrg5C;09-5WtF5gb$HP(VJ*5BJ?r+&"s@BVL/]_l/-A7P__=6KaRI7'rtKrAo1!J/SR1?_bHH)[AQ+W`0'X_Q=K`Fkql^H!].h$/8tE)H:J#Nkg9n-e9@8b[Im5EHf[<1*gL'_'nM$D;Z,&.+P?c`Gq#be.-0?)P-`(ZA=d1q:7UHlbqdh%#ARk.:WkZp6m8J6h3bOPuTcQF#sqY-f[TGe_)*b:,UBsq0H-adf2Y*0L]3l09fNn5LM#CL5&om+4C:L6ei7]a5c&b,Xl'5@-cJ2_WR07FDgAm\@q9>)s]J<^fd%_G7U!%nTRQbnibb12qpCo:08kF4E8fMA1]/5p'jK7.:.Pl^.Mq3qDiJ>H-N'Kb'/#k:8-PpI,25%L[X(ft"ipYnA>rbZ`-!Q<"uPM0jF)t8-ZBqW^JcFrNJ\fm9!WA;RGgB0%))W9-QlA'u%7dflk:j4M"dR$Z\qTqiN*GhD>KGLt`_EER~>endstream +endobj +407 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1275 +>> +stream +Gb!#Z?#SIU'Re<2\<$&iZQf),]qn.&"DEO4(##"eck9o0V*pZGS7KB`QaDu[FYc!["r%,PRNIlOpDi%:.7EsYcrM1&RhB'-8qA`G)k[L/TR7:=b)FBk6r,[tc5+AX2E?k<"jF;1Usp0t<="Uf/%O/)@+-q[&8-(\0:1q1^>0Q;\!]$@#On2JUL%"Ijs/W0(K,?*r:M@iJ0,/N;WnTabK"RC#U6jeu_E=#*NV00Yj?ptN"6`X2r]Ed-MBI@Q:SU`k)K7J8%ZJP_=MQ:Jgge$>>c!mmZ=6b5@G,X-4;@D7ckd>a@je)N9O[lY>3>t+t.I),2dBKpmrs"#JLKE'8@R!2)V5"`QZ:tZ?7AjoM@5<1Z,7ta$`GK.:e?A*M<+&%fKAP&CS7XNg78r_oF"5O)<_EF#"lc`0/@]aTU"@h!1F^XUbFKeQ`5ZNoh1mY8H_k8rA"#J@s6=WXJ_#M,N?'@5&$75QS"EoC4:":HVPoGZ)E93b@]gZpq"\6WbY3%>bChH/<45$AHFUt4MF=ldgk&.#2JS(sB`4PI8!rE*XAU/UoRU8T[ljPbJ\E-5;+s^e=:&b_YAL0EHRR8Ik^865&kt!X)g]m;fm("+V=m3O1a.L!t2G3l\>$o0r2>oOfH=ZN#8K]:U8PqSg805qrY+9T&+ETiZA-lbK!0-GSC!7fN$OSCgiSGM'Q'9ZTq+K,4WkXs6pNk\QK&WQ.*XQd5@XXdi'=J>%QkF=/[qqrkd"CYL!mTtNJI.VOT&C?f;jQbe"1$;2MLKPQ-YlW0B+(Ak[eI#j0J9DD6:%2ReLbZI@/ZGPk`\`s\%QZLE#]^I`q+dZ#_41sTR#]5-HKB>BkD#V6IR<-;d7*BBISSFf'lb7MMI[8P&(9g5)plVl]/o`_:0WsPr:E`Mi&k:;^+<%!dbt54dd`2s5e[p/Q\#k:P-t4%^U3PqWFG7unG:'OHGJ.HfKZ5iTceJRendstream +endobj +408 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2137 +>> +stream +Gb"/(968iG&AI=/lqqY2L>\Q73LXP,1S1RECO5OT+s@7M#q-p08,;g`J.IPQ-!=t9Bftr_1:\MCqdF]`>nCuF6gQZ\!C"ff_^>N(KD?-NhukaKL#;%]^:khoA/ID2k=6T3IcPbK$ch49*(]pM"\o-j'"l/1hr5(mN0;8?_2/7\iX"$@Vt&WUi!!_e:2o1p^jROgljDm)_>G**ob[Ob9HU+9PP!.^l%.C0#00=`LR3R-rhSA@^Y&bGRTo^@S"%SSJo^NZ5JR+m/NPWG.0i%P<2[kQ?_@H7?S8^[R79@31Ln;WU.)o;$P[qgI*W@qJ#OK!1]P0;pkif=70QR30W`)_)=TH$E3CC3)^SRCQ;D;e58Wh%k1TT"q?>Q8DN0n/Aj#t1ZRRW3m,f^\_qm)H9anM?$KZ&5M.AfhP'Pfm@FBr5E@oln[^/PLIa`W^tH$/Rr5j1=6N[B9`-+(.PGD&dL'0WqGlY]Bg8?gL!0i2/SmCq(2*M9K-C"OZg'Rk<@%*4EkpYL"iAB+1D0pnX`i4aR$_Z]Sm_c8d9?NYPIMGo'Q>VX&g*mA%Ib_20r/U:s9U)F_M((AZ]"[o#a4W-f#`&qflVkk*.f;'tL5EV('$&#;XQEo54B/3!._nVh-=2H4HSl`cgHHRh0HSL5W=Ak9PA`D?*ScutbIEM]Zbb&X4RD_f-Y.#7W9^$!DBXoSE'UT2])#nWbW)kJpdo0)N\CS=2A?fb"UV0[XRuKh-AVO6$W\B_RI%`72Z0L<5*8lG@G]j?UE^?,B-]TGupii[^\X0fUC9g-h&K+#Hfu@_kJ3qnnNQi1To&V[H!X7l/:4K(!+PHg4L69'c4DH(P'lQVkg3OV!R<[^`MNS;5^jKG&VXt-cGTohR&dZnnkpmO<5H#7&gYge.83fMn41#U(Zp]AI5n;$H[KcEK7!_eI(0a^8X#.0VISXG9NlCrTkk2XdoVG#L8?CUaa56?%[k"N^tY)aof%rKJ-\IZV._$&!(k=#DSL!4H"WhYS6Ss,#k$(kI@fF=9W+4U9c`mBITHn<1s7R%W,^9Q[mqQ186@nGP`DcAmH9"b_6XDl72K#cV64;h^s)jThHV_\eCa%D=T9_KcsZ@4/:',lVT(Q)4+nBA1LZ)Zf!Z9GaJ"Nu3Rc[D/haL>42\:rBCL\Qr$C)MVN`\*;'L;lTF'T!JmJW>OZY*A7#WPbl"2R0gg/D]h`-`!-!PN?j-f9s;B8FUE5)MGu(p\Q1JhmDEXBf54lW:&Qn2c9"c/,f+&Sp4(.39jaTe>?P`7*N>oKlo..(Ne]b^g?j"^gm5\AX_^.-e*PJ*\\$.2f*bro*5?6?f;;jg4>YiGjV?%m3Ln!J::.D,_#6iMV<9+Q)#1q@XRrF457J%nM=Q7AjO-N^Wj9TtAth%)9pSs=0ru?!\H1`bje:3gS"t=i:FiaX8kb!&oMBs#3$.U6$:,[jEHhAdeEL1+HR\E7<[">h9JFm9Qc8irHsh;*JVm!F`9@4P(oY3`%Dg7WAQ\3bB*ca[k:8htMBI7oMP)S3n1p1Y%2?4keMDP2O\kmk.aCZ@J)Md49>A<8&C3UF>1oSu!IhH--;KoZmRTMZZ)8aHR/,(!.]RG;WGZ&!,UTRgA"T:0P?NL//m,bD](p6qALkMei^R_k%6iKfhcYgm=f@'EujH%D*M)4lm?]K(&tQL25MpdcA]$;K51Z58CImendstream +endobj +409 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1921 +>> +stream +Gb"/(9lo&I&A@7.i97N`"+]:B3H!=9N'!4se=/**"^+CkT`C;lp"/pi8C=06%nk.jdpuudZj6_kI;a>e;M&o$KKJb4k"[GNhJPhoh:3d51be:S#'!=EL0*Khf@Dc00VBb*!8ogc:]0gScEH=/$&+hp1Kp2p&u4b^6b?">0Epo5i'oC4U`nP7^8iim0*[tEGs3l"8qkoh%FpugC:Z;im(g)/=Ib@o$?k.=J(J$8&Uk\:7p<]*cH3c)@h8Z#jIssa0([l/f0Ea/0<-(j^_nk$k0"p!*?f4e0gt\?64)R:=F=e[9'd]QJY3Thd"HI8`+;A%;@phP=[U/P2M^0WT\aeC&^E6=g;`3#="Rs'10rpsF;(rP,jDJ8%W=+9Zmk\aQRH]=&Om2)T4KB=L,"T"d"\al1;P]6ik[.+V*m)c#@82i:cR62hPG`HNel&YA*D[h3ii*Pra^1O$-<@VlV,N1XP0HLdHjYhFmcG$C?tY%maj?\RSDFAf'ioqb?(2pC?S:61YharBI5,Nt2fls$LUO6UG%#fh7[S?8CaAr%fj"00?QsafNe"YHn;D4"1u4Vh@ldXTU4GL*3WguBjXg[]AK)&0A:8h_ZuEqQ)R^-p<$5SB8\G)!)u$,cJaRnJ@V2KMfB@;g'Sc$H(F:l;i2#_aa"N?a6Ykj#M7g>t"eRUq'";muo6Ko&^rc23]Lf&)-i6F5$:b'[ERq2'T'mGWKMdtR7NA#DL7Q!P;"5DTe4,OHcu'is>I;[)b@b_U=>W&@'OCf1c];U<50\XK7pD+P(@U!kdG+e9Z0UG;NXAAdVMmI5btBC4?&.GER9WOAS0kNW7s[Ve'W9$ZE?>D%mYjdf=`ieL$]be=+Pruo5mfOfFF.:dsW-]-JO$6aag$jT#K8qH\BMluBb)H)(A0G%8?aglmma)H]!8uG"eA*ApZ*@8`p)-sgd=gt1r]$A^FP1^7)*:5!)Yq)UM2gPF=K&fJ!+q@e//tcl`p$a$Y,"'ZmIQDEUHhB&'-&@'~>endstream +endobj +410 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2410 +>> +stream +GauHN>Beg[&q801R($),((,Td^,^)DRlsM)D:H;LqZ)P9`#afcP*EBBYMT@(hIKK8&k'csCdQ&850OE>^]4ckril6?^'8g@K-0P"0FkDm0UK:'H[tjs2ueqQFl[K[eQh[e@)4?rI`!$lM$$[a@g#fH[;J\)8f&Y+(Gn?]%rb[*35"HgZBRjQ

>5eSj>j<77;M6E9ZLN/2TB"L44W$Y".At#hI/FkXBG=f5r8j&p_b#Q/+Ti0e?.*%3cM<9g'BEC/_#sH/"P"nTmVp$p^bHtO=W8E:`:f$uN*/M_,0m&e*_%F7DK(1SV_7L%*a1GZVn>'t*Ne5J3pLF:eo1%ZP8"K>O'S-!a-h=j>dl7PC(UC\;1Hb]FH+cVaWS/H68O7]]ae9dHPYT.)OU4JE1LFf7alPE)B.0V[#&M#"P=S.QYt\:$-2iWGGh<0JpA-<@VB0;.fm*@AdE.rfO)A/e1>\mVFL_e?OP3ck-Bo9HG[i!BeHD2>IJ_SXQh#7Y4fBEU&7]VC11&^Gu>u7B&I$skQ^YGeGu'j5nEIPeje1LRH%dL8uT!$JV:-Q=?ruM6fI=,fA:I&3VSQkkfEbn50uD>^YaP/Z(Mcm=:oR'jNEUe0p1HOrFlVnK3eMDf7eR%SVYA>f/;gSl-!pu)=1*I7'CoG#ZPb'2Cpa#WM;_cF#O4FA`n-Acnj=.GHNnIWV,[4=ig;mC[pfUaLInaN8LYm]ulNtn^GVm6?t04*Rq"+AaUPm/15S#H!OnE(.$\#m%ED,Nd3&mdBZ^P>0B'M5]9b:8p\U[;n:_ioc@^:Z-`G["(;]aS`OL#\)rZ9r8U`l3RMRsr0o7:ZjfcY1I2r)1;s.B0U`*`Q7L3TrOL[i*>DlpY3GhnIu(M$ttPX3*S"!cD&Fn*H(+'[^aT`k+/NpQ)2)\/ZgjH+p>h/FcrIgnC%L-mDN%lAR8d2Y(*U`0f>(p4Y[S90CL6>-&q.dHP#!DY?O(u9-Z`Ol;?\tOZ]h7a)@3dBJ)aIp0%dXs!p-DV?>5;cr"Ah*/rGAQ;7Htb>A9UKtYY_K42rCjJh76GO@Lm/\'F@WXDu&Q)sR'R$/>J=?q6-M_:PB)"H!\bSe$]p$[m_p`]n;^Ii*q+O1IB4[EK8a'!8n8h+'ID@9.H+uF@1Q#PEqgnDQ%0qQO=cV(V9hp@SY=H?g^h?^VbZ2?K$_>o]dn%te5i*:]&GQt4,@6!Z,WW+mTc0]P>i5ji&\O-\K'3YN(%+c.=TD;RlY@-cqEX:VQ-.4CA]_#8XcUN.)D"b5ZPt]dNYRi$#sOS#u/nacJlCDP+H]!LL=SL#%iEFrs-oJejf~>endstream +endobj +411 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 3422 +>> +stream +Gb"/+>BAQ/'n5n\^nZ,*`o9atDb2;?UpE/;1nMEK!g0B^3b[`.`mS]Q2:ulZ@!=N"cDjfD'$m@-qt4#rl642/A83#*c8iSJ5*E*-/1;gO3;qkDl^TsPN@QHZsTt1h`a)Ok,V"fhLHbHMTWgh2Gsn/*m@0`5!Cb00T0;[qsoBaZXpFG`OYNX,)lqnHY*X77rNO,X$2u^?9gD-JYEqpS`F'V"slW'hJ3n$fp^B`5_/Qc:\YJ*/a1&H%_+VR5F0"T$BtO/R^\]0p_IJAMXTJ?Dr++E8W`mH@[[^K'b'F@*LDW:W)27!If8SNPkgZuhud0ZS)>8g!cfbV\U$VT["XEZ'JX-q\c-n)_-C8OdjRUpV2[Z?>]Qp9EVWG;aLVe1k+j3B4!agOZ&cAYP31IR*Y^8D3Tk$6.1?O7iT^pnhK!Uc0EUsXZ_edpXor2bhEklF151Z56W2?#T(hQq@J$2;6p#^1_W*RCNX*l.UkRt6]i';GV3'7^!(O5q2cUcs*`HGQ+#!q"3VB/1\CJkB72NA/+Mn815QZ[@%&fL!E6XsquV"g3AZ@;GdNF!-=jH*f#n\:Q.36f"9`-eSja:S&TV4*@.O^cL`rB;Y/XJ.P[F4_1(p=F_l?+LOWV'L_a8C+&dWCnP/k?Y^>`JVg?Q,_A`#ei[Km.4K0[cNho'l;fXQ-Ua[j1+&C3MGFrjDO,1aYYlsL%q';Te]5:f57CTpn<$8>UU;=soK'9g$i5R]C)_@FNcfk[K1mP)S*AJd?BBJr_$_VQ"EHjE63`&:!(4/1`J-?OQ/kc:Zl?k*=-`1"Y^^00,7,-uC/Y0#!/-#RR1&Y+[JKE-otYP!Le>W)j6=ac!$-CZLX_\eq[t7ad_%%>%ZqHCLeLGK019YuA;FdC\)0`"m?b1u^Z;!b;L8$q&gn(7]IFC$&_/d-+]d_<[k7!$W,7P'&YjdONFZrGOa[+@i6NU@B7D.GZ4P9]i`Z#lB_XUN5"=or#";>'M6LcWONLrg4_`Zg2:O+/8CLS2k&aniV#[qg$80qu2&YWeXph,$6/Bp;&AVi*@99S1*&&(TC^.*bCu*LT;G(3L\iEpK#NH=OgeKs7WWo25A0)R-9&$Mukn;Rlm7CcMQqsKp:^Lm7MO":UFdX'n"uI8D.l[&KLYW83gYj^O%(M,dC\L;P0B>KN]Z_JFlq4,qWOJhdPQp^gG@4mbib[AR9>CtGVVq:gqr,lTU(bA`tU3/'b9F_Js9t?"c4NKu`OT`^Tcc*PI8hAq>D7C![8T^r?<`*A,o4FL3uC8iiEm`+D_6o8.]eIRGCN*f7su84;U(Ltj?iq:%Oi]IJr9U:[(cQ;Sc\ei$$Q8;2Kh]PkRUWH]/&p,Bd(Ta0TBt[Fs&]ZtZ+W9l4Aa4k/ke&O3KfK"I6s%h#smhGJT=XLE[^cTUP8-P$Jg?bb(Zs$;]._4IAG7ooO-tpL\bT+UOK9+<+ZZ]+%nc',M&4S8RJ/scF7O_7QV.#+%nW#52XP0FDhUmO.XM[Om$;SAp2,`Z9HB75rE:KA+k0FYnSuM2a436mFFE@[0ofu<^$+Y?-d$qo4U9n:,)j$PY/Vebl7\Xo7aGoV3pG?R&Q1%p6hCZG%#!YQsH's.:sB.7Z,lQR?:s%@:l=&?'lKYk':Ic;Tpc;N"iT+1F'm*_TYP*].h]3b`)1nh7CY#G0OA)sIsbZqk[bc_`P/&hEY1&oj'bYT?YrlbfE8hgjSQsDJu)aN/%*FW^N'G#N?!Y^Y+>2+JfkuTH5dCs;1++-a92];u,>JleCeGO$o=f.HHYL0suGK?!@6]&OERfeT\IUbR\p+P7]dqe;\mke1\ojYqH^A0'sU9U=C_bPGjFul>PmgnFi\RB)U=<&=#T6H!J;`2s7k:[g_P@:3l'6"^V'7fdUD1~>endstream +endobj +412 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2109 +>> +stream +Gb"/(bAu>q']&X:m]K/l1B9u8dB"=s?DWS(W&no?&X&776NK`ilIYb3'Icllol;gOA29a=/`RJR^c=CK5mdaEW;E+I!$B5MmZ@Q^0V/:S]E#qniNMdrQQ*NEjH2s"+#8Y(jtD)lL[d[ac/li )?0$$\Rk6Pb4h"dDJ"kW(93hP)"\Aj`h-];]63$FrZ#s1-=1ZD$-Kp-qVd,o'(?%D00kBW.ZqQ>U(Y`ETj!6Z"HG-'O_-?P@Abi[QtN]f)e(`a_FIV6!]7(r&sg,!b-].nV]s;,PKu#Ui-sj6ENjtM"Au8IT0LU]Cqi'u.h)c9d_SB-Yb'aZX<[/[fH03B\FZ1[L6:3kh]@.TYnn`D+JCrpoH5nbKLG4Qn+u0n#Jp>`O$eh2bH>I66ablp0?_nWJtC]-<(=`?%J,FC*IO<$68\JP:_oN]A6$bZKeiKRf_YTg=]lTuH_h4RYr85h+b^-W=i(aU1rLCM+29@&+HGk,`e/QPAmh/0,Mi*l2r4_rndTbt%0G[s%bqGS3jcXV&i#Y-<@Ag4)U6ZM>&0n)jngBF.SSjn>blgb9?_%tctO[r0h%9VON:G>'=FgEHWs`*!Y6f;-eaWDn\ResQE-ae]%3jPjO3f=A@j@=d_dV^B%)"J^('43[)NJ_Ea:ith1a@brFhrX[3EKS[2sqEW;kpu`(Z1uUqm=:B9cY4Z:/X5jZ9pR;pT&jWnb?]=HlqqG+p!oF(o3d@9?eLG*u@F8/0NeWDcg@S+nBd#Lt\XE<.^H3#o+T!e7gWr)u_2:l51V-.Nk_KX2lm`KSq2eB_6B<6GHMiA!&_o-=++A&opiq]TQb2\PD48I/Sj$VU!j`D64)4f`Y\DVj$QGJ7gfRFcK$9Np9hW:SehiEZKu:$3!OOC)a$Tq:ps;+'=6N,(](&'dG;CPOoF1,[;(tt5ESaO/,B6!$lX!bsX5;ZKZW1sapoTbGcQSgUSttXSV$NM>:uWkY#2b&R#hsrTVj^$qKq'tQgI?<_)gls7lD@!`H_*)1j_]Wgo6RGZ9M?aSo,_[JAm+q=e'b[VYM?Nm2[aq)h[to)&dHV7gB"@sGkWSl)m)h%gq#`dQW)M3>s%DhA#YsLBskqn?+M%M75h8(;7TqWLTb%B#B:=D33^+,gAn>D7DCq3]1@^NWA.OZpQ]sS`[X464:L'**EEqgHOj&Yl+D8ZP*r)Ci5L/^W$?In)/[qUMEKJM;:%Eb9Kob-G,J2Iocb&">@?Ai2["^K<@r%]GFOo^WTtpM6p`Gk\m/u]8)'VA?QUVpE(.t!a@LSmLSK3e_ZMNY_Sb-;n?M?lrf(^O"Qn2T7u+\Gh7]]AgJ*ag'Zj?@J72%`a"\SE!gImPTPlsZ;1N+uKh(HJJ-ei0i)pP9oM@/A>0Riu@":Rd-,dR+TDT0`')O%fmZY9/fR>T-K.OY=$cto(.spA/0J:[>*QTei`e/WN%;t`3;&O!F0oBW(cq%Z7Wh5F.Rq#kTa4*n2`r\4""5@qujT~>endstream +endobj +413 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1032 +>> +stream +Gat=)9lldX&A@7.lnN>t"j%lpH?.GEHK_1]*.*f6@!26`,a$UTp"bn<'9[TDGF/sJX`iUiHRj)lnDRe,4+Ne%Y4"Ai!C[G[Ahb"Wi3'S7\DWQ^P-RV0V1:CZ_ADXXA_b@f^'%*%k^Y%f3s(_h9SsNd)KVKBYRLdKT?oJ5es#'(_Nm.dpci)]`o1bDA>4/M*k>Q$MplqLO-2/RBRRD$])7Gf'!H?mBqY*,.;5FN1Le6@&lFBpD#e.^VPV,pe/cS.8NjlK0&RUaIR>-oF6pKGpq-WCOTdmO1OtNOUku1A\?PCeAWF98n=DPE(QnpFLTiP&J_X00q$';(85sdkJRumN3`N_W-e)%^J0cRoV_kfX[Dq4lT)0c@A#AuNH4;IK6ma%Ijq+1!U>qZUs+**#6[T:=SgDdh,]T_>3Ur'N*".T#Q+BG^MSi+*GPob9HFrji`$P(:a,AKR#D1~>endstream +endobj +414 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2376 +>> +stream +GauHN968iG&AI=/lqp>+L&g+$a4#.YEG$7qduuEr"\D8[:d@XQqs1io78U?ZR`Squ*`F8+M>cr2K32"S>l5iCR-"C@(@^iM!CC5"#3%^./FMKk`Od5\a@S1D*1(spKuaWBMVmna']XncbiLo@/97DgqRjiL-OAao7c6Zu+]/McK4a=c/l5mmC)(A)!%bPu5-^!_K.Ks8g.Mp"/BctNB#TYLeU(DGK>e(Ll7;j>5o(f0',tHl,t[=?J=d/PIR\2da,$E"RX/_V3B3`$HQhB/ShML[$hjd$_-8C6eHlbfpf\BE!X:YX\+k*;nH7*T+>"n39>2!`'pfh:65-k@F[:#rU"U.s!sA]1,kqE(CrnDsAiVhrdWD79.NZ=pr!IicX@uQPul@\^+<\jKPi_$J)&,l3/d&'ca\&=fh9n<)Ag`c:V+^Nqogl71EHhEb(?j!+)M0HO+3l[UQS^Q#7l1:p(B2/nobcj`e,"UStZK5YK:c[NYI-]o[X'9Q4'pS@P]`'2LWA]G3g[clP6%O@(^Xrb=OrO"L5B?r5kX^E,6r9h7OiU.5#\,-IB4[0l4PE)`XGp+#F7]oj+$/!l-8[TWt`A_FV3"X;i84QkpkIc(cm,]4H6:pVIf?9-oBS>rSe@a$5o8PoYQ%\^MkZ6pcgi@i-1hdCPX+G^_^_=1o6Lid5.POgbE;E1&s/PL3Yoi3s3(#Xp%',8-k7V3t],of(,7,?R@8O0J@l\0T^ihfb,h`9"8Q7TJUMqH]-bZ1c;@/_<_JXNM%BWleE/n?)D0cD:6%2[P*J.[@2=P"2l00_=7lg,(h#X.Mb_-87m$/G(NmhH/MWT4`?Y:QH>!op4-#WLmVr8hW7X$TuTIHr[;H,T1`!BO=d/j2;6um,S'Gs"Mk/Xlm_-2[ZhAKJT?0J'bN,9?DX'OCfSS/L0[9Mph"[_qbtY'#X)',n\@Is#OUB6G+?(3=-/]X,ssH6h&HQJ!C0O7)"HR*LeUeaZ$`d-ob?M!TlF?(am6OYc!h^/7.m5>Zb1"J5H*Wj&2.pAV7LkMdDGN)8S`dcUn&UDE'ijQ6]BY?=A$M[#GC*AU!g#oO>^u6&B.PjDAT6.b=V+DRF6MiGO7bXBJS'*rD"XA3"N1p1h&u>UPYBJfTV8LA_De>;^^+3.YM=()=a]UA/Ua&7bXd,dPLopGTWA%6cqfc)j]eMHOb]e*ld&MROdr7bG[ajH`R!euHhF;qSKm;=XO[W$Rq&-PZ0L;Q&0V?uI9f7!'!IpD5?mPIJ"$G"j%;$(eS?nadGjgU2k!T=7t!\s0sLQ`mjHk,Aj/UJCN1X#F@/Js3=*4@Bi2[qbGriFnHain;!7-5-ZnC@Mj1auRk^)s&jb01Yh%`+aXB#dJf,%f=gi,6U$o!jkT>#;Ab_D6A8'Y82>&X@e5b'_#pq:qq\:Wg8dg2HZdc0Vb6B#FX_=#k5NFfWBRuFRenM&a(FDe?unM2qQ;1c&-[91q6.r7_[n8-VtRo"U:)B@d_Zk1^%l;PSLNrU^LA.qS?YmCsUf\T!&pp5Ond.l.*`4+7G^"Nm0)^5lHoqmp<"O!uX3Eh?ceXm!4u)k@-S^`7T=R4g?&A`AF:O6?R$2qL.83:_+BtmY-"lRK4YQSQl\+1XM"N*;([t#)kMMqi`r@7dLBYadW;UVLF$>c%$Dl^1>G&7IGP7IjHLB3.[UnHqcX#CCDt_0<\]F.I]04\#V5of.SapC'Va/rXcFFG$*l^=\gXB5NW/=f]uWXe=8FM`W%29A[E&KOH0)hYQ@YX_r'Z=OOVendstream +endobj +415 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 3027 +>> +stream +Gb!ku9lo(1&\\'CoT;m>GR!'XS(=hWV7]saAs00qEYoEV&daBQ%RA1l?_?a-J-78u?`kB=Pc?-((gq[$?b_'GO>t^8C9G\g=08/R[pb7-flPnN[N"Sq]60prOpg/p:n34LD2p0%Er.&ZiK9u@^(aUlL#:[/8kr?1pD0"DYcW$XNId(hC])4n]@Ksa4&p0@%7PtP'\(m*2;>",1oQ:5>MX+4"mR[5rrpm/U%rB)(WA-U0AOIdl?Z[Cc[gd-COF?8>KrYS<$%]dHW[=3W3S<84'JmEAPTjP]@M8$)VnP:?Sneb2h]J<]?tHLN,e7#K[E\!g8]0%VCM>UfA9f2%pUs?c+V\.Hi9WGqa[6YZE3NOHJM%-fh_Ba-G"14HN"T*:"(p-r,7DZk;JihH#W1'G>\#Pk"%QK"4n3(D^<)p?pT=S\?Ng62064mbpAG-;s]eC4Bh5I'La&$R=r9Xe>P;iV+Ya5Q^04R&ckc_AuZ[eNLZEQWN3ZHm,QX):3cbiJ0;5f4_H%:]ln/6hQ`DUpZdhk:s_K"AShofUBK.etD3VXd6R>AqMF-4.%PK]I$3C-P1Q+;_hA`Lp\*72/Gn8+QV*MDtL\E'QXef?rtumX30HkVNn_FQ5)cUT7nX^Ygg\u'&_BrHAHPo$'HQVr0H=Uoi#;-g^T-t(eAs`]Dj>+lMO4bNJWV*df+#<=%Pq)u:OW$Nj2X^HR(Aqm9St=O)iYriM$5)^U+g[)W(+VWjI4065.4b'Z?EXH2/J0qr0=-A`/%jPTuFONRTi,oVC^J43to4>`_#2m,Uo5!cX5XV.g)&+W"SdQ>@]VQd""n0(j>Nb@YYR#SWW+%IH<$,H;^da5)Rg+\&TRIT)?Qq_:1C=4+pB9ufd=Jg'Le@j`Y9#jkC^J%P?KdPFH9@T??7%jZHK6sL8=OCiqg"%O#`REf.V_Oh,J]%1`/V2[iSlAhmQt&lc/@LV2[Xgh]b['dafc5^=p#1Z^mA[2HU5;e6I'AKf7?XK>"6:1//rSUTcue75B1C8m$SnF(2'oR;el03Ap+@lRa`#ih]%IBSAA.RSU_XnCO;[d1&R-2Vla=!EP_?qlh@!d1KWaM)RI83Gf)^"FEd<-JDJ5ML2K@".B+9"gVg3>e5rFK3QP>@LF*[@7GLZ92oD?"@oSe5bEWhdm@/p.V7._u$$^(U5lMJebUY(`Y'I8`Zo(]=h2h3f,W/%bVgH:[b)=k?IInnY4?,]>bRt(>)tk&nTWW)r>"QR_NFgiX,D^P7!8@9'=$Xli"-m+:?NG7k]LY-]8$J(9BjKGFKnqS@67mOd'aCeV&kQlYP^'Xil88?CE>GNY2ATQ)2]Mb$=S[]`%r4rWd`FjJQa"\MX+pTY5M3I\]rl[a@#q"!apiJE>djc5n=f"\]]U1#)/_tI2_thGY(HDgZ.Y2O.B/78brI<5G@C8U:YT':b'*5F;_]ZH'phSC0MT/uT=>g!#GpBr3._Ye"PLYYe_'sSuNSt^8h;GHI(7efLR)O:eoBu9@2rFHn.;Muph#8";A:h@srB>_`k1M=DYXC>FBCpIun3e5ioE+PIe)t"]Rmsl:bPRSUa:2a[T%k9S6XBUZ/^q5/cR#.;!#1s;k$9'lEE]c1[(l&EN&,2>W,shI8$FkH[[-u2J?8/Jm!fUF1;6"nZ?tbp&cl:.]a>g0.-Qqu]F5==*l'L8r/e?KMbq&VYHXrUI#/YOEHKm.pL^b@`H$[gNY%Fr+rJdA1r563^.P"^,Q:C$dhd8VO2j@cpfOVUVU\0fRMLgUUt;U+ZD'0+jr/Ulaa1<9Z;JN*T0N[<]q~>endstream +endobj +416 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2960 +>> +stream +Gb!l!968iI'#+6EoMM39*!1L`:0qkE2-Z7ra*.9S$UmAp'9\F_;NBqlD`9pYUb'WEB4bpp>V8[8fK_C,^OF$.*;j7A2(\k.E=Y9`AAC1[07\>V#PZ!LL\m"S"m[T8cuuoL]SWL*Ib@a7msNUBnDkKloOhMUL3ea?g[Rr7FRWCXI)o/_h)*8\3%p0lrjM6/@A$8i6>($0D<-[mj>baP\dk,uA94Rar2I-c&+SM(:TI':>gh!$)PugV+Y\Zs;V8`SHI`S%p#Tt:iE)A2[giZrH[R#mjZ7,HGp/-4B[JrY*c5uo./&>08R&]0u,/m.rpC4Gd&KqiiH=O\DTBXIq^h;_`kab2gJI(2Qpdk!>k%4j)8UGQ>2rHUiO)2aK2;ldm#Bu.q2Iio9>VaaQ;,4aZ;7HD+'kAE>/m"rIo.jXMTO`nH080iF(G3VP)k=<%!3'C,9b032<=:eTX\;EH(&Db`,B1W@=2bL(s@iG1B*`b\e]!Y4Mbi#!f1bd\Ltf72#Y`B"/Dh%UA@NnSkWg`h1Ol_,];5f1W?h&XESXeY2?p^>onXn^[_'A"fd00Se$VF\,X%T\GWj,E4O#nr`m70Icq3432<[J+m"7:fN+V?\fM6*cT4\V8)D&SStk*ar*0FE3eGXA2T#sqnF:*4iD&B(AVEt2WdQm>GVL<$`p_/?@;*<2fcbEHm[m*?pP<[Nd+\&qbH)<[/dLs,6K%S^bnVD(G9ukpa&4OZ_W*,dS3T>8$7.-Hoa(p`@DN5U(k,rOl[1%CmJPK%R:Bfqb@_J/g+rs^r"eK3jK6Qjp?XqQD#?02YMJpqRM<"6^[8rt;F""V71hkuW,6Y?&jJF_hIa^TZQ0gTS^&;r%hr+`.j,4n#L8o<%49umak87KFa=#obQWXFKSDmJOPSJFc-8('s]3T9g1^fQsJu"eJQqt8se_2\K*_;m'*gdEBBBQc_uP,>2WQT]]m@4VKQWgdLfYqJYg7MWPN%&L^_QeD3A%_(deO4G/2gjJX%C_1'AB[^);PR6Z?TuE+t.GO+nk3JGL,gBRR;k1('n#>a8]c3?m.#PS'MUg;+;TKO(<`\2HNVSG#B,H<pu)q*r7:BY_fFjo4OCga;7=SWgTFlqg3/&K3*_Qm!]"G>;u*SLS"tK+)gSMD`,BRDJfu",6FaDY`:u61p&h*2S/%HO72Zfb%7OIP15uD9\IF\^^b%u2F>?dtc6f0ed'+[(O;?#nWIO&=>,RT9[*NgXC&HEH&!X5'>$S-7E12t@Zr)I^%H`q:3o:0*'N)P+&=h/G=YGY8mIkH9Z_V]`:S)hOocA[+$rA>)c>E[EhV8he4_3CFLh\D)RE;7((L2:D>,e?UIk.+9G;A#s)5_-QV#hM!%-/s)aW=>h]A#(7`I2+P8Ea^Ths=2V8#7"dG&lgG&VerRTonBUe"Y308HneU1o@IO_N'ZB/7:G=eIQ1k#E0+WXEIb*!b4+5t*!6B4%/m*#,;L,^.2G=ItP8dhT5.K*45,[Gtc"Ng)8J/>fpcDtDul4Et/gq-dM*^L4q"E=)lLWUiD\E%K9=.*s+a]_"V3&`r;hIlf$tI.hhSuOL]Hbo=p,bgl'K+)",,DZ2'lX$R8$I)lEA3"dPs7;-1Ti(V0t!hJ--^`fnR'46LI5ud22B4ZI\F=[Sd+g1eo(%h=o]k`(qTcHW68s&/mK1R]7W4%>GRSFjm,GrBOgb4is6.uK)YiBBh:9R1`Nol1j-'XXjB=*&?(:XrU+p(We"H3q?j+b[e=m.M8#5TScC>Kl%<3b_j_M[/Z&?P8SN^<2gWDWk'_Qi&)EHKa9.$nY#Jq)MbFYIo_55e8D9f+l3ZH.W=PHoXK@?7r3J\%`a*66nMrBdAc;XFfF0JN^FDQj%lG:plu[nkE??:C`2=pP\F%$tU>7L^[(SHg:J5d]f2$P'>W%&Z>=TKR9!>s_0/endstream +endobj +417 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2655 +>> +stream +Gb"/(9p=>O&\[Y:khK2G$-hBIY4?#gZ;XMt>dB]q*3qR5JH7b<5%k?i/tL.7GP;(?JL.P4>l0U499_[I(^t`%?bb2UjM:9&bq[KE^0/:-ml>>+<[i">EaeVQ^0BFkEl5MO?=5MBnSJOofTELbp/j7JDZq?qY2'b0+=Q.gu+^8IgrkR=QT;jql^DPo+jf^5/Q]%.j:7)=WO206,9WQA#V?(U4Au>Efjp'=A+H+Ts[LX9L'>8$)uY=78_6&=Xg>bH3^LsY^0)Ws+I!ne(q5erJ^nqht$Z`1NU06Wc9+:$aVLVkc!0bE'0VI(ZaW-JeF3=E%I?E]j9:'=J!9k=RgPb9oYnMn=-LiS"d*B#J7)iSh5b?t'*pQ,k[lWn;iLP]9XFjPenr%U0Zd+`DHs!Ni.g`&a(i=>r)O&d)@&k0BYuFa*!S39=k&N8B$Akqg*Bihrni?%!kuM%g`(6>Q;6^:#*+@@X!f]"89AMpg.LA?i8[U(L\<`k8FT;BE([:@-e?aL4_?UVck-Oo;_K0M=cF*R!Kuo.U5gS\lg["[9(JLn5+h=Q,qIY10MDrZ7;2n.j0]PRUUMQPg`+=\E/FIb<."^h`\u"=$p.ld!Ll".LA_B,,.j3d\oe#Z=la"JKej/+Y&kjdL#:jj1*b_Z%CE&]==`J/8DS,b!nU@3KKm_jQ>Qhgo.M1tn2/EF3>Ir$mBLT-ql4:U"lJakdb:E]_COQbmWl(m58%B@#a0Mcp[aFid&Jd(8Je[jEdml"c=I(2<]-Cdgto%*oDjD/gF[0Z,,P2[7]mOn2UW@XYcl?#u28d-K\`Ug(IV[%We[oC/DGD(*YUncPA+oE;Jfea5ZTdeVCoZPcUDYF%`Fs8I"ZJFW,!Y%u>^r&Z4]+FL]%lr_8ptQuBXr!ln@@&\,CH5f3>ICKP0lP?e/hoKLrlO[W\H$TB:#YW1rBGeUfNrdTp8G,&GCf-`pb5:^1Po/,F\n+D0`KDma2Xa=QB]K!%W"K&,\5^\/CZW")h%h/Ihc6Y'Wd0Q]5rOHQXk@]AAQ7(WXksIb"#u_0eK^/`<7/]]6mN>e2O<"?[,\Ul#TSU/Jq(g@,.QfhneNCrYlZ8YRVnpk5S(%Ioj`=f=itqD[nT22FI1J5`C"@'AP9s[/;rTM[Ah7Qlk)0V7BV`521;lCgV%k_1K#]Djl@o$s"k(;jbD"R>60&[Xgg`?E]3oiZ>;gJCh+\>J`hd>f1=hUQe@(oKpM>SeV_B5_jSYI4o]W;/Xt"NCt_kjq&kuXTQe!Vaj7JaE/G8b"s!cs#X%)tOm!(H)ehF@(Kk6%//@.22#sq75CVdjof&;q.OV%W&bA\+jT(Z=BR\"XL>Ye9:",'4JpJZ)ccDKlTRHU%`fH4GLZ0GV"?#K[966h@TWQhO%C[PLpcNk_DQ8HdJ[cjn?H-!$,*l`o;SnJ%Xnkj

nshMM_jW@#eKGJj<7VEmX2#4=N,<>#p!M`i+L2TN8VQM\^jLRPPPHV]3kbUBt>[X:]#SPcRap^P6SF5FAKP9P)p9"j]c]TQb&Q`(7s6$FNCd6do8;%K,`'UHCHek4f#iHoH#JM.Be!ol;0?+r?oqL>ao%h3M47MQJoEanj[bKU2p'm[%46m`kJ.@X):jlMFBEGKUgkgI(\lPm6Ji^bR3qlu[h7+ZNQ1H]B+1^L,shi6-Sg.;GIt0,&<`CLG'L[!gU]o17ptXFApX=2:N2YT5/3P_uHkC-FbM5JQ#_LXD`9s4_"Vj*7mC2s?]-b.HKYOp$b*XtFqXXOOA\Tsc(rG=dJ`K^F1(gdISfD;30^h1&UMf-s3u3EA0>Dla!MVH?%Z%__G@o7G;5RT:dmJh/0l2-h:1l.'NpVM*3Vd%Wc$G;.?H%P7V^eUiuK]N,7eoG)pJ2SXDfnnXQ_s@Bnp3TsLN$rf\8p7&VW#+m;Eu&$GV9P'5.J?0,YJRSIVoM)WPpdY'S?S)B@:"mi-26&DR6I]&`_SZ0#cRRigh;GV=t.`,FkOm1kH"!rP=$P11&bmWj^VP$&"\7Mr3mX3^(3Td/UdK]#1V9Ml>.Ea7a-;PhAC5i=8PctQR%'.`8Z_rD?9dF/.9pM[fAPd*t80spcq4)043c,-bd)]8,U$))@r6KLU.pQ5G[,t9"-`Id2='bGf2:8ea/*H6qSGnGM&OLclamF;C&XBH,`i#C?c_LTCBj^Wh^e*Hq)S.-9bE%:!<%V\_:k8fW-b@Fk``9RS_/P@%BX2hsoUe(X[1KTDVAIm=bu:c##AO`C?3IB]rjNjCGUHb+3Kt]:8OZSlm&N*PG#qD9S\-%/IP_Y!iB]'XKu,IcZ6N/NSFRo^B(S\6endstream +endobj +418 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 3032 +>> +stream +Gb!ku>ArQA'n5%I0lQ`^(-BDq$hV7AZ:3J9YZ\S6EDVV8OYp9J'OIS8Iso'f6dhbuH#7&L+p\EM5]D([h]/RL<&cKcnEk(Fh/rKeWggM71A"jgl_X,.@nK.]i5o>Ap`14mb+pqKOJ1]P@@jF"&Qsj'ij#Y%s).U.X9L#eh.\!l4\ni<4>:/%+>N\%YGbF>]9\!?jpFElQL4oF1T-XM$/34.%\0;6)bj\=.iB-$b*(-7,m)"TNH7NH6,inS'1>Zj_CQ*@(f0H4GWCYVa==Gte#.aP/J/fuDF=I/ujcT(tI&+I^B,'!NX"l-c!#<#VS(gpt9OKH/sBe=MF7//m_PIkX.L\I5Nk9P2]Bnft%FfeD;k]S9nQ`Ku!K$#$6'QVF@!@Ap%KB6[-r8CS%'"-TH;H>Irh7p1Oo1lT;UmV*O@Q/Th=a5"!30J=-rd'TX7@JVl,p(RiDZ^l-g0M8_MUE;8n*kLY@Ns`<`c-AqZWH0S;c92("Jo2&_>jh.:+[M,fV1-f7lE"aH_^\!D??^NB#F$_FR1tJY2ONa@=G(:VJX*1M/,]@1d=QiX!?")u+O$eFs-CUVAk_qT@!:$\r&O!ViKE$YIUh]>u*&Upf(?Wuh?'RYFBZBZu+,u&(6?=gVIXjQ.7]s#keY#X/l=M9\Je4OTNG4%V.J.1GqX8@S]^EBYmX(A!WLWX<')5?%AIX/_JSWEunq`lDCS`Ak[*X%F*rRP[B#hdCUMCramUe-kf=:9=RQQ%si=hdODD6RD[=l$tjH-Wi:VQ825O`2i67KS)]Yei24iCC).QsnVt+H@[B"IBhl]hGp/.'2*bbdf>jNH*E0a,)\54e2j""#LfdQAnn$[H0oO'`!/_HnZJJEgjJ)7Z+CM%\,r9p4f0=o)[N'VkKUWX*5YAT1EA"T;%i#bA+7/At(bkG;BU:'(0MBf2^GT1X*cK:?$TT[tNG,SVW0#D=tg?iW]Br.sjT>b%<_L`o,PIO/.$aUHT/3oZ3Qim5dAl?>)4#kt2&$HSe`+K:\MXh9qmf4^D[RE_EU>?:ac3b2HOeaHW`3,Y'ogU0)R%S1Qk8W9Y]/Q`5@[[[Bm<4mX<`c^j2`i8"b]hse"s[-i2I-f]*N^Tb:D=0,1%G-^;5opsa=ds8.,O?o@/K)aCqQUGG\@j4%T2N@K,fSWurOg$KVqX4bg/EZ-$"i%4W=qjHf^oP(_;l^5qt5p^6KQ;T:;9$mrJ_Jp_]oMG7i[orF*;>*o'%)WHd&B4-Y&I.\sQFng`=eQ35CH(8I>5k1CK!0Qk'&ULi$k1!*R0GD'8&KW*J^i:6t5S6!:)-:^5EFlnDV?TMeUOjunHkZZFI6I6q''9QnKfXfdN?16j&9C^:s>Q33,@>%s&DW$f84):eE&!Ij[PH4l(bn$J`-_%MHqLm*b):OADYc$blBQp*o0$DKSNq`QeCJ?`-"e[oGYF/jE!c5)J1!\`\4BtAGZI5I6=:q`(@.hItid!H'Gp7i#RX%"<++E'=9#Y5UXeWp>m#DELRk,Vchn#E5qpM+8ToO6<.(&TH$\*G6/M2$%iY;QaN,eJ8=bV+fnp6%-h<><^\%q$ESu5f^_?8da[m+.#C@Li?Su_%kfUaK"NNZ!CN>jd*F5SgmG1CRso(JZDnaN_#XNY]KLGV//hO;jmA3KKp2#Y[u.[Nbu(l7,,%_@RP7]MIG='e38Q^Jc1^KtIA;DM:q3cW?!EZ7bMG1\caJ:&5@`dS8L%\lp8l!JalqjC`ba")n.Xi#L5l9%P#nJ+Xq.bUZ+-KA!36`fUMmB)bT+4~>endstream +endobj +419 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2749 +>> +stream +Gb!S-?#Sa]niaa>O5@R;-)p@i;\F3,/[BS7eh%56MF*Hi=C#)#MK6AD?[jk!8X=0Ap81*+?C]X[3LoOUcAX%_n<3F8X;JR@Z#AJj-5E"%Quom^B".-=fu<+^=[RK9(Wu7_$QOr6n/n0qD1%UE(HF`@P5EH4-!)R8L6*NEW[7hK%3;'4+-+TrWS-#.(>qVL\WW(.63a'R+8YIE#VQU%r3DS^.Z9-V8%:b(Zj#IS++cpL?18Ei:Prdj/p09hMkXb9<:OIaRUqpPZ"aU#Qgm\SD45%;N(]`/R/W7^4bq9[CDP7ZBpBHZS?Xn0l+AKiDqOCe'MBQ!';Id1FIiJm*3a:<;n,G*D9AA+a*<4GPk5X%:f0Hoh]EZipJp-tm-sWGq[;hsmG:mE]s[&a@ioD`FE^,k=`bfK0LMdfCABp_+Vh@J1^N#p;9-e)7Wm(POt7st8,K?;n\CDF!,m4a/M'%?Yr/c3*QHaPc!smmW\i>o-I+A&TYkPq<#E+g)ROL_EVCGp^[%UtP'sOV'tM1t\QM(=4TJ/^)3tt^'n\$TCgEngE0P"[b[..LAEk5IACn;WCqEB)A^BbRO=G9"(aa$(K$UV/i-[*94BYFgBsjICC&Wr(YSeZ0%Ld'K(CQd5XB:#MlNBdC!6&Ak1O3r$r]mmqFV*pW-&]j`sdg),_sc&kde8&CPf.5aM)EdRiU`^V(ec6<,7SA]<+j2jLV@+QeNP>;BCTA86b]]&3E*ZpPlJI#PR(pQg<32F3fUBHj4)kc*WEY-s#s$]*;^c-EAq6%FLX(>P@-$W59VP%j'bMbofa"R,d[bBss@&8F(RdirKarR=)EJU[,rI-/Hu7I+%Lc1IqqqhF9(m)4La0.3o,"h7TVHJnJcki#&@,\_&0dAe10"B7mBZMVNjf9MNZ?(RkE7')eOS+6+,`8"7gEf](DfEd\2SXefh3R/Mk,LXH_L[V%Wc0?24uRaY31!SO#l&4:"QJed37HT#)WlOO8eNkq]o0gK\P@U]b0@7[nDoH)"FAb_n9/77&Hp(C#eUh8MQ_H4>PEaqJbnj]0b8ZU!f7Kpd5aB7pjR]t4!=;I4h>!V^UpOYamfK?=j-tpNP!oRAViSVE&q[-Lupc81=Qo\SncS!o=O[66AbG&:P4H4%d;1797E6s>=_t3Q)BKC[VW8_=+1j@;.I><>8mL(AQhC)\H*e%p=LUukl(51C)pKmk7gO`(eXd7O6oJ%E-0M%XJr]3/9tR2TrO%1ICk;GPG0UZ,Nu,K3sJlH\hFuc^["@rmn*helPVj;njQ(KI3iqaBsFdiGrA$?M]*/)1L*9;r7MXBn*a*JVIe??BpN9T3p$@O0%!@3\?A#%WA0It:1pGNqq"]sn(48T"b54dnRJHD)QdF]&-uV#W%#`#ZYn?6!l^f]=P=:*mQ/)N"_=?-]YfaYf*NFRb8j;+pZb%*^k+0>/UQ'ZL8@CGD0=Z`UA&*HfG9nJUgq'p-4ZoG;Wp."E^:aq+hWOQ06%3o&jpt_HXX6`Y7%"a)JWkci(VV22eBenI$_mS1a/H!M6e_rphF[upNh9BgMgG"#to`T0boA1#.S<80sN24+#iC;(g]q4(`L]&/+fQk8L[YP[KC1/pq_,0M_'<4rrf173Y&t9.qr[4?0QT_02@=efU0p`mS-oVgS^!T!91E:)VM?3_C!l0fo?_7CAMqL^T7fQ[>S`A/!@D`4'6p#@l7m&Hs.HBS.Wt##i!E<##K&Nf`-G#&I`%DJK%!CGB23!Z7cYjmXN!_s7#p1ea;bf@.KKR)rCG8O$A-(;sr8/]"@e71UA00a!%$9_'MP)Mhbk3gT@Xi@DOd(GL!''TRM+^XqqDV2=+1u>V/m,2t7eQD9(>M'6CL\.mW3pP9VkU[$8#^\r+sQ0VJClAX.q_D=@oLfmG"h\-,f"Y8[?gLuL)sEFh$M1oO<3m\YEjj.L3JEhQ*M"\6gR-@ggXCd6(i%2@3$qR!?nlN&DLKQ&R6h4)8cK`=Y5miK/tpoXa9.fYZ(_-?gI"uA>#Hj[tT?7aMUKqcAum=n[T,&brA'$tpI^pOSFlFUlXpM*PHGGGoFeAendstream +endobj +420 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2879 +>> +stream +Gb!Sn95iiM'#++Di6dsQ1jVCKes"cp#Nu=H)$>6UKpHDAgQLbD-%"(TI]H"B%pMeFG7N)]"rtOTa^We%?[YplQT4K\eZW+IaKK[Uo:nr`O3C\D%IAVh5EgTA5Q(2jD-)IsmI>D2fMr6!MSsa>N)ZJ6GXR=1-Iq8Nl*rcKCRkhg(Cp,Y2p=qKF&d!RQ8GF,c@?SEWA*[A]62QTQWOBXJN>;$o,:0HA-4-lsI`&K$TgTg"\:Sp<(sC#nM@uM^ROYAKW:3s8C*lT6:R<`1L7RUr/L+^.mqLTq@UF('f=-D,][W^tLX1_6^Fm*[hZ[qE60Jd!niKUdWP]3mT""5`>VQSOia9O^dii&AR(]rsM)P"ha2mU$K9U4;3Sj@=nHr4nm./-OESb%=p`Sk]3k`<:DI_XT>Zsqc/tgpX__At"\JYU^BU/h.FAC\CL-ejTrR&hOks)Z`+6N4%l]EJ9bSIEHaTP]K,"&)eA*V^OX;4]._+F)sS+./q>L5UZ"ugKcHm%1b0*3=U`irG+aY\=KNF(PGVoK3LZn1&a@hiVD,pPgEB;DrI:kgCL%6$H6dmo!]5h.B$KIeES7[-J/^-KIfiOVm'd0"^0RhCaeLp`i;lG>)r!gU$JR:+Vb.;lrUJja;4n^RC[=a!X&NdUNm@H.@m*8Gt"B(jSCs2&Kl#d]S6ol,+j@3L,Y5d$/67#+*ssdSsrhU3+?dZ*\23\cSC1jM0ORKV!7Sm-k#)s"&,LHecF11]l(#0_hO1Es0ntJDR&Wgi!==E'PuX)?GN^Oq$/@kj<+u&W5QO44'f(k[[k`N^.&DTEKZP(IXg#_="^gS\f0m@<>/-6HC?S$#!I5#oMB(N6O58SP+S*)q_W3U%oIr5T_52!cAgFVb$'soTEZim9ck_N6n=1>_>;Xc[ck@5r]P?6J%04_+f]tBJPRut>0,M/`^lb"kHJp=6f^O8-V&jf=po(EJ8nGRo1\M'olWY!l%?lVMAjpE`8!)gBJK4r,4&-g(W?1u5aO(+V>+?7gqEXd\%Z]r8/OEenZDG@l"TQf3!P,]sti$VpemD3M(_\JtZIlnN0,B/9Pt]s*8$FG6-H8%h;O1X*J@1KJ:he5i+o1R#Dn#]HXRY*`sS%t);E^Ok\!Eg'F#gj6lRBeUeaK(+R2pS7;1[>[$gBPn?A*edNsM0uHp2oTpX`IdD8_ngXkX%5%<]Kp(tpNqamC_.K=oc,SQ;+*2^MZ.A,gDuc^Ag6YGgrp@._P!4]ZA6_e!!hLR4^gl)DCO-`I4V*a?Z,6@&0%_gk!US7Lq<95-cSE<@UK-(V#"s5GDXS)ImY?iA.IV?F3Z-(UN*;O/+3Lt/"gL7psb`MfU?E@O#E`Rc"D9h?@](bT'PnK%T*^$AuOWJhHLsG;\4\?CQbZ>''/X;aNe!]_2W)r@MJ-)g!=r4Z/t%Hi8"dGXGEcirGObtgFnUS.9Z1DJ**pe'l=toW?TeW8HI4lAZ29rU*-k_GD[6T%$;b[m\3PL361j;Fb'Nh5OW+PF+AcO)aW9]JR,rUemCRg.EXV^4J''ilV#~>endstream +endobj +421 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2532 +>> +stream +Gb!#]a`?,q&A@rkqWQ.**!0AfZp!GP.[]p\8fRro*_FLV%aQ8gnrg<=[Hcp284^5IBVnAJ.bZKN_5m==jF17gipQdHc0Wa?8HnR7grYTh]l6JSYK81/S@DL``]2H)BY$E#Igojm+r&\,dN-l#"F#/0)^>r$-n_EQ!ghABJ`2MFCnJ\sEt4Y9aNMREk3]jiDNn*#-q$@^gNqkBd'#a@2u%qT+hgj3ZPAGOO\483G\Jl,Dj-(8H(7)?(XlM3R\Hbj.l3>`">(>\B2-WG9>8_,[nee9dEpSQ'9IORb5pgC#lBp#;Mr>M5bllD`M]BUfBe:eNcMg;(#%DH=J7oP?9/npg0/9*:'Ds#dVMGWO%;5'.qO2^F3LTqe1l\4*[H2QI&>5pm[0l0#M&+XjKZUOG+\KMnQG)`r-P`)b<&LRY3Xpi3W'CpHR7m8;J$Vod%E4gU0j82B#;ogb^q;V"2;YQC/Q^u)C=g)J?l%\;KXm"*%hgtB+@+!k8.W-aXiB\fCinuPe;PfK>)Q@!J\h],p*DR[.i"IC(76dDE#Nt+Jq/0kLqqBcsjmo!1T)mt#q-hpI!WG0S?,_&_\FarKn7?NBdKH6T%l-;';_/Y3b4X0*b(u?7mG"$O2)tsHX6`U7QL<"nU'#O_MlBS,PjuQ:r]G)*9e[FE*[)ktZ..ID5Rq5`/_*(km?\sRbagQhn2+RR>&]E7>^P2Qtg).??:5J5Y.ePC.fNBuP$WO),+G4GmRPccVln!F7A@Mnt2Jfb*,ql]J;1kTkT0@'r=nJr\89f7OFhM;WL(jbZnK:s92g)Q@WWDRN?UdA%DJ/Y*r/#q#780:I85^ImoXa"QrUoF=3Or-b+O+[_un&e7s0?4*NWi$[Q\u-&>Z?HGe)\3$+a%.#g*S+aBqaL_u*eB>.HR.VJs?0(>R(Z=A*\kt.I%A9ta54XLpm\huSV[-ft*$f$$/3BS"Q]U6S<5\"#RG^_h(n26O1J!YaM#p$k)KXpK(S+AA?gdjt=f-B$\nG@oj!\XWIu!X>MVP'\N1Er/hI2O"8aAW+9B'Ws$)0hO`I+Z4+qXu^'JcYEU=*_L.cF5c;A#f?Gi$X^;*IE1S=7,,Gb$o,tCV`Wg[h34*IY"]enZ7H5=dFB7ZBrN8#V03,Mo/iJZ418r#Y:W6mauePPqK8^'97J*,884@m'R%(c/T_16JgGEuJQ(Y_R_D'GRC0/1ZFEHiL%?7h4f2C9M*]fHhc"W&<__Sr$f^c:,bp4Nqamu?X5kA?21:_kUQ`&;^1)YJ"qc4(u@:>_YBES]APNo*gh7;KNGOI/=+$=iSS]HW%=25nTW*N+Ae"8sf+YClMFC_pT=n]f6]d$nYEQ%)JLUnQ?)971)rPP$N*GtZ`ko`]>$>bWnL6o(Nf@35(LEY?C=4"*$ZP[oCNMl0(>\\PffMQ..,d?&M-5_)RFH)5\MMFp&kRU.$gNG4!eQ^sCWGW$m=&30jFepuS;Z?eNAI@lp.Q"(]79#!B.o@If[,;SG&NtDPXjk?QS-6"J&-eJabK#1[0.<]L%WRdZZ,#&-dM,SO~>endstream +endobj +422 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2835 +>> +stream +Gb!kuhilbR&q6If6B>Y530o'#Dqs^A.Ta/[2T+Rf#[;g;[cPU(Yj>_Hh3)Vk0JK_pWhcdq@DJ%1XmWC)^Qp:Qi?-UBECS$a(?F:u8uP)m.mcDB,E%WDpVkst45.pN6d'Z.^`d%F=:T_2pL)Y)_eOVO9!G]+kNMkp35NhO"&ipC5.)T?aP]5ZqiOtjSHX1iJ,LJsi,<<+;$8D"punhSAm0X>Hh(QA.k]\?`Lg]2$SM2Zr7`ksS]D-?a!H`^_OgDbVcO]bk.#)YCin>@.oL4D@;9(\4KjPh#pTtQ^H<'n-TIo@OHZtVX[F^Z8tE:FPqf=a6=Mr,P`0lj9%E2j;1't*!$74UF&uVf^muJ:P6rrl1<9p?B^b]Aqi#hg8)=`]1feG>Ug8/0A(uI8eNNLknVA+N$,''uS.bWR_B=M2I,a7/[C;Y(d4HbQ60$1#H0;#V:48\\XMIdsHe0a4l7h40UsZPP#l(70H;2AnfN3(*T,=QF%U22R;Bg?sG.KOc4ptG+b&U@89#cp0W'FXAg3A6KVmTh81+c#2XO?QCR,bW1F)8oj)OtlN>PL%)ch&*=@E!E-iX[NTWk?*peF.,REu*EB_E/riKc]d15*qnafV:G?-8Q<)dDMX"Bc]Lqc\7^bGb&gp%KlX9_.&,QKm)I8&[k"1_pG-?Xn9if\iRZqbRF\Xd(T\2lmFM**Mk5)d3f;;!S%[6BJA:*,j=&5/h['rirscB7>=ahVS0*7cJ)[=XgJF#7!%C4klTn7qG&,0TclkRK5?Hb],EnP)e,fhkP8g\8GW^?WGMbPQI8CT@hc0#i$h"9lUl'VHb7oePW-/2r(^Z-N%3dj1A7G85URUSBm="*L5M!Hjc_9i1(h_#LEh&sG^L$9=_<^J!FB/mHtX4nC;Gdfc&7WBY54g?4+KW8EBl;=o(La"7$.q#=toB>=BAcLT*N/k6iD+WJ^Wp/7QsOkVuhaWp0J"d=RT1]HS7NnqTk*.Xe\Gm@LQh4maA'?Y"!oMUS[gr7.W!e`.#M#FCW^K&hf2.!9uGC2Wl#t@i=d=8Q,!\fO:N'CMDXeRB[B=L##Z3fk!jGnQU<8s[+r)S_ThI,AX$'G-Y!nkT($C6f>=>C<)TN.2.d@1[,-c(UM;;%[ZXg&5)!kUjH#(W4T_C%]7qg>mo&'_ZKCAR<(N=[.^;H7srG:t@q3g?gm*J^n0eMP.PS`l#/%EE3PL+GBVe\Z&1[pe'L8^_tL:`2HLo_TT.(g0+o)of^JAg1&U?l8Gp\?jMV0Wr"gj3*))IALGEBQcRL4(36cmDP&$N3CmScge(U_GB=-rYiHTDB#/s%\pAt\mn:^qN)6I=7>5+qp6lKq#ZNUC!Tl$f"T/5iItmgna5'Dm8"h2(6*"g3i?F&Dk9RQFDoOt*8Ci0_fB*I@1U)_/3jM%2Am`[e3W/e\QCo[@;+`JVe(27h'.i]53Mf/>hiXA>!S])6ek1Y5CoUL.84r+h7+CK3=uSS'!;Y&l@N=!)C"hlKgG0(;YTcQB*`Yp(da:(ZT>3VOn+()OuuI-!tGb_CfQ"WJU1+13X#m^1!L"j%TPdQV8*l3#.-(;X]@R8BDtDt@ECIp=\*ZE^?jd*#,.1RJ!rZrm!HHnqP7kTJ1SshNp=Nm>:1]SO>@FJ^td;5-7scbO$)130KG]$Y40*<7R$-`2s>O9f[4L3BJ\;Z6>JV*YsON"0\D^kl>(_aCZ2As+WmpcLKGhU/FZGNoH9'_''i8GjVfqGmG?m$5D>.9I\i)(o^ujMT8jA5:2mBGr_[2-UJ5+M@["3'JS0r-jo>`?]W2'n%2f\.as]*_S?AH1Q^^ponL_Q:Cg\$!pHPU#@Nb~>endstream +endobj +423 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2057 +>> +stream +Gb"/(D/\/e&H88.E?DM><[I%MdP_M)Jn?O[!GuE_"1Fnb/_XeeM&mtrc+i-V.Eh:o''H).<]?0mS]H0D)"`5X?f?A,fETM0_LV8g/8C8Wf?E'i7S8PpT-8btreOU?-.Z8dQ4)YTDs8qG)?U-.1$O:V6:,j;RQ.8BMk=Pl7Yi^Z\+0YjAWaHKA5nuR+7`Zi*/B"2MfP8W[7lOmfBd)2L\3&+n/U@H9QUca0bV//C/^J*Zc6XahBm9a+IqSr>,jtr6H@]k0_"h.":fG`e#.(X,joR/'>Kh,.`3DUcP<9.Drl:lNK'r#jQF1gK>`#'R3-/O7$1sRHg6k^?*I#3l+R#R-%0B\5m;[LF,$Rd9V'!h9BY;0FAN#+2/O@`,E4s#WEi-@o>,k'\+p`e$Sgj!q\<@5l*'H,CC#P:!E,fo;-+hPGDkd8N:OVajoFP*\T,q3g2<\ZhGVXBj>\%JeAhO%M\&!<9ZN0@RKV59QUB_78#YY)/'SN87L4"[<;me^cl!d%Znb'9:kBAhD?J\Rg1r$ZqtFtm\%"XmC+`)R]DA5uK9\L8n8co28BTk5C[KiDFCV+aR[qXH?"]%@SthbsDt(7O3c(PfFaL__$En;kdif,smCifhL[K1X4%Ui5#4NJlga4g9fd.7(]":9G8`^GkAlejTLDum4.,A'IZ(g#EUTcUFedlX"nO^UX"f;Dj412)PmQne_HQ."oYBb'36!"I21TfP>E9))G[3Vm!JK!aNe$I.)?9J2)J\T@@^j;IFX.4]*nTfn!e.-j$1R9'EWh:\p;#\9l'CN.aDamfDgmrZ53Ig]>nef:80kK?,%JR)F;YohX))Ib;"OUbW"=%<&6/'"XV0a]-GrYbRYTg62j*K"mL@1d`3B^Lg;27&S8G-!MG])'(+@J50oG-r-PW2/6DH>Rq4Ro])PJ6BkfL(eNF#Y*$k?>Z>iCEGb8$pqQk&]kcJBnK:2;A;JhZakaQe=*BW)K67T3/n+&u_LMJ>&5$AhL&"aXl7X(eX"5Umf_JL3pFO"&B,!t2G_$o:NEf",L1e?^#NHIT_^:_-KS0Ag.:hBcKE03_UtHQ[,mOa@Eb][[3opF3G-k6Xr.C*dJrie/@)3&3pu4K3$qc2'=:\(,L9lbZCkVERe)ft8T0@R&J*\8h29)6mb&(;i867oQe;]h/BV<,QI.QdGAO^4FLX6"/i3ML&qF!MM]()Q)OW17)`e0"cVV9U'qbN56Cot%bDPbF4pUi0(:gSB.ZqIN6IHPVIJ[]nR]4?d<[H;o-9_j0^jM/&`_H=Fu5=/7To7Nn$TF'"ub[DP2&VbC?OR+fa2*^p1eqLS5G+&oWnfOOPZ?bYendstream +endobj +424 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2171 +>> +stream +Gb!l!gN&cS&:O:SoV7qaaNSV\2ogdL;YF/NW;P_CYo%"LLf#!7`OiPX^-)Qb&:n_B;H]ns#$EcMcT.nYmKO"$5LaMi;h-eOG^W/71)Rtq*Qs#]'tbKVHp46hV<_?=fcC7Qp+1sUn2rgl-dm;l`sQ32"W$>#(&hUO?JmO[/Dm/NpE6*dA_8X=R%l=A1*$&h0R=<"Z/Ad'o*B5ZT(&gXV_IrMUK?07&,>C0LBNoE]43Q6H6`LGlV$9p-Z0LRL"i1sApN*1C=t3I"8$2.Gnq;$8EJLf+]4oN\4C-7Ho=V2Tg35S,8:bGR10tdd3ol"4O8tOZJ\XC^gZ7/'KF!WXot]R1i[K+0)9!?:SJjIba""B?5=%_l_3mkHn)E'AmD;\RfK#g\PVn)^g/\@1M\pCSkg7Y[BQkE&A&dSXoYEZbG*)1mH&]TbJuiMhnfF\6>(5T,Z09:iOn`-%gkIh2&*GOL!8*teJ('9m5&Z2Ko8i)Lc5+,$klR2Aa!ci_=A`e,Li^R"iX"a)X/_j]EoWo/#G"H*kpf($P,#'I$JSXgW\?#Angi:QCW<^hlY8I!GH&V"`g*7KO&=7]L6Pa2$JN-jO"u5[`Dku\@"9^"E&/W&F8/@(Y$#&Q\Lf+(dEfkD<9P/97\VmM6^*E06AK'2`1`DPBoP]W:!/Y*:b9BscGW93f0/9U47uj=1iSite7]D7\=C06k_Y]VgK>`h](Tb_TZtj]d&Ru'X4co!?u@3Ds3G`Lj.(d9KQ+-7n+X:]'cT7tZS$K,'tpul1;Q&e0n+Q8Q#7(LYMS,85Ag7GLeqbel_)_B_8Y8X\O*QQ^pW!r#pkc+qsPqtNQZ_dTf=3tFjOuU)'sRE6D5Z4:DA]L$uq]U/1rG4-B!0?2_n_db"idE20rIWe_mi%mKRS[0a&HpA\Bk048lV9nsilDh$,c#D@N=2q@B[0FiiVUlBG<3dGglOpkJ'Kabj7c,IVNB@Bd?2VURdl6#(N\[&LV%hNN*]U^N`&.1q=!9P1k0Y+k>7rtIMM1Mhi)pqir8<1NVfF\j+sjQ,["Ln.!.^\uG/jZJM2\F>pCSK8CTCr]^p#@:d1re(W;@0QA=/nQ`?QpD(&f-[pB2CaWfBKB&?\c\`rTij7[USVui0JfqhT"YRLE)pA%N#F)Kc2%k2MGJncT*EqjedMZ+>dQmJ#SW$p&2@[76ao9"qB?d-g.H=lc#8eZ*Vc=/3&>ej^JED_BO'3)XttD.9sTEK2Pq%PBnoPS1KuVR#J-DC2L'FjbTOP/Y:GHQBp(^X8&:fs*2KDU4)e5dtre=KC9TZ_Ll.:sXn\k-&!-f2ssOf6_k,kZ@QshBmECXG;nqptIl:N-R(Pj)o#;'T_!0iT]`Zr[t*h+u1>)$I1"4bTefc=QSL[F?2;-og>K93Rm]g'1QSuYF\3o69d&[jZJ<:U;1VKVdTun#e4LPP[JlEYom7Sl#kmQ`.*DWX%1Y;/hK@F`Gm^HTSUY?H&l@_5-=rBC7N?89Q6Rt[A?/T,M_cVHQRX_T:LgTOI7g!<5o@B)mC-i@G0P=Q\VlM`A.-*Fh[D-5qqC\KJ63NE%$-"+3=':0d/KtVl^2T0Dph;EG)kZ*iR-pf(=l3RkOs7E+_Qj]13k8J=!YFC&n7KtXZOHqW4u5B[]\9fHHQR8ADS2ok>Sl_VFdJnK&sTP^uICG'AYjVi&j`M$spCU(r<_T^/I'Lcu0BS$P?NW=Iendstream +endobj +425 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2654 +>> +stream +Gatm=9lo&K'#"0Di2H!G>^hm*4ZT+YDPk&q:TU"qkQ6m.KnLWbJs>9np%oX-.FKHoZ:j:7D$saH5@/QKL-=\YbL=dR_qdn])0bknM;gAc'BUR6k9Vj`+)uu6GO0GE+aVlgEBM9cI)af?U"Z5L[:n:*r4qIPf_fZ`_XI7\VM60Q@BYFGHaD@!)1L#=pmJWhRc;]!L7[kPUneZYW>g(u^:-_ZZqdcfC9;Br-Rb_^frlnP=S&m7CgXS=c4k2m>!9-&/Qc&=\SPeV.^l?l`n;H+mb."+f39Se[7'(LQ?k@EDX2%A9ATqT[[X+l4"FUb@oBF"Se>2\aIU\PC@N]P,AP1s_s9dXO)-J=[2)hCH_D?.$)_llfj^]2CR4#XLV*?>SDd(!FlKmBP'jYtm_5E''m-Obh\>+Y3-^&p?]bXf%bJS'1?We/L8&Z#%hu\coE%;HT%A?7aELjk'YJgJ.acI30Bof'ga*4ap7Zn*2:?A.PF-LF*F'#M\"OZeeWN1Z/(5A;q%AVLL4Elk`VLE_TK;]R%]RRHn*1+)Xqtc8%;L^$SKXTVd/8d*F5kJ@b6Fap_GgatG?u6A%S3X-LmSD*-R5.P7k\@P[:d\l0)X6lX=a*e.:p#2O+LFgGD]&jKPsAqH%BD72VVnP[HFoR_pAHZ&gPD`]_5hA7j4:_&48N_-S>NK2rDq4mTMSVeFL/doBEKYs``I!N64d?LJBb8bl`D&8O:r+t!i6qD2'*JBMGUKu!hd!:1=b/[D\=fqN6Su]VF;.r=/lhb%BVBf,!lA/:.Ee#_WV[(]\jCd"D*l%nNOaUk]LK5Z"egg4o)*'U!e^g1ca>0e"M&HA4Bc*G)rt?19]g5+YJ,C4=Z`Nk:.4?f%e^*.BrQR6pYQ)=W,.>692(MCOf*u*q7cL=srLdO/!7MgToWfF2!">4e#MjD_>)U"HfX'4QZ/#B=01K@CFnFDZIB9:5CQCaoV%6@"L^Wa_Q2<,K)R27SJ@Rq/1f>$b7O#2n\fQk\54]$0)J0-uTpK+d:=Yh$ioZhLX&Y-Kp#i:2h5[PK"I/7J-OfeACd_e'AN$btJGS"js9YULOs*=.nFnJRs"uG(;^!JJI-?9d[EhN$R#4s`7:5?TF45Cf',rWr`c9LlPWN#L3r*XOkitr<2Xe&ugWjCkY^$q/BBK(I2ps*V9[")rK>r7T)dN2@:m]CA'W\4eEqo"Q]=Vc#^B:?0[_P!s#iV$CNQfW!:0#-C_akI#&hN2TGM$b45L[E+PK8e9%QhnYbT093a]m(4%j*[EFS1=VF]IL.[HS40-iRSR.5A`;TE;,>=i$>*A^rmrBgseE:Qd.=bRK8##:doiMl5Ol*o7oIif)[__rr@*hig,FEp!rCcApIr$T,`B93K?ul#g"=oVU,]/g4(>0YfA3(7.d!.q&uM-?:)u.qHS3Y0$%UP8@s4XSOSS>#%2o/eY2T-dK\VK_N7c*g'*]lX:>=H5_ir)m,Z_$ih+;_4W/D@bkrRnP<]r'aaPg`??%61OrEChgP`X+Mc?Gk0@;/.=Gk6=*$nK6SH(0QiZPuSfBX?-=lU@Amu)M.?:Q_Ts(Ph2(NX!5jL8k0!D/7nk-qE0gtAC$tX_)6@&^W1aShgHZQ;X"^f0e%]l*$A&endstream +endobj +426 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1029 +>> +stream +Gatn$9iKe#&;KZOMETCRW0ZGh,;CVa'Z=bl?>Y)n+kI(pg9#db5M]5g[;K'@i!QiD7fH:$g((QYW2kY?#)$u4e4NrC%%*!iA[b`&APPM-VNq\u)E9ml_@1WrP]]GK*E66q4!*&@[]mn_;M7A[\!E$[EMI8Qf68H=pA`jdBel^UHYG]7mD!#`Gj0XAhCo$L$%Zce-cksQ>$Ap!2d7?AHCj?5gY<[[>!)Ks"9Qq)o+P:P1/CBK>kg2ICJ(W5n>1q:]T'+;UWI\TSotl[!h"4/cjf'HAX*m,ZN2Wnc>EEV5`&+bcR"9~>endstream +endobj +427 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1994 +>> +stream +Gb!#^gN)%,&:O:Slr-EbL]P:1puj\4-QTgiUj>Zh)-A(/A11>%+hRaN/jSqR;&$.Z8Ok"__ueS4Fr-&E7=jBPRd5>1n9,"ShG^'UAe@k[EaX%mf<^Bb0'KI8lW]%q*WtHnUqKIlUE3<`?^`q"cRCioK=JZN4pH2`"A/o2AXMkBH`$P7.'Pa+Gbqr9HW)\WgjsujQdl=uNA&oAt(>6QV=^!R+)?dH1lMHP*qh]2,rid"0WLf?IXT:>(K8.WTL:+Ehl)?q@Y-IRN$nR9en4kA4Zc>d"`95?)VOq"o6/\>$?n+XXMAh+5ELBD+>),_q#@eq4gc[7gBJF,1K#^hOGRt)Pf@_>TR0Sr/e3ZHQEZ6AB*HnbNC8<="$ajZ1%cNhL"n<5A-h+il(s;b8[uf4D8Y-eNUiQqjL_OQeJ-+XZjkM=O&8@b*c!KL*7nXWM^e]Dic&10@)5.rfl]5[#ar4I86@$OffKDj1;p$?u@fqaUl?DGJl>J1WpuuipLX:CRo332D0T^+Fq4ipp%ji'R)kgq4p5!ch.SRm,po9:\)Z<(l!:S@qUkLrJen-Dpa;c!t=RYVp9kT6!G-u4nli@$g0:34-s`DJ!k\o^:s;HB?CKp82G\H<_4rS'kW;!nim!B@Xqg#aITh50Ki:f"^N5MRBsHZ*"Ae:XZh][TTKP.2Q/1$GG[!c)N3>NUZPfkEd;eK?-%AIa]Aj6,TnS^pEGf1+m0DRPN"9:0&d_45+[.`G]st@7cZSp(O;e3/qPSe_nCl84f:PY/fL6#`eRE6(Cc6O&j0%O0(k`p.TG9s&fX#Ga\0N;MR!kBcmA*`i`H#=iOmfb=0DN9GouB_#r"JHfEhY?&Yl6]RN3.+n%b^ds9tWDQNfc_-B.Q&2f2LBF%j'NO>=Wj1_n=[h!8ZP_mJAceA%AdN*>4E8GQ4f99]R)6e("8+Jnio$26+qhq5SZN#/7HAqk/I_Y?s\Ng%A@Cbl(]^T;K'@aUBj\Z`1n2CCu/!4EI@Y?,_)DUc%a>_)8TG6-c^KYE.Mh4Rf[G'od8n;8V1khg"&i6EpX!jpb`,o3=;:meMl$D++(k6n/RAiYB@(a%Z0a1`XFR/WQ+;7RXdogXI)`hS?md`l8KQR`5H#[upX@_^Jq=)YMKI7p!=]&q0.Pn=]cn$Vu?_i7no]h6h)foQPb)c7kj.9)[=E/@d2ds:Vendstream +endobj +428 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2400 +>> +stream +Gb"/)>Ar7S'RnB330.qq.jT8X[K90hC`Uq4?2_2?JR[*:#$LLB3GpnE^[I8qgG1(e0%+X^7C("PF6E?i=qh8A;L'99!%p`[ISGAK(eoQ"RK90&\/Lq9F[C.Ae(+fR/I']ZjX2#q]>\\N3sh%lF9i]neJN@L(fF"J+Qb@@4X$JdQ=DWg1u"\Q[`!uZ[frX?Brm"5!S>[8@Ak"h-+e@lf#eWMTVZ36ksl,gMnMM:dP/K=%3&QD?bUmoUSfDf!M_kfF=M*T-i"P<.:lD]68cmoNS)`CWbQIcn_=Yc=ug1S)RFROh\`[Vf/0"m.X)i.4PA)=aFP3BR6s@P@9XV&g?Ouik=nl!_e'Tca>q#D1;Om(Y!j=M(J<`D-DlFXb[:uMiCp7,JF#1NbhEK-4Jn<0HIce2Qc)@!gEJ-)^,`%=9t\F(*,Bb5Q(1Er15@N"YG4eb3c4jD9YW6K2aM'(#H_GKGNkSoQ)4%B^7"ukC;ZqA0lS6[]r(q-"b;"\C2]H0Oa76/sV$`CUF_WLg+T2%3Vf(I0?[)=%rVIkb?U$Kc%CWFD.Y"Gh780`52a]YYu22\EWgeD9o:)An`CTN`>5s8cqf!TZ6gFPm=ZZ0[0;-gV$>hZB\gQ-SdZO:GKNg(;\HKo1F<5t+$FP<(+F:o`PpdW@fGh5<*!+TeJG:+n)j"U7O:YJs]kW="]gMk@,`T[0W4!DP9C><[7lcn&7S(%Rbags%=WO2s;9U@5Z_b45YRc`u$i(Hp=$)Ym/^c1=!hD@8rCP8-`S:WAMTB^ZBUL-bQ':aM@m5kKJuUun=_K+d/ZY5&8KgL-aPW.54D%CV-Hu"LVV49.Jt&3l?PM;ZN`4NuB7_)Y-oXrZi21)S6?cILRrgHdFuTiP7$6lR)nBkT68!9QeI>g.4UuRV\OM9'q/31L.8"-nG;>`W:bn0I:f?LhLCcIqrW<.&f&!SM5bH/Yk6#,#n!RF3+P$aGgg%SpU!Ug)ZJT&\@@uKQB][LY[!oQnX<_]oZ+0jC.-Tl-KqAs"H?DO?P8lKMU]Np4S>C=9`(mNT*AEQ=Pj!Tm:,cq]V2M0sWci$fB_QPe0E#XMKA5gG#:Q]a7PRuZ41;nHk')4jp1T5Z=bu@P@S%GRBAZHj$d?9jM5U%01dk94VHHpqdR$G.FqFi3M0`5/O?c+EB2749OaS=a>7&5&(aZ@WE^WLm$8kRb6?"A721=YJY/g\#aOnm&0?86Y,%b#Jp7m$s-9*n@X5@`bj+OOka7Fm^d,iKDO>BQDb)V=c]0Be$;V/"YTbf+Zt_K17G5:Ud6`H34l5LOUXag@6+!UnQ9rHnKMGU;`5?G\fnAaet"[_h8F0!QSfK5bdO!UAIW30gQ7s`']%99:$,SGdI!;qhXYs0Z)$@.Rd3U"P(lVF'\StAFdL\XSHjd(>"R:CA$Q^?BXDUb!9LR,/uDR7FMSpFo1L2ppQ+,q_cok]p9V(+WOG`[eUR'*W/6O8L`i%5B3Sj(B$b@OU#aNeNjf^Q/4AH8K?d>Gm/^_^=nkNH"la#A@'f=tgt&bM95IA[HiBrRj;e4$jc<6M*'R22?Er`CJ)3eWa-*>T(@QAt?bRo_OQ^nDu\3!Ct#iFQs)p@V:;/#m-44bJQS7E77R!,?&9+JB6ck)E&#Cf3^Shh1f.ljBposT.0~>endstream +endobj +429 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2929 +>> +stream +Gb!SnflH,m'n)^Xkbf+PU+kfNm['.hc_TXmDPir(0Fob@8]&<\!C5&TfB:r/:4fO2ZK"i>$bC?Tp>+$)%HZt>d2>%ESI_Nj;[f4o'Z*2?HmRS_\ACeR-0k5'CNalDWHd.@5r'91[`WG-8QXnGD4'GcfX#(=gl6miYHV.`'lP'j;ZkcN3OJQ0I]u@-80/"hlt5+D"=0^p^r!CM?mYCN?X,8JSY)jn$FY&4k&qL/DA\fCV-C,K(%*j/6$*VTJ0XJf/,5b'T0iD4L:JW&_M:!)>m&Jk^Stu*b1R-3lEKWGa>AkZVQ_>JsjEEgc;5QWAfD(O*=u]KbYh=0/jFo=6E@oKV`.G3L1G[Eug+%B:P7(T,a0[7VssfOZm"W0@[t+Q9+SNN=@>al]&ZL[(c,njh]]RgI(:XKtkY-Y>m1Nl#6%+A%&r[]&4b`DBVT$O4_e*l/WNsZoKnP1T8(?K7^Jr%-m:5F='s`g(E0\1qOuEn$:W;fpOWAlGmrgIW0m+_"6])dfS;%Qu$5s8`Euf&l-L<%0((ZBil2WL*%\2@F";UHt7:5W?p%<*]RB#+o"sH5YS=:Q%L8Q>mYI=[JH5#'P%e)'?L;<2:eM!U[:[)5\:S=!7oKLdn?t*&sY)_G2Y2Tm'R0\2F:"`]]EEJ&gIflFI:F@!1K'!Y46rM.=9Y#2D+BEq"J'1/lZolE/t:Vsd\hODAV;7I$Q.Nm.Z]@d-L8]2*Sp),ZX;15]C?3h(!;U!A=.M@c>=G/+XI=C'Sr9L69HI?WaNP(N;XIFB-f4YH"GB/3',U.miV`VM$:<"'@8SP;5iBLDStF`-#PbgR$T_?7E,dU_%k4jAZgpGXYa/?.;#?MhR;knYMM1;\,W@^r_':To^PW44F4_<#)=h'.)TaGQeaG&3c0uUaUshiPuEF9][.C==WL%@6l$KBr-(=XPR1'/eR\E\b&W#C:7J@>X0E4pdnf+@s'nSM!P!'[9[2!FqoAV+AllKMl1BV,0st,"n5OVXu3#Jmcsp<9tKsA;oMOI:(fKGlI>IgYNJtnbA%FNn-&0&');JED\%A5i-WHK^B7Y@1K%G4S%aqs=8G-7)n5fm5P2VQ@JUjLQ8-HbOSEs]7oEV?\8".@dioHq?pYgpkGu6c\uQ"fDgT4FTr!s-^n<)YU$N%:Ru8sf.BU@l56rYJBr__lV[;`\3'-\\3,atjb+b5+bSQW8M"+WG6l/ESH@dufpgEBPaiSu*3%O-Wf7D7SHimS@aXg`L'>f/$Zhuo\W&[;>o)uS4`(Ad9OIsuRpr5otR^i^!lG.#7nlZ_lOrg][2*]BP\q(59-fR(B"mea8P2Ig_'K.N$qtr-E>,$SiroCs8ftq^2d&?AB4i7b7kHIDdl1u\6Q`G]=`'HU]di-]9F,-*45'!?(*W>9PX"Z7VgbHEriP,]ZYc,i9.5KudjX-e$MEjJcN>CB$N%iZZF3cHR)Njd#B/MVOZO"UU\:2ZWRYAHP0<;nY<<~>endstream +endobj +430 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 3414 +>> +stream +Gatm>=``=W&q801^n[(F*.Tu3-TeZ+bu?0^fkC-%fAkapNNUEIk0b'NJWK9G$RdI[7$T"5;?J\Z3a$eX$eR8ZjQ$<2/p5ig\".;RX.kAt',:-?<&3fQeGjhRClC>6IE9+!+m.m8E'=5-qnfH/$!\nZ3T/Q7c6MtAO\cI`jJC&g?J1Ds@Q+X[]$sGd1YVQj]Yg9'1Kfh7_d??3S;_La@)VT8kjGX:GAuJ263ZN0L64bMN5\V"GD-dXt!nG*chuEWA'iZ@20nN]NpkR8@o1=a,&-1AU)^jAGkBmK3"?mBU?uAA@Q4-W4S0ifbL*jisGF0ehkjoE^.t5(#Je(mYT*':PDe-T$=)-A3B1jC*''1o#_IN/h[K:[H+:3o)kLGlt.5a8!K<%(R?:HRSn@qJC,COcS#Cb/.7:pafdlQQ+AmOgLZi?K_-[+FeS.WBXK/+A[6&keuAekIjP[$Dk`#T4$Z@DAkRls@2meFdGd/.lp)E.T`*--fdIiQAdkPuGid9f6#X.M!_9>8L,(TF,m_Y99ZA+7`m<.cgb!tNnJf8s[+8RHp[T3!`n,fm4QD3G`##0g+C\nG%<=6QniBSiX\$%I+U@!MYL;GQC$\d^(R&gs1.tK-+++i+c%u&5-?&O*gn7]9C#;e)&WTp$E5+9dX@c+'[=5\U/ARBpPC*jZt;/^\3*]5OongKPdjmqm0s#4"JFt!P[,`;Z#b+Senh[AuidcHZ<\Bc_]9rCH+&X6F^G(CECFOWLg3?)Erl^[\\o4>WVTX(o6mn&[KQKV(OP0V=4'77[,RC_m<2dR]A0E.Mr*'o_DA@d$MLEC1_hF2KUIJl?:5i`gNYb#16GgJm?FUnW)Hs?o>;fPr+'kCOa"N&'h(aMYEc1<=D)'9qQ"hGLQ0Mdr#*DA0NU.&TD`Z3c7&WQU/9RkNUi3:8QXs\'I%6#&.>bb49Dj=C(]%+t7WBtfq&,EqE"2mU:Bb-7aM@d'JG:;Y[Lp^WADI\Hpf%V$%u%eVaQ@TQXgfB)&Df=FKEjM_i3gLpQu(AHM[hK9-4Ji33&:V,m=]E7fXU>[?_=)Vb2gPT!,cB&SSW6Al^YS\8cZ'Q9YYgQ?sCj_T7E>3X=h1T\Y17\Jugji$ii`_2Bo&BtO]tLB(M"60L.nN_R"HT*k:&TSOGo&bZHG`Ml0DoO&,`236-1tqFP;#2_O3\/#W?>8"R(^esZ4Y+5\f]_eQu9^K50S#q<\BlLIW%cq&!#\^$eU7."&`ZT2.B.o]d[]`,`G4K'GUn^pe`IgEie0cE*.8'F2Hq["l(X_1k\B*Z_L)D9*r]@I%sp)mdmXj6RrXdgZ3J=,oO)9(;LnSM.>V9[RO\A37q-0)eG)N]W_YG_?6%A=KEZ36Ej]nb8dcud;!G?&8#r@3[LP@Rb4!(V29(gB!2!q%PN\g:@DCXHiR;YKN)[d7I(LHdfcbtAY$<,DKqGYn[H%^#[MfR#>#QWd#QLtbp4ie%!=H2A\bu>eC,OqIHbs3'*XV%mc^9<7#aYr)&aC$Kb1/Lq*;1__oAj%B[i!)*S_H8"P4+uoDk3Cj_W,:%N5)DHman8!fb(Z\p-A'HNp!jJ)?i]G[3T19@-Z1-tXY9>]teDBq43Uh*T*?MG0)*@3a76Va9H$@q979OTtmCW/s2#/\QQA,liUb93G1n+I'lW0;rI=HOQ,;\h/T,TYg\tW`c0ub:injQ/orQKlNu:.Al]?0'p3YP:D"VEcq=8n=S4L\E"@%h^kfU)4ba^(mHF%s.VB%X)KY[9;j%ljV_>8Mu?KhoE)3#Y/m>!I[!d2SU!YI?[&\KDeJ2qVMT=j[3^j7&U_Fo$`,D:Lu?$>PQ^DMJC[$$F:l;)t2a1HD'oQqIJ8\j!MhUGg8cS>p]1h.]J_9..NcqLp5-Cs:c;t@s^H%P5%ETlJEi.+"-N)uhJMCGa6)c\NB@KsKsbX,Y7*s[j:^&~>endstream +endobj +431 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2381 +>> +stream +Gb!SmD0+E#&H;*)Z!Z5$2CQ/cHTV%2Hg4@0:HD(h,bEtQ5R1OD,UsfXjO\VM,_Q:+0MXZm%(=O+l#(F3kFUKm(mt),qDaoEh"K^G4Ti]t56b^=2Y8*L\,O@](T_H;G_X#NuRie%:;48*F;GYLsn4G_;WJ*Sc7<#f.!`J-Ab:`qX\9=H&g*l;]hbT.gOVm%e&a9eWF#M.ocCg@/!W&Br1?Q+W+HD1p"e<,6D!.ut,3r*E&IbN$NMVH+]Qg,uMOhS[Yo]k7k*ms+DV/B7fWi2Bknnk.KlJfIQ/"F$s+"W4N9iEkTm-BHG4F^i"BX22T2kCc_2h)=Jh!9lG?H!Bt@+`5**WeaY\QgR_]f&V'RO7'ddY#R\bqTL=!+#GJ*2FTq,T8'WKq1(7^^fj2W>5]SbWRXBRjbiM1oEMEB^;F9?DgE&])b,lU(b!C@sb)hD!hJX+[Y!j?UM!5,RUl6:,J.?>?mbHi1^sF$phTA<9QZ?imO[XMe!N[0SAId+LNuA>IR9EFCL!h"g60/[It&[gqpZd,#4g@q32.cUH1-#XBUl+"k(XS]JnbS&o!%cUMasVTm9@!8#b.a\j,]aj+ZG9Ji9XPf([qV0+8)O)cFp=[*mP\r2h'0od=A3[TRNOCU:4F'bQJ'9h)3a$'f^g!YCAk]Rhs`R3?I-.Y2N\f*P+-n\&uH[='[2&ufCYa"(+?JH9DBen?Lk_\L90)Hu?&\I()4fS4EE%pCaCR`R!.juE%dYcA.c(@2]sI?NP.bjZ#FR,(_>\fHC3/3Ug;mNW,6RTRNWh[BR>i;=#C6)Yuf^USKG8"KPJHFGU;0r!4>[[a3ThMIt#j>eBnPkgOH+VICX1RtBB506'8"pP=bC'r>1\_,"K%Kf5p3&p.ZMJO?aM;NmFB,JRC51*G]/*%i;M'!Qc@s:*F;^e=[*3frg$K']T>u%=k,#@OPX)"\MBn?ndYl$`c\P9$0Ys?13Is'CXLrX]""OZLfoK=:R\&b*lktB:*]$V$gq9"u'C8,WP'>q;Sa#HCMD]@51*XbZ'I*FuUuZe)I^VY^RumM)Yqhg:(a[%TmaHj]LdGN$La/c2XJYC`[%7RA_l^0/OQIOBlei]IhC&F$`,*.uK^"D$JfL;cFX6WG\):`+Vb2.@LLm[R*Ak%oU">0Wuhlh]Rl\o;&;&d2d_m*mL5^f-FW7[d2Q`Srcrlhl0?Mqoc!WX)*'P?X83nf2*^E$+UaolFH$U#QC3U"[@K;X&>.;erf87&t!bQ#F?[^k)TA&r^*D%tmaf3l;YK';+GEh'/?+q<*CRDFr:4@W.Ce9[cj0d^5Di]nP9Z%K>e`p<`;gEq7!Yi7<*X=J-H7aIOs=FlB3"$IAmdKseqF',JF9hTpM/;SC0D-=q%h5\5iViRL$AGXa($0k)3-g%PAQe:7A`p"`@B[mB1EpJGGmLdEtZdhI_T%keu"7u97n$,\RZPqL"ejY#B?:J\;cXQ2JZHc"EF]4R;HrUu!!endstream +endobj +432 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2897 +>> +stream +Gb"/)D0+Gi')q<+Z/6bG&e"3"GF-UrC6RtCP9u<1mEFfY^a(J;V+?B;XLQ%E(*H%s'M6!G:3)P\9o0!<0)!u;m^"#>JfcJ'rW;"u$K&Om5]rIN!];hKVH<9Io^(TU/ULZ]7WcQ!_(hqeL#]C_e"O::El$=Cq(FC9[]MWg"jQN%=(ds*.i]ful23)eOY-Q+Qf)@Y"*@gi+m!\=r9TuaO\S'/\:EJ&mFq@&&Bf48:n5Hn(lG)c$_3IkSt$K,(Tq08Ao]o'%5,[g(sgLd"\c*a]U=_V5Dj5q!FlrIQ#5\$F@K)Ui2Hdg6LG`Fs*)_<\l,/\X99O-6d*!Dj_6tCE00%=0k&)&f\#JK3$OI#]\EO+4[uA.7,sC3SGO_^1Xp6mGNQ19N*"q!Yo*/ORarTfg&/0=lTLAYe8C>M5KNclo%&^o-t%"4#i8_*eP%7qiX7nEFltaTeM5h3A=a0!*Bls/<"i]=ni4D65-@oTSC8A!*Br\irpX@26n1\+TspjklRIq^s/YHnGMf_HF*Au]c#I^E;)JG)r^g*+ME7_E/-^GiECk%b^Em_6dR[W+Ob=DAJskUCiuTSk2(g"'[Wqu+MZY4r0L0o&[iQCAaO>=ZZGL6.'3qVm2ZU6M,Abs5$fRM&ZhuL[&'"R4^NZT/`;)r;O3)0$qVA1(RF#T(jhY#*G'FoZZW`AB^P4QSL9D3(dk8gg`Ef"Ppq[WSY+!eIo7)Z6S3d95l?84aUiNZI*2Q53&i<)"ts_(7BVB#@Ic.mnJDt.>q0':!@.&f.P-MH>Yam/d96T?)IV)jA+6l)8er/6bpL8S::1@@$InjI@G\goBq885MKcmdPPYC)PXFIq?afHM@k1tIQMfc6,GF6'4dk8PcTCb\kg`oe#>%@bj(MifSUZDA3k1\r84b6bL<;[09NqW4*(bDO\cgTJ1pl(JNrl1MG!q^g*=V\`!p3Q^*2BBD;Yu+iEg[@Sbo4Zal9qJRDN#L/)_C1FcTa7'7"!^')rEUB[#YnK&gd_V:iG&t(tOoAhQOa$0>mTmCk;K9d$J3-q@5us]=&>glA;hj2Z5@Cn-mJ_Vei:0Fo8PJs#^*VpBm":#"kBQlSjfE]b@MN?JlrGe:)7F#g6Q16gdQ,YE_R1US=>?h3rFb&un4-q:f\6KbR&ZFo6:-o>STbd)(t:D/m[7i5MT8/aCY.0GIGJZWK5MY$+WGiq.8-iheo\>Hlj8a?p>Z6Q*4kI4'l"=,\GY6LFJrf>3jIKSr,[4/[r,+@D=9Z(m(>OJ8RY&"/f03'F-#&8m8&`6P)uQEt\>(Vi8sc&I5UbgeOoKG28up'PAPQat=EM?oB@$XAd+"'k>@.[-4P\Us"M8qnBh[,c)->5%#0qo>Gu%B/^/.%j+g[Nt+\U5g&$mmYYl9N?<5/!Z@Qh^XoGQ--u\[XC'kgZo!u.s[4;,ia''Wem&1:o1+L=N5)o;LMk40PR4NhBu)+ji?LU?-_maZ:`:,km@6354Um7UJA$j[tcY4(N:*5&JYl^M2ku>m.N>r6C.4:"'k7ZhRoT.3l,?[3bBP^TI:`J@.TPendstream +endobj +433 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2752 +>> +stream +Gau`UhfIhd&UsI]JOa"kjq6P?bEL8S46`Q38Y^!,quW"BKp3hs&3NDGoRH<^5`(.^q;cVFN:1;>>V6f(hgW0F;\HqX[@_"taJU?[n43jsA@X$D%UuaAT)A`nSA]-[9r+T"-"8kiZ8=Y-)<>7NQBTT+u$Ro6H5\H1R^4=TT%%Pd0Ihm\NCYsofe@iMn_*(c((8r?DIENF&+44$CNrPl?>a:(X=YH<>$.=]Lp0DU?oc<'Jl.1,=ZfI9q\)#*M(cCu/r+K;oT&8r6=Xu_p^XamftEKL&^Ne)Xs^m=nU1ZAQC[8l)8Q1crr7'RUk%D@X??=WO2'HQ$/(;PeVo>rYA<^(I2G_OK\l*8.QNWZ;t\%B,)kb]O6Jh]O='[jbF?[=oM^Vk=I]+#C$1"69&>^GppDYAf>3YG2VWOR9gHukT!rq(SOPFdf^+sL9Stf#0:3V#@3\!j1etsL/c7HupsG97hqOOJHj^67[t0&l.d\(If3?c3S#DUfS97TrW55C&'[2T*9r'Scf_8/9nDVjpAbOm#\ji>pSuF3tX-#36mL8>a?Iu4<`kdT&[1W7#DPa;Y=Ds+HWaCU_,\P/:bmUal!^!TG-:s:olDVCDI3";7G\t0T7X:*p!HjV^B+YNg32Gn83,SBm^ffQ3+iY?Rp]^2@U;in6lFo*g7LX6'ACEe?+l'jK0#@Yti[N<[9Q%C!"'GJoO")l:RiYsj&EEd+/Q='%[4G>8)CuNg)=1]2hmZSmMj+O]gZtmi`C+a\;B,imikNa3Sf?=Y!&6XoOcdjjbKKuZhUZ*r8YI8d5kS7p-[bFthZLj9,TIk.7L9_as!;A)".DQGHD;9*="#_EkX!%V=@>G47L;M,mi?ID\YVkE=O/Gsbqa>ilCQO2;GnqR&W"$Nh;9@8=Qe/3>EmN;pXkAE#/^P5I7tN(j:bnqE0r<8n;U,;4eqd]qK)@q$$4GX,>LUDd9A&Q>"Ya-b-Zd,\Z>:F#-FR^qb9QZL6*MoTFo`dc(?)7mdb8Xq%WJ'5+M&s&LL5CGKMe95^FTKo#:bL/q[h*eY`WGKViB`Y^+3.>9W;"(#GYonp^/9HO/;"cPR_b3VB>aitHf7\HC6_;2GP_G.]qupoCmU&D^4jhSEcZCHZeAUcbm#u7_t0;12S9d)n*9l1I)$JP:&tZdWn@GBl0aeo!M.-N"#Xo"/MY8iGIU(t"4nI&Fa_o;QhsPdfp6c!kJ@^T*FhhmhMuDcT%qmE4,9pSD",&Q?*AmmIX5X^97%5-Ugu@BZrtND)Ia5`hhe,`Q@ZOa=9Ma0p%M%9-kD+(HC,,sEo'[^O5AE:]IVWhGKi@Xpfm2^\`)8SZ1=OepY3jr.GX@?ZKK=0b:gV@6Y=HHmaS2N9ioUn7$V>uPn#MrRTC:_]"Ki*pX7?Y0@>=6Rc26s]B%-(_!K$cr@CUgXK07.O\G-@U]fB!4`Dbc_N7X`K!d\c;eBYE$^AD593/Cr(Z@/4mNl4E,F^daYlJ*ahk0J+Bituu>jNio4469?#%d4[Qpkpj2,&ROBT[a@REBF:Q[D;SV9C6QiBot1K9`u0hftDe7%pq@kQDLl4+#Csdmhm^7H!(`Y9(9b##((-e!EpaGV^AbIpbT/hA+&0A.LeoKgF''cWuNq3K?9,\8Bn5u+c=)74>bXVajOmMJTKLdm*](39"2*(M5,~>endstream +endobj +434 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1842 +>> +stream +Gb!SlhfIO1%"@qXJOd2+>L+hFIZOO^*"h_D=_<8%)JKK1]/:N;F(6cK[ra%@KH]/1aLH&#@(dHt>[nSOI0I]jKNq$MEp`ATi!&OB"86EQs0W,)n3^_+/id3Jkg=_<+o#X!T[*'IPW-K/XM]C6$%be=p[*`0;OW.U>q(*$?j)f2jdg_mOP4>qHCH62fk!uRT.T<8fTL87mREl$,#29;2)Jks=/[`!2C-op_po8Fg$T_>_e5$OpP*c_JY[:U?3V"[pmaaV(T)^ljL\NQFel2?6nW.ia:aO9^jG\"3(gq![A2Lr?#GK.;JAk"@VkW8)P,O39O1;j2S8@e2&I!TjXp\r(NJ@@;#UGmT*0#KM/^i!C"8P`If)7Y&P/c;NNClSAn'Xtq-OfJmk9prPPGs+RUWmJUH1"74?i:+/e[V\UW/qp%Me;2phVZ4l(+cDJA"fO&]WZPNm5;H"5QbJYH=@q.-2TF[XW\okmC5T.U:BFa#d:mpCLL(,:7$KBjctH;YWTPSQDl%HPW5pB:E-^mo[6RSR9iD*5aM-]*>`g4Eh:onC^tmQ:ie(VRVZ%QW/i;pendstream +endobj +435 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2770 +>> +stream +Gb!;ehfGR$(4OS:E?@Q%IeZJhn>jO)9`j]4lX^b>f@3tS]gdIR/I&lDdi5$h1odUq,5AB:g$tP87VGaq5,3'cl_f,".\sr&@b2(:LU-7d8#C_:rN8a/b:OMc:`i]"9_I=!F[hE:'c*p'(Hl"IMefl/i_09kQm/7hhbb7=M]h&)Q2$ACaC$+J^KTWb]K%]$^F9X/E\=ZZV_^,)3jugHjq9lY>g>l[^cMQa&',OJ.'S4;5eS>DpZlK(nj'dOf*\)MNse3?pk\3?H]n3(=K;a`o)+5l4kQg?#IVZ((b,?Au/i06,aIo&uUOJL2sc>;$<3.9eHgAQ?nBSFp+Qa:iTYf.(Bn8$"C&'%l>sp9!R(o83t>d5n4Ws!`Z-XT[H9B"`g'#IR%8i3>af4@A1(?`1dq]nIr-O/d_E[:hCp\-*@o8-smX^Ee.YKl?/Jp2IcrqC-?2'V,4/QO\4fY2V)K'b37V7#>O`UTs#_5p8H#4;T(Plh6-OeA%D'bEf4BNo(?9\`_rhLs`iS-3=Ng+pY]'5#(*j9FU/Z4@!5U7#=cL"'LKqD$S\o2u5LI6Ui"&oM4nMG$=_L';=[jTf/pf#U@P&\3#jPp\)I>RG"O3diq9V>[]+M\&*:>B'"8,mY:AOoR&a:Rca;_,K?#cRP%#Lj>!S\]Ap+Za8msLS%*r7&=f+QC!2R"(AL2'VDsKTX;AR3'#m8FKt5/h@e&h70"#N4_5Fm0`*1Z.W!*2.MF/+=NtY@AZu#2YD/#3+Z;M1Q48^'G)s1*_M;Nj_P;UV=lij#jIX)PXRcG[Uq5;]q%^",`"mS8gN[TZhC4=VV%3&/f3ZY506NAN==;R78dYl#5U7$]A-_/F-A08C'THWn=,h4*,S%b2%1aP,=YBn0tDW%hU_9CC7b@[=4R5)It(]7NXW`2naQCu%i<0>a$".gHeOqc"?r5>V`j$]*]rbWXZ:f&%D/CqO,%7)ElTO]oSKg0*@[:MXQ>N:rp(Sf@X#fr4H_I[@t1[Tm_-?@Rd\uVJdV'8cXkFEbCf.sI54YgM*Y@B0M9ehE8Pk?=dn]'C<7FPQr8iVO!dGn!S2P=+B2GiH"d:"gHqF-ORp0bkCjZ:S/*RRf@3.Vl,V7lkYd@`t))fdm_<&5[ea'pb#H\p'NqQM5@&@^$X)L5pX\'6$f1+nsBa!r\ApK&=k9/ObO_gk3;=+:^f\\]/mOCiQ7d"BQC903&ctV.4pCkROG&pt.3CNIpX;\gu2ErG7l?#;,$M1J>^;ma8it96P$X`]Q2k(&tqpO^MZ47!.cTVA/c]7]qrQN[Q%MgaY.[#$71t:7o!PLE?1:(r`A`m$n@nps2R$%,,IoFe%`#;;cc!_-OrfIINAN>&P$fgr9n")>2O5IU3MlRAH2Em"6ZsdIu)h1qK;6Qq(ESgj4b$3"M>qa\[eu,p*(Xbd+jucbXb2Ci<Is%Wk%6gt\^n?(\@u[3?1(q1lf$tQgnP?@]*T"+kN5'[j]i6:Q-H]3(I"O+_!!><1Cr.MLAK$-iP"!ahJ%3)EFi;unegoC5ZIbB0YkWPrF&*4ZHY"'b8'`\Nh_M]a1^![rY3*krKB#.m@3Q@Ikq<7)8A$.+b-]H8tQCB/ejeY`BkkCPkt=:$7c+_GZ-KaVBVgeB*%8s'$L3HeSIqto8-,rW1t-endstream +endobj +436 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2164 +>> +stream +Gb!#^hfG;G&:X@\TgpKm'SF!Id4&!+E*6!CRq8()PoHioo,/.OABHJXA,"#l!F>EM)UCs3eNS6q@oN!QpOW0dklnHO^-l#Zbl)+E1T*)>8U@;1sUi\>(+7!TG;so5I)'(j14u\J/GIL:\./X[@ud5jHH&(,meqikb+[&GYh4D/rl+,:?\&hJID9:d&AOgH*kpZ#^MH9"*bfg"mTq`0&LkXlB=E`6O.Ug?)tct#MW)HquJS4+ibhGm;k02%eV9[MX3H_A$EKSdiaQ,aW4l.prm`7:Ck%,GGAVoR%biNp^pkG#-ULu[VbAG8>W(#)`Z\dl0c",GmoRklg&Dp?8?gY+nYi8#sZ69LYfiO9X^s?)r2h!&q&T49*;1t2#a*`B?25uZi7^PaaY[Ab?pm)^H6EA&u&X=2%h\@Ll=l,X.>`5ibCfs$$J;h">V)C6j)!4/3a:CeRGl?lu(h0P`cef+XtOs1eM3e*GgRq4$`lGaA")9:cg0R,pX-r:9h\[7JQJ=`6>F9;&c=d-%!#3H--OrF.7W?I,p_<7^J7uCTDSQ!Q6&V-Dm5t[GKSNM3nl.-WoAi-.II3`#Cft&)lW:+C5p\Q8ZE"abeY<=sD9:.8=;CV0-rRRY?j`XY;5T`/kG"R>2@%OGh\[?mUt(g`=%RoPr88P\fQSSW.6dj:k7GMC"BT#LCIYpc'[n[-8)OnK`.3[4TZRh?:hkG=1o%bUc$t;0.=@7]kK;Cj61[Q'gWHc$#*B;QXVXT8'i&[ZQ1kgg`THMiP'JMBdeUc(38eV@.Z:nba(Y>K`qs@ao/9`;0-@R`*P?0eXjeft+_T^CC(pt<\hU'lIO-]_9-H^((MFUE"lR7Qhe)hfI_8l/J@smt3H$3A4N6NA(ZaN0Gm:3dpB.pPT0-D-LL;qtVe.V6(@jtl1Bkl$SPLfQe`;eGp2Y?7oOlM&^73Rt;@>o>7N%GrWn6).-5UZXSX"rt:<8HK;;?dfD9QHITF[Ib@P;4dsaf#Ci6^V)TN(]SM6-0DBZC]j).iYL+lt`0f:Y!F_rh)]4l5$'=f=GOGl@u$9l]qu[2e'K2X(Y>c!i1!`d84t,WiMH#X%nOpf^ed.H_BMiL'*\oT]VW=7-+`R\YUICquAUP1aIsIJ(#i@O-T-d=t;.-)M@AgY]VeQ"M5KYoe%Y^0DF>]el)tt!ot4"=6r3+cU#@$]Cc5FhfR7H4Dp_756(KV)0cF*ZV:dm5o.O^p(m#c&5hpi0$3$pP@QtDKPC'S\1FJgX7>JS&F)C6JL,BXSaX?jeL+_sq2T?_YPro>2tS2=^8&aA5PTUrO0`3bq$t@1jdfp"ZT%etj?m8slQg)GCXY?,'K`[UL^$EuWuB.`*bNqqS(@&])dNqJ0JR$tM.1+.[!PkHBBPssnoM/M7W%(G4#lp/r#$$VfK5WZMhjVh1bX/#Oios**9bZr6rcD#Z6GmR""@J-j\,1gLm;j:rZg!G";WU_QHF@k\])_r-2g^]NkG.RP\'[)N(Fq7)lVS.uW6q%A/;B,$lOV=4G;BRECUNd*d4B+(@#.[",8m)fTBW_P7:4](**m>!lJ7QC;5p+YP(o+ZGXK,3M._R\DAIXS.=CK^j,r]=M>!qSf'5/<^T=2S(H%4b>C6=CQ8g9lTJ@\Z5mI_AN=0&H'a3Y]DcnVq?Q8!+A3ACr%nd$?,)RCb"JYJC7:P[$DX0dabNAdQNUFMki!MdZ0SJL4^Ug?&ioaY\Q<.-oH%*_/FCKCphPG",&Zqg%C`n@@$)25DK%2?.UEBrR%X8f15Rsq0f,/fD7ij5^*N.jS9HV-J;k!s&%RJ%bbQendstream +endobj +437 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1664 +>> +stream +GauHL99Z,/&AHJokbE>CMBmida.:K#t$fY\I/0Aq+T.fVYkA6q0%CbqIafM^2sFnaLNBEf0#RD(j;!Nr?idgZE!@6+W?Lk+_AI%Hr.3+En!gEDecP(RUC_XS:'Vl0.DA8rBY'^(?%&;m03ZJ^U%6pfnNr;;KTZ)*W=!TFC)*19#iBpZW7+M:,j"Y#8n8m;:.V7J`&CK64OYgPJMnU]p;?/l_8>jWJ!K]q1OpXDt]HX$:f@/:n@k:hb*AbcL8^XGXZAVq9H3cQnuM5"M?r;:%F4FBg&Dp8qjp3.A1tYb+lR32XOZtA>I29$$Kg*/fa_X1ka%XPr[m/oa-5mctk:ZW*Z8(q%fBDZu>f]Un6BgF`Eur>-B(=J:*`UXj%)4GQ2lHq#o7UN<1.;W0VT"JTCoBX;j_'-fM^e'T@C:Tf')s[WCC?KNciBAlQlF:JMkA'g`^X/s=)n#4?C;d$d`V"TP#R&P7*uYlYtGo\pN'mUQ^djY^]-!3Zm&-h%%]tXQRPHlU:IiTf-L%CDO/=:k#G6hK#WhT7>+'egM@M_,Se-^QcD(#W%$b:,L5qXkP7FJ)i;`Pr]@GLdB>fN@e&n7J%.,%YnZo,^!7#u5F;_qHQ[?^IrNTD;MU'ba4;*B<9Y:hu@Y-qZUVYHPpS8SNX0&_Ha^o*(upiTrYWPfr"MoZOOTl7hnUCO'oq5(c-&gnSnp>gkoJbcXX3dqDVC2IrpQ6/dE/4]Nmk/?3Nr9$a`)\Co<@IXr=ip\5#uSC_>!5@(IZ[Ee$A9pKYdU'LeK#hd4RIEBc2P+NdZ/17i=U\/p&UWcXQ4a>8'cKleCDOR,CfGhnqBd_=hkRf38WYU:Lt+9#elAt,P4*l7C]m@;'TK5[_=p^I84&TB.,#k5@u$8)mcdO_$9g)DrW5^LrphTZieq*OEm&8bHIL@6\'l#n2sP06MnQn72S=9_pR]k@U9L(!Ht*e]*2q'glU@0g%^a\,BKQ!fV+J0p,FqT1NG_,XUI9e9neNNg4OIsScYY5~>endstream +endobj +438 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2069 +>> +stream +Gb!#\9lJcU&A@7.bY`4gQHO1n-c^(S!\'5LlDZ@e'tKPeiJ5!Lg/a.2pV'd.Z6WXF*;E>9TW$smAp.eCRi)P'nIb1m1dnM`o]$lV,+q@n,4Zp]]VV5:0E-8@VO__lq*CANPfBE/_g?XpP8>TL&u=TbOUu4nB(Z5R`*NI42@pr-J=jOh+0b7fS$')7B3"sP/O%SAHWKk^T1;j-H/aOOT(.02+q90d)N$(^/I8AZ3PqO;[t"c]K?.*Eop0K;YEuX@buuS'F)H,+do%4Nnj5S8&TUoPFNm;,Whe_liYD-l;)Bkn3Q.8ZCQ^#G5lN4tETOYfP9/*En'A7<'`5LJNdM-tG@46/o8X?32OK9UH26]>$C8H9;!-O#SZ=aJC8h2&+#APomc,(kI[HR'6SK%'EUfc^f]IlBSng+0.a!Bb<2bL5RL#fEfq8H@sqeP50RT;K5Gr4S,'Q[-\;d$+e$a)Y8aYpXUIf(k%,F_Qi+'A"GF,LrnIk9]Bfnn&ioh_ZTi$;]$3JKlFQq.7gMT'Ukr-]RJ41I%uSYVA9-ZSF/f^.D:*-`eBa+%Jin5MVQJkbF:d>b44BP4'XK6o@;:7uqV4<[/All2E#s*8sJnCdV0"A!"aX!!>,r-TH7b1Ds%e6BrT)E$]Dn^beKa@t%n6%=p/!o8UbW)`0RTmL&2J/.gK#?V4?XY3.ifaGE8_*Wqq@,;6=QU/o\r&TYo!!8HndTs`[MhX7;A?"Z5hDl>C1i8%#_E?2Eaj&@iE2\3%g`BN-pE[*dP;6o91dg)l[\H-9R4?MYI2+7b)i^e$K);7sDQZ4U#IoH-6!)2r2QF7C=5m1k3)6YU6a/OALU0emYIeYS9cCtRbp,?)1Oh8F$SqBSZ>'/;3[`4:_J+uS5!0EiE286jPk?AP.C[A2A?NVA\K4I`DBY`M?:d%;j+lq$NA(T6cnj"6L#\m7'A0hCV%[K`\sLTj-a"Ir:)CFT~>endstream +endobj +439 0 obj +<< +/Nums [ 0 440 0 R 1 441 0 R 2 442 0 R 3 443 0 R 4 444 0 R + 5 445 0 R 6 446 0 R 7 447 0 R 8 448 0 R 9 449 0 R + 10 450 0 R 11 451 0 R 12 452 0 R 13 453 0 R 14 454 0 R + 15 455 0 R 16 456 0 R 17 457 0 R 18 458 0 R 19 459 0 R + 20 460 0 R 21 461 0 R 22 462 0 R 23 463 0 R 24 464 0 R + 25 465 0 R 26 466 0 R 27 467 0 R 28 468 0 R 29 469 0 R + 30 470 0 R 31 471 0 R 32 472 0 R 33 473 0 R 34 474 0 R + 35 475 0 R 36 476 0 R 37 477 0 R 38 478 0 R 39 479 0 R + 40 480 0 R ] +>> +endobj +440 0 obj +<< +/S /D /St 1 +>> +endobj +441 0 obj +<< +/S /D /St 2 +>> +endobj +442 0 obj +<< +/S /D /St 3 +>> +endobj +443 0 obj +<< +/S /D /St 4 +>> +endobj +444 0 obj +<< +/S /D /St 5 +>> +endobj +445 0 obj +<< +/S /D /St 6 +>> +endobj +446 0 obj +<< +/S /D /St 7 +>> +endobj +447 0 obj +<< +/S /D /St 8 +>> +endobj +448 0 obj +<< +/S /D /St 9 +>> +endobj +449 0 obj +<< +/S /D /St 10 +>> +endobj +450 0 obj +<< +/S /D /St 11 +>> +endobj +451 0 obj +<< +/S /D /St 12 +>> +endobj +452 0 obj +<< +/S /D /St 13 +>> +endobj +453 0 obj +<< +/S /D /St 14 +>> +endobj +454 0 obj +<< +/S /D /St 15 +>> +endobj +455 0 obj +<< +/S /D /St 16 +>> +endobj +456 0 obj +<< +/S /D /St 17 +>> +endobj +457 0 obj +<< +/S /D /St 18 +>> +endobj +458 0 obj +<< +/S /D /St 19 +>> +endobj +459 0 obj +<< +/S /D /St 20 +>> +endobj +460 0 obj +<< +/S /D /St 21 +>> +endobj +461 0 obj +<< +/S /D /St 22 +>> +endobj +462 0 obj +<< +/S /D /St 23 +>> +endobj +463 0 obj +<< +/S /D /St 24 +>> +endobj +464 0 obj +<< +/S /D /St 25 +>> +endobj +465 0 obj +<< +/S /D /St 26 +>> +endobj +466 0 obj +<< +/S /D /St 27 +>> +endobj +467 0 obj +<< +/S /D /St 28 +>> +endobj +468 0 obj +<< +/S /D /St 29 +>> +endobj +469 0 obj +<< +/S /D /St 30 +>> +endobj +470 0 obj +<< +/S /D /St 31 +>> +endobj +471 0 obj +<< +/S /D /St 32 +>> +endobj +472 0 obj +<< +/S /D /St 33 +>> +endobj +473 0 obj +<< +/S /D /St 34 +>> +endobj +474 0 obj +<< +/S /D /St 35 +>> +endobj +475 0 obj +<< +/S /D /St 36 +>> +endobj +476 0 obj +<< +/S /D /St 37 +>> +endobj +477 0 obj +<< +/S /D /St 38 +>> +endobj +478 0 obj +<< +/S /D /St 39 +>> +endobj +479 0 obj +<< +/S /D /St 40 +>> +endobj +480 0 obj +<< +/S /D /St 41 +>> +endobj +xref +0 481 +0000000000 65535 f +0000000061 00000 n +0000000220 00000 n +0000000327 00000 n +0000000436 00000 n +0000000643 00000 n +0000000832 00000 n +0000001029 00000 n +0000001260 00000 n +0000001458 00000 n +0000001626 00000 n +0000001822 00000 n +0000001991 00000 n +0000002191 00000 n +0000002444 00000 n +0000002557 00000 n +0000002725 00000 n +0000002892 00000 n +0000003060 00000 n +0000003227 00000 n +0000003394 00000 n +0000003561 00000 n +0000003729 00000 n +0000003896 00000 n +0000004065 00000 n +0000004233 00000 n +0000004402 00000 n +0000004570 00000 n +0000004739 00000 n +0000004907 00000 n +0000005076 00000 n +0000005244 00000 n +0000005413 00000 n +0000005581 00000 n +0000005750 00000 n +0000005919 00000 n +0000006089 00000 n +0000006258 00000 n +0000006427 00000 n +0000006596 00000 n +0000006766 00000 n +0000006935 00000 n +0000007105 00000 n +0000007274 00000 n +0000007444 00000 n +0000007613 00000 n +0000007783 00000 n +0000007952 00000 n +0000008122 00000 n +0000008291 00000 n +0000008461 00000 n +0000008630 00000 n +0000008800 00000 n +0000008969 00000 n +0000009139 00000 n +0000009308 00000 n +0000009478 00000 n +0000009647 00000 n +0000009817 00000 n +0000009986 00000 n +0000010156 00000 n +0000010325 00000 n +0000010495 00000 n +0000010664 00000 n +0000010834 00000 n +0000011003 00000 n +0000011173 00000 n +0000011342 00000 n +0000011511 00000 n +0000011680 00000 n +0000011850 00000 n +0000012019 00000 n +0000012188 00000 n +0000012357 00000 n +0000012527 00000 n +0000012696 00000 n +0000012865 00000 n +0000013034 00000 n +0000013204 00000 n +0000013373 00000 n +0000013543 00000 n +0000013712 00000 n +0000013882 00000 n +0000014051 00000 n +0000014221 00000 n +0000014390 00000 n +0000014560 00000 n +0000014729 00000 n +0000014899 00000 n +0000015068 00000 n +0000015238 00000 n +0000015407 00000 n +0000015577 00000 n +0000015746 00000 n +0000015915 00000 n +0000016084 00000 n +0000016254 00000 n +0000016423 00000 n +0000017241 00000 n +0000017411 00000 n +0000017580 00000 n +0000017751 00000 n +0000017921 00000 n +0000018092 00000 n +0000018262 00000 n +0000018433 00000 n +0000018603 00000 n +0000018774 00000 n +0000018944 00000 n +0000019115 00000 n +0000019285 00000 n +0000019456 00000 n +0000019626 00000 n +0000019797 00000 n +0000019967 00000 n +0000020138 00000 n +0000020308 00000 n +0000020479 00000 n +0000020649 00000 n +0000020820 00000 n +0000020990 00000 n +0000021161 00000 n +0000021331 00000 n +0000021502 00000 n +0000021672 00000 n +0000021843 00000 n +0000022013 00000 n +0000022184 00000 n +0000022354 00000 n +0000022525 00000 n +0000022695 00000 n +0000022866 00000 n +0000023036 00000 n +0000023207 00000 n +0000023377 00000 n +0000023547 00000 n +0000023717 00000 n +0000023887 00000 n +0000024057 00000 n +0000024228 00000 n +0000024398 00000 n +0000024569 00000 n +0000024739 00000 n +0000024910 00000 n +0000025080 00000 n +0000025251 00000 n +0000025421 00000 n +0000025592 00000 n +0000025762 00000 n +0000025933 00000 n +0000026103 00000 n +0000026753 00000 n +0000026924 00000 n +0000027031 00000 n +0000027202 00000 n +0000027373 00000 n +0000027543 00000 n +0000027714 00000 n +0000027885 00000 n +0000028154 00000 n +0000028325 00000 n +0000028495 00000 n +0000028666 00000 n +0000028835 00000 n +0000029104 00000 n +0000029273 00000 n +0000029443 00000 n +0000029613 00000 n +0000029784 00000 n +0000029954 00000 n +0000030125 00000 n +0000030295 00000 n +0000030466 00000 n +0000030637 00000 n +0000030807 00000 n +0000030978 00000 n +0000031148 00000 n +0000031319 00000 n +0000031490 00000 n +0000031661 00000 n +0000031831 00000 n +0000032002 00000 n +0000032173 00000 n +0000032343 00000 n +0000032514 00000 n +0000032685 00000 n +0000032856 00000 n +0000033026 00000 n +0000033197 00000 n +0000033367 00000 n +0000033538 00000 n +0000033708 00000 n +0000033878 00000 n +0000034049 00000 n +0000034219 00000 n +0000034389 00000 n +0000034907 00000 n +0000035077 00000 n +0000035246 00000 n +0000035416 00000 n +0000035585 00000 n +0000035755 00000 n +0000035956 00000 n +0000036157 00000 n +0000036358 00000 n +0000036559 00000 n +0000036815 00000 n +0000037050 00000 n +0000037285 00000 n +0000037520 00000 n +0000037776 00000 n +0000038032 00000 n +0000038287 00000 n +0000038556 00000 n +0000038759 00000 n +0000038946 00000 n +0000039063 00000 n +0000039439 00000 n +0000039640 00000 n +0000039840 00000 n +0000040041 00000 n +0000040286 00000 n +0000040495 00000 n +0000040696 00000 n +0000040925 00000 n +0000041094 00000 n +0000041265 00000 n +0000041436 00000 n +0000041607 00000 n +0000041860 00000 n +0000042031 00000 n +0000042287 00000 n +0000042458 00000 n +0000042703 00000 n +0000042873 00000 n +0000043102 00000 n +0000043273 00000 n +0000043444 00000 n +0000043681 00000 n +0000043890 00000 n +0000044099 00000 n +0000044270 00000 n +0000044499 00000 n +0000044708 00000 n +0000044917 00000 n +0000045126 00000 n +0000045297 00000 n +0000045376 00000 n +0000045576 00000 n +0000045813 00000 n +0000045982 00000 n +0000046153 00000 n +0000046324 00000 n +0000046495 00000 n +0000046748 00000 n +0000046957 00000 n +0000047166 00000 n +0000047282 00000 n +0000047483 00000 n +0000047712 00000 n +0000047883 00000 n +0000048112 00000 n +0000048283 00000 n +0000048453 00000 n +0000048690 00000 n +0000048899 00000 n +0000049168 00000 n +0000049437 00000 n +0000049674 00000 n +0000049787 00000 n +0000049996 00000 n +0000050205 00000 n +0000050376 00000 n +0000050576 00000 n +0000050778 00000 n +0000051023 00000 n +0000051194 00000 n +0000051423 00000 n +0000051594 00000 n +0000051765 00000 n +0000051968 00000 n +0000052213 00000 n +0000052384 00000 n +0000052555 00000 n +0000052792 00000 n +0000052992 00000 n +0000053162 00000 n +0000053332 00000 n +0000053577 00000 n +0000053845 00000 n +0000054016 00000 n +0000054187 00000 n +0000054358 00000 n +0000054611 00000 n +0000054820 00000 n +0000055029 00000 n +0000055197 00000 n +0000055367 00000 n +0000055537 00000 n +0000055707 00000 n +0000055877 00000 n +0000056138 00000 n +0000056922 00000 n +0000076722 00000 n +0000076983 00000 n +0000078303 00000 n +0000079057 00000 n +0000097326 00000 n +0000097598 00000 n +0000098955 00000 n +0000099712 00000 n +0000119938 00000 n +0000120209 00000 n +0000121559 00000 n +0000121669 00000 n +0000121977 00000 n +0000122055 00000 n +0000122277 00000 n +0000122468 00000 n +0000122673 00000 n +0000123003 00000 n +0000123204 00000 n +0000123455 00000 n +0000123686 00000 n +0000123932 00000 n +0000124129 00000 n +0000124422 00000 n +0000124686 00000 n +0000125039 00000 n +0000125272 00000 n +0000125489 00000 n +0000125771 00000 n +0000126004 00000 n +0000126186 00000 n +0000126444 00000 n +0000126657 00000 n +0000126865 00000 n +0000127184 00000 n +0000127488 00000 n +0000127796 00000 n +0000128064 00000 n +0000128371 00000 n +0000128640 00000 n +0000128853 00000 n +0000129060 00000 n +0000129267 00000 n +0000129494 00000 n +0000129767 00000 n +0000130126 00000 n +0000130405 00000 n +0000130680 00000 n +0000131014 00000 n +0000131329 00000 n +0000131638 00000 n +0000132001 00000 n +0000132234 00000 n +0000132542 00000 n +0000132924 00000 n +0000133176 00000 n +0000133434 00000 n +0000133722 00000 n +0000133986 00000 n +0000134218 00000 n +0000134470 00000 n +0000134683 00000 n +0000134987 00000 n +0000135371 00000 n +0000135749 00000 n +0000136039 00000 n +0000136392 00000 n +0000136644 00000 n +0000136902 00000 n +0000137364 00000 n +0000137728 00000 n +0000138027 00000 n +0000138365 00000 n +0000138722 00000 n +0000139074 00000 n +0000139412 00000 n +0000139765 00000 n +0000140009 00000 n +0000140252 00000 n +0000140494 00000 n +0000140751 00000 n +0000141008 00000 n +0000141280 00000 n +0000141622 00000 n +0000141877 00000 n +0000142255 00000 n +0000142602 00000 n +0000142894 00000 n +0000143306 00000 n +0000143834 00000 n +0000144127 00000 n +0000144581 00000 n +0000144794 00000 n +0000145098 00000 n +0000145307 00000 n +0000145697 00000 n +0000146916 00000 n +0000147859 00000 n +0000150498 00000 n +0000152555 00000 n +0000154145 00000 n +0000157030 00000 n +0000159974 00000 n +0000162842 00000 n +0000165127 00000 n +0000166495 00000 n +0000168725 00000 n +0000170739 00000 n +0000173242 00000 n +0000176757 00000 n +0000178959 00000 n +0000180084 00000 n +0000182553 00000 n +0000185673 00000 n +0000188726 00000 n +0000191474 00000 n +0000194599 00000 n +0000197441 00000 n +0000200413 00000 n +0000203038 00000 n +0000205966 00000 n +0000208116 00000 n +0000210380 00000 n +0000213127 00000 n +0000214249 00000 n +0000216336 00000 n +0000218829 00000 n +0000221851 00000 n +0000225358 00000 n +0000227832 00000 n +0000230822 00000 n +0000233667 00000 n +0000235602 00000 n +0000238465 00000 n +0000240722 00000 n +0000242479 00000 n +0000244641 00000 n +0000245139 00000 n +0000245174 00000 n +0000245209 00000 n +0000245244 00000 n +0000245279 00000 n +0000245314 00000 n +0000245349 00000 n +0000245384 00000 n +0000245419 00000 n +0000245454 00000 n +0000245490 00000 n +0000245526 00000 n +0000245562 00000 n +0000245598 00000 n +0000245634 00000 n +0000245670 00000 n +0000245706 00000 n +0000245742 00000 n +0000245778 00000 n +0000245814 00000 n +0000245850 00000 n +0000245886 00000 n +0000245922 00000 n +0000245958 00000 n +0000245994 00000 n +0000246030 00000 n +0000246066 00000 n +0000246102 00000 n +0000246138 00000 n +0000246174 00000 n +0000246210 00000 n +0000246246 00000 n +0000246282 00000 n +0000246318 00000 n +0000246354 00000 n +0000246390 00000 n +0000246426 00000 n +0000246462 00000 n +0000246498 00000 n +0000246534 00000 n +0000246570 00000 n +trailer +<< +/ID +[<553fa1e33cd4ae10e40af1d79a1c109d><553fa1e33cd4ae10e40af1d79a1c109d>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 314 0 R +/Root 313 0 R +/Size 481 +>> +startxref +246606 +%%EOF diff --git a/advnote132.pdf b/advnote132.pdf new file mode 100644 index 0000000..052c117 --- /dev/null +++ b/advnote132.pdf @@ -0,0 +1,1247 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2+0 99 0 R /F3 3 0 R /F4 14 0 R /F5+0 103 0 R /F6 73 0 R + /F7 80 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Times-Roman /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/Contents 135 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 134 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +5 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa) +>> /Border [ 0 0 0 ] /Rect [ 118.301 457.0236 391.1152 467.8236 ] /Subtype /Link /Type /Annot +>> +endobj +6 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/issues) +>> /Border [ 0 0 0 ] /Rect [ 275.1154 429.4236 406.6965 440.2236 ] /Subtype /Link /Type /Annot +>> +endobj +7 0 obj +<< +/Annots [ 5 0 R 6 0 R ] /Contents 136 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 134 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +8 0 obj +<< +/A << +/S /URI /Type /Action /URI (http://creativecommons.org/licenses/by-sa/4.0/) +>> /Border [ 0 0 0 ] /Rect [ 202.7023 719.4236 438.1145 730.2236 ] /Subtype /Link /Type /Annot +>> +endobj +9 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 63 0 R /XYZ 77.02362 668.0336 0 ] /Rect [ 193.1223 539.8236 232.2262 550.6236 ] /Subtype /Link /Type /Annot +>> +endobj +10 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://www.apache.org/licenses/LICENSE-2.0) +>> /Border [ 0 0 0 ] /Rect [ 57.02362 507.4236 262.4416 518.2236 ] /Subtype /Link /Type /Annot +>> +endobj +11 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 63 0 R /XYZ 77.02362 668.0336 0 ] /Rect [ 434.0436 354.2236 468.2331 365.0236 ] /Subtype /Link /Type /Annot +>> +endobj +12 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://www.arm.com/company/policies/trademarks) +>> /Border [ 0 0 0 ] /Rect [ 57.02362 256.2236 290.6413 267.0236 ] /Subtype /Link /Type /Annot +>> +endobj +13 0 obj +<< +/Annots [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ] /Contents 137 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 134 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +14 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font +>> +endobj +15 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 718.0436 102.0974 727.2236 ] /Subtype /Link /Type /Annot +>> +endobj +16 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 533.9986 718.6174 538.252 727.7974 ] /Subtype /Link /Type /Annot +>> +endobj +17 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 77.02362 702.8636 128.5976 712.0436 ] /Subtype /Link /Type /Annot +>> +endobj +18 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 533.3848 703.4374 538.252 712.6174 ] /Subtype /Link /Type /Annot +>> +endobj +19 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 546.6236 0 ] /Rect [ 77.02362 687.6836 133.659 696.8636 ] /Subtype /Link /Type /Annot +>> +endobj +20 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 546.6236 0 ] /Rect [ 533.3848 688.2574 538.252 697.4374 ] /Subtype /Link /Type /Annot +>> +endobj +21 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 491.8236 0 ] /Rect [ 77.02362 672.5036 223.9948 681.6836 ] /Subtype /Link /Type /Annot +>> +endobj +22 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 7 0 R /XYZ 57.02362 491.8236 0 ] /Rect [ 533.3848 673.0774 538.252 682.2574 ] /Subtype /Link /Type /Annot +>> +endobj +23 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 77.02362 657.3236 125.5458 666.5036 ] /Subtype /Link /Type /Annot +>> +endobj +24 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 533.3848 657.8974 538.252 667.0774 ] /Subtype /Link /Type /Annot +>> +endobj +25 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 574.6236 0 ] /Rect [ 77.02362 642.1436 163.2243 651.3236 ] /Subtype /Link /Type /Annot +>> +endobj +26 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 574.6236 0 ] /Rect [ 533.3848 642.7174 538.252 651.8974 ] /Subtype /Link /Type /Annot +>> +endobj +27 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 399.8236 0 ] /Rect [ 77.02362 626.9636 147.9692 636.1436 ] /Subtype /Link /Type /Annot +>> +endobj +28 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 399.8236 0 ] /Rect [ 533.3848 627.5374 538.252 636.7174 ] /Subtype /Link /Type /Annot +>> +endobj +29 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 334.2236 0 ] /Rect [ 77.02362 611.7836 164.2665 620.9636 ] /Subtype /Link /Type /Annot +>> +endobj +30 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 334.2236 0 ] /Rect [ 533.3848 612.3574 538.252 621.5374 ] /Subtype /Link /Type /Annot +>> +endobj +31 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 236.2236 0 ] /Rect [ 77.02362 596.6036 133.8645 605.7836 ] /Subtype /Link /Type /Annot +>> +endobj +32 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 13 0 R /XYZ 57.02362 236.2236 0 ] /Rect [ 533.3848 597.1774 538.252 606.3574 ] /Subtype /Link /Type /Annot +>> +endobj +33 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 68 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 581.4236 147.133 590.6036 ] /Subtype /Link /Type /Annot +>> +endobj +34 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 68 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 533.9986 581.9974 538.252 591.1774 ] /Subtype /Link /Type /Annot +>> +endobj +35 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 68 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 77.02362 566.2436 154.8908 575.4236 ] /Subtype /Link /Type /Annot +>> +endobj +36 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 68 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 533.3848 566.8174 538.252 575.9974 ] /Subtype /Link /Type /Annot +>> +endobj +37 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 68 0 R /XYZ 57.02362 678.2236 0 ] /Rect [ 97.02362 551.0636 275.6818 560.2436 ] /Subtype /Link /Type /Annot +>> +endobj +38 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 68 0 R /XYZ 57.02362 678.2236 0 ] /Rect [ 533.3848 551.6374 538.252 560.8174 ] /Subtype /Link /Type /Annot +>> +endobj +39 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 68 0 R /XYZ 57.02362 437.4236 0 ] /Rect [ 97.02362 535.8836 181.8161 545.0636 ] /Subtype /Link /Type /Annot +>> +endobj +40 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 68 0 R /XYZ 57.02362 437.4236 0 ] /Rect [ 533.3848 536.4574 538.252 545.6374 ] /Subtype /Link /Type /Annot +>> +endobj +41 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 68 0 R /XYZ 57.02362 135.2236 0 ] /Rect [ 77.02362 520.7036 139.5048 529.8836 ] /Subtype /Link /Type /Annot +>> +endobj +42 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 68 0 R /XYZ 57.02362 135.2236 0 ] /Rect [ 533.3848 521.2774 538.252 530.4574 ] /Subtype /Link /Type /Annot +>> +endobj +43 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 77 0 R /XYZ 57.02362 580.2236 0 ] /Rect [ 77.02362 505.5236 191.9342 514.7036 ] /Subtype /Link /Type /Annot +>> +endobj +44 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 77 0 R /XYZ 57.02362 580.2236 0 ] /Rect [ 533.3848 506.0974 538.252 515.2774 ] /Subtype /Link /Type /Annot +>> +endobj +45 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 57.02362 490.3436 187.525 499.5236 ] /Subtype /Link /Type /Annot +>> +endobj +46 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 765.0236 0 ] /Rect [ 533.9986 490.9174 538.252 500.0974 ] /Subtype /Link /Type /Annot +>> +endobj +47 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 77.02362 475.1636 335.2522 484.3436 ] /Subtype /Link /Type /Annot +>> +endobj +48 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 716.2236 0 ] /Rect [ 533.3848 475.7374 538.252 484.9174 ] /Subtype /Link /Type /Annot +>> +endobj +49 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 435.0236 0 ] /Rect [ 77.02362 459.9836 260.9299 469.1636 ] /Subtype /Link /Type /Annot +>> +endobj +50 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 435.0236 0 ] /Rect [ 533.3848 460.5574 538.252 469.7374 ] /Subtype /Link /Type /Annot +>> +endobj +51 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 315.0236 0 ] /Rect [ 97.02362 444.8036 295.2849 453.9836 ] /Subtype /Link /Type /Annot +>> +endobj +52 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 315.0236 0 ] /Rect [ 533.3848 445.3774 538.252 454.5574 ] /Subtype /Link /Type /Annot +>> +endobj +53 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 176.4236 0 ] /Rect [ 97.02362 429.6236 193.2911 438.8036 ] /Subtype /Link /Type /Annot +>> +endobj +54 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 176.4236 0 ] /Rect [ 533.3848 430.1974 538.252 439.3774 ] /Subtype /Link /Type /Annot +>> +endobj +55 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 543.8236 0 ] /Rect [ 77.02362 414.4436 159.0333 423.6236 ] /Subtype /Link /Type /Annot +>> +endobj +56 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 543.8236 0 ] /Rect [ 533.3848 415.0174 538.252 424.1974 ] /Subtype /Link /Type /Annot +>> +endobj +57 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 505.8236 0 ] /Rect [ 97.02362 399.2636 303.5512 408.4436 ] /Subtype /Link /Type /Annot +>> +endobj +58 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 505.8236 0 ] /Rect [ 533.3848 399.8374 538.252 409.0174 ] /Subtype /Link /Type /Annot +>> +endobj +59 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 279.6236 0 ] /Rect [ 97.02362 384.0836 231.6083 393.2636 ] /Subtype /Link /Type /Annot +>> +endobj +60 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 279.6236 0 ] /Rect [ 533.3848 384.6574 538.252 393.8374 ] /Subtype /Link /Type /Annot +>> +endobj +61 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 94 0 R /XYZ 57.02362 230.3117 0 ] /Rect [ 97.02362 368.9036 333.942 378.0836 ] /Subtype /Link /Type /Annot +>> +endobj +62 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 94 0 R /XYZ 57.02362 230.3117 0 ] /Rect [ 533.3848 369.4774 538.252 378.6574 ] /Subtype /Link /Type /Annot +>> +endobj +63 0 obj +<< +/Annots [ 15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R + 25 0 R 26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R + 35 0 R 36 0 R 37 0 R 38 0 R 39 0 R 40 0 R 41 0 R 42 0 R 43 0 R 44 0 R + 45 0 R 46 0 R 47 0 R 48 0 R 49 0 R 50 0 R 51 0 R 52 0 R 53 0 R 54 0 R + 55 0 R 56 0 R 57 0 R 58 0 R 59 0 R 60 0 R 61 0 R 62 0 R ] /Contents 138 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 134 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +64 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 63 0 R /XYZ 77.02362 668.0336 0 ] /Rect [ 302.384 194.8236 336.5734 205.6236 ] /Subtype /Link /Type /Annot +>> +endobj +65 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 63 0 R /XYZ 77.02362 637.6736 0 ] /Rect [ 356.4191 178.0236 425.9329 188.8236 ] /Subtype /Link /Type /Annot +>> +endobj +66 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 63 0 R /XYZ 77.02362 622.4936 0 ] /Rect [ 422.7107 178.0236 517.3604 188.8236 ] /Subtype /Link /Type /Annot +>> +endobj +67 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 63 0 R /XYZ 77.02362 607.3136 0 ] /Rect [ 300.7141 167.2236 344.6901 178.0236 ] /Subtype /Link /Type /Annot +>> +endobj +68 0 obj +<< +/Annots [ 64 0 R 65 0 R 66 0 R 67 0 R ] /Contents 139 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 134 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +69 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 733.0986 104.2136 743.8986 ] /Subtype /Link /Type /Annot +>> +endobj +70 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/products/architecture/m-profile/docs/ddi0403/e/armv7-m-architecture-reference-manual) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 701.4986 125.1403 712.2986 ] /Subtype /Link /Type /Annot +>> +endobj +71 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/docs/ddi0406/c/arm-architecture-reference-manual-armv7-a-and-armv7-r-edition) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 659.0986 129.7853 669.8986 ] /Subtype /Link /Type /Annot +>> +endobj +72 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://developer.arm.com/docs/ddi0100/latest/armv5-architecture-reference-manual) +>> /Border [ 0 0 0 ] /Rect [ 63.02362 627.4986 112.8752 638.2986 ] /Subtype /Link /Type /Annot +>> +endobj +73 0 obj +<< +/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F6 /Subtype /Type1 /Type /Font +>> +endobj +74 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 444.0978 428.8236 512.2048 439.6236 ] /Subtype /Link /Type /Annot +>> +endobj +75 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 103.5349 418.0236 141.7233 428.8236 ] /Subtype /Link /Type /Annot +>> +endobj +76 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 172.0983 418.0236 218.6628 428.8236 ] /Subtype /Link /Type /Annot +>> +endobj +77 0 obj +<< +/Annots [ 69 0 R 70 0 R 71 0 R 72 0 R 74 0 R 75 0 R 76 0 R ] /Contents 140 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 134 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +78 0 obj +<< +/A << +/S /URI /Type /Action /URI (https://github.com/ARM-software/abi-aa/releases) +>> /Border [ 0 0 0 ] /Rect [ 305.0993 663.4236 362.7353 674.2236 ] /Subtype /Link /Type /Annot +>> +endobj +79 0 obj +<< +/Annots [ 78 0 R ] /Contents 141 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 134 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +80 0 obj +<< +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F7 /Subtype /Type1 /Type /Font +>> +endobj +81 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 719.2236 0 ] /Rect [ 113.1682 474.0236 400.7302 484.8236 ] /Subtype /Link /Type /Annot +>> +endobj +82 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 148.6236 0 ] /Rect [ 420.0637 432.6236 537.5495 443.4236 ] /Subtype /Link /Type /Annot +>> +endobj +83 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 148.6236 0 ] /Rect [ 80.02362 421.8236 91.16376 432.6236 ] /Subtype /Link /Type /Annot +>> +endobj +84 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 148.6236 0 ] /Rect [ 495.7331 394.2236 536.5146 405.0236 ] /Subtype /Link /Type /Annot +>> +endobj +85 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 148.6236 0 ] /Rect [ 80.02362 383.4236 176.7736 394.2236 ] /Subtype /Link /Type /Annot +>> +endobj +86 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 94 0 R /XYZ 57.02362 232.8117 0 ] /Rect [ 173.048 299.6236 420.2932 310.4236 ] /Subtype /Link /Type /Annot +>> +endobj +87 0 obj +<< +/Annots [ 81 0 R 82 0 R 83 0 R 84 0 R 85 0 R 86 0 R ] /Contents 142 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 134 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +88 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 94 0 R /XYZ 57.02362 599.0236 0 ] /Rect [ 167.5275 723.6236 300.3698 734.4236 ] /Subtype /Link /Type /Annot +>> +endobj +89 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 317.5236 0 ] /Rect [ 425.2018 683.0236 536.2774 693.8236 ] /Subtype /Link /Type /Annot +>> +endobj +90 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 317.5236 0 ] /Rect [ 57.02362 672.2236 174.4719 683.0236 ] /Subtype /Link /Type /Annot +>> +endobj +91 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 79 0 R /XYZ 57.02362 178.9236 0 ] /Rect [ 57.02362 644.6236 140.6153 655.4236 ] /Subtype /Link /Type /Annot +>> +endobj +92 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 508.3236 0 ] /Rect [ 447.1701 149.3117 537.7905 160.1117 ] /Subtype /Link /Type /Annot +>> +endobj +93 0 obj +<< +/Border [ 0 0 0 ] /Contents () /Dest [ 87 0 R /XYZ 57.02362 508.3236 0 ] /Rect [ 57.02362 138.5117 180.5715 149.3117 ] /Subtype /Link /Type /Annot +>> +endobj +94 0 obj +<< +/Annots [ 88 0 R 89 0 R 90 0 R 91 0 R 92 0 R 93 0 R ] /Contents 143 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 134 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 + /Trans << + +>> /Type /Page +>> +endobj +95 0 obj +<< +/Contents 144 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 134 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +96 0 obj +<< +/Filter [ /FlateDecode ] /Length 699 +>> +stream +xœmÕÍnÚ@†á=Wáe«.Àsþˆ„ÒHYôG¥êžÀRƒ ,r÷ÏJFm‘`^ðX~d#ñÝãâ±Û›ñ×þ°^æs³Ýu›>Ÿ—~›§ü¼ëFmj6»õùúmø\ïWÇѸœ¼|9óþ±ÛF³Y3þVžÎýKóîvx}Xä_«—åª;½¿ô›Üïºçÿ]^ŽÇßyŸ»s3ÍçÍ&oË%>­ŽŸWûÜŒÿ9åmÃ÷—cnÒð½¥r}ØäÓqµÎýª{ΣÙd2ofñ0ånó×±6MyÎÓvýsÕ_÷NÊk^ºúöJ§I+h:%´rÏ Ú¸çíì:ØÃž)÷ú†¿OÑ·Õu?V}Wõ¢êûªÞº­üm[uªZªÖª­j¯:ªžV}Suåo+[ùÛÊßVþ¶ò'úÓÐô—¥4ýÃ=Oô'<‹DYJÓ_–Òô—¥4ý ÷<ÑŸpÏý Ï%Ñ_–Òô—¥4ý Ï4ÑŸðLý ÿDYF3¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àú¯â: 0+0ç^gÐúÒ÷e< Ãp<9».¿ÎËãሳðþï0ŸGendstream +endobj +97 0 obj +<< +/Filter [ /FlateDecode ] /Length 19554 /Length1 36880 +>> +stream +xœì½ |Eö8^ÕÕÇLÏ=Éä>:7„„Ã’I @€B (˜ƒÉa"Ë%  ‡Q] ˆwDØ]p—×UPô»ˆ¬_vUHšß«ê™ˆ®«î~÷ÿùü{ºº»êջ߫×5a„ -DeŸ—×wv_¸sŽÜÂò‚*ƒ·á,B¸?^…³kT˜„— ×jIÕŒò{ûÏž‰×è鳿–,y°ïŸáú„ÒÖ•EåeŽGhô*x>°n˜¶JïÃu\‡—–×ÞwõðSpýÀÛ6«²°àÎ÷fB(“ö?U^p_ÿ¾x/BcóáZ©((/®û$\/Dèv]UeMí³h +B.§Ï«ª‹«†Hƒæƒ;JæÍx5 (l„‚´3ù*áì@…A$DÇsUÜø=ê¸!‡çGóH1f•¤!'RnÜ=UO¼I*Ççó¾ñá ¤ýó³‰Xf­…ì.|äaFIH‡ôè_ù'#À3!3² +²!;ò@žÈ¼7òA¾Èù£ˆ‚P0RP +Ea(E H…z¡hÔõA1¨/ŠEq(õC (õGÐ@4݆’Ð`4 E·£a(hKA©h8ÒP:‰F¡ 4A™h,‡Æ£,tÊFPšˆrÑ$4åŸïDw¡©hºå£4¢"TŒJÐ Tʘ2µ¢cðyíF›ñ¸*Û÷Ân?ZŠêàΛø^Áõ…{;Ðtz6 cd7ðhÀ÷ô?#pè*ÎA/Œ$쉓$‘Gü8þ>›oå/ò'Ð ¾†?Áçó58‘Yx€ 5¢m¨pñÄ•hWÏe÷…h|*áù ¼Ÿìá%è4Ú@xnÚ‚O]ÇÐ?Ð’Ã--JäJÿ·Ö ¿ Õ€àOƒ6¨\¸ØÃ\ÓÙw é+œfŸ+hÌœƒ¶‰­¢§³PŽíÀoâKâZÔ‚N’»È½ä,^ʇñ;ùQ¨QãÉG{#–à¹@;ýÔSèÜ>ïFŸóùÒt€ý¥æ|ËŠJÐa8æˆV i^JV¦ôi :!æã`<@æÕU’h&´êÑ^´õ%ͨ 1zÅAÂ?`äfþ# ¹?Äý #@çJøËÀkPSԌЋ’(ð„Ã(F±îã"2Šö9¼“Ò7æ¦KÅ*)ûPÖ>Ó\¥õƬɼ¿·OØG"tûøˆ°¾ïáG}cÆdMVöu¤pAMË÷&L†&½‚Ûp?m{F'Ý'DÀùû”ÂRåAëƒaƒ´Ç¡µ™/¶ÅJÈÏiä¯#ñ:Ö 8ŵºÔYO]:u)ÞÃb‹±…”𨽆ø·¢6Kæo¾ª£ý?>{ƒÍÝ֓:<9áè¨ø£I£‡¿kLzõyÇx4|TZb<ß/uhrÒÀŒ~ùcæ:Í®3MÔÝÝ{¬õÜ…öKVµ£í²õr‚-)Éžd÷Nê‡â.ÿCýÂúE‚õòû—á^R<%3vxzyáÄ„ƒ†áý#£bqTïíewxr`,a¡áQ‘Ü€þöAÃ=ÂlÐöIðrxŠRÆaп¶…Ùx´äãiÓòó§MûxÉç§M:í®içØ€¥òòY³ÊËÕo7lP¿ÕÚXš___1irɾ½eù>ÃcWìY4$c`ê£BÉ´iÓ>Y¼èü´iwß=mêÇK–žŸ:mÚŒ +}mÓõzyy´±°a¡­NW/ “™öϸ•°þ¾(Dyö±­{ƒC\ýÝ{_PgΡ½àAË…ÓÍ (Êé!¢-z²È¨—E_‰ðòÅFë¹öã§Ž[ÕS¿Æ_z)!‡ ðH$ûÆ‹Nn[”7Ñoòù38Y}ýöý]Øz²ED¶ä+bÂc_æ¹KúÜån°$Ì#‚}cÔ[8½G} ;÷°oУýDÅ`°}œ²-D‘A¤À.¹@a^¾A}'k4ßuãvˆ(W«ahsh\|oyÆäMQFš4ö>ùV˜õˆñ­¡kÂ’ãàY<ïØW?xLJê;IÑO2†ú%E†’I‰É0Ý…¶SÀ…X/Û’lö$›wX´µíê…KÖOÁÌá6Ü¡Oâñ˜}rΘ}†œ;Çì3Ò/sΓ_fqÃwò×n³ º-kŠÄTÈí?ÞT³4ÓÖT>jÓ´ýãp,ô8ˆ}ƒbáúäa+¯%k<>¡ñ¶‚Y¿ñ3Ÿ\r×ÔaIÇŸÉØ4yÒºÁ%÷~àûþ©wòÎŽŒdgì”ØÌ¥ }œ[»-**k䔋nΦâ)«ô/w>¼30îæd–Ô›ïÛXrgc¿˜ª´Å[iòÅ'Ý2@[•h È ½SݔĥLE€ `,üi€Ó"¡%ü"N'  $YÛ)Ÿ&D˜’zA™w¡ýx<>€ Š!Ë@¦‚Ž€Û³‘0‚;vÌó ‡ª +§;îUÅÅšœß"»¹ÏÙJuZüéxADÇëèv‡Há âèDíç.uê¡Ó¯wê³ôùú*}‹þU½4•:Ó8pån.|7LušëC6×ð5ÞÂ+0W0ć~þÔ ñzÁ×Ä5x{X¶à ?opXv›Õl2êuî@ê'ûˆŠµý8 CO Ú>”~·%\JHˆwÚ ØÀŒ“'ïgó³{ëðTBB 88 .“aìÂÞ[ý GŒ(Åáe-3ðmêãÙx˜úhiË õìŒÇKÕ·p~Žú +.+!KÕý¤A-À[Õ‚Mêþêt¼…ñ¸Mx+øP´[=É—‹ž‰ö/ú²sb('ä(Ù‹3ä@ÀÅEÇùsÑÑq©6k¨ìÍ{‹úÞ!ç½ÌK\&ØÂüôZaE½ #ÔyÉAR´ŸÄjÕ,EêL‚Oe¡§ò¨¨yŒÙg¥&ba“jÁUï¡íÞCÁ²¨.\¢f—ÐÆ, Ì,Ì›Y¤ ’•ÿB²š¿Ëë<ñ_ä…â01 ÇÛ¦ÙÌ€DGóÑQ6ͺˆM3>³>î…"\öû™%ïU~pôOÇ‹¦î0áٻοþý¢Úy÷~´`Q½z÷åúö}Á™‚ñ;á{šŸäóH¸øˆñ¡Hûª^ákúG…øGè‰ÉaÖ›,!¦>f“¥Ÿ¡¿Æ¼îŽÅÓå]Æì31þõ£\m»tœÔ?47j~!áêÐ ìŽõ²Æ5á zŸÜ«3£ÐÂV÷û‰8Ì£Û3á½I……“&NÜrè¥ÇZ½Ô¾>·pú¤I…E¤_Kû”–à-‡_Úºõà!nͺ75-^Ò´àÜK/=ûÒá³\AÓâÖ­{`Qó‚oÿW4}éå?=|èœfËnœTà“ÅãÎf6—Ùì6Ã2Ù`·ÛôËÞáå°¨[æååà0Á˃Ñ2= + + +T‚¹ ›ì!ƒÙO›‡,é8Qoóð9̤=â|ðÄ“ÞMµôDDréå0‹½¢Ä&Ÿ>MQëÂ×ø¬2{ȱ³ÅÚQæ k +1Û,`uvk?È^ÚNA8¢1´‘*$5ç#þÁ¢À-4À¬4ÅìqêyO÷-¦w:ïæ…î·$`<Õ¹_2¡ú考9oCoÔGÚ£þö†i(O‘'Ú§øM ΋ŸTE›ðFn½®É°ÖÑäÕº±o°Þ 7êlÆ(c/Î_ïkð5úÚ<^Á Q( +GëÃì½=z{örÄ% Ñ÷·'y$'ŒÖgzŽq¤ûŽOÈÅSôyƉö<»‚ïN˜i,³å'Ôá¹Æy¶µh-^Ï5 ›¥Íº­ºMúG ›Œ«Zö%$MESµ8Gó"=†%ŠœI+M‘P÷xÖ•Bá¯îœxªåîj=n’*2–NÅÞí¥–üMýͲeñ n°}â¤-#Ê– !aw<>ù‘7’\cÇ7yǪPÕ_©ç׿MÂXøQaòü¡O¼~ ®_åäÄË VàJ+9eüZÄÓ Áß"H\rE6§É)d ùB•°ZhD 4hqÕ ö©_qõ¢VûƒœqZo6IˆØEä!›­ç(P€)kÏÂÚÔÏ]H¸dc9¤ÄXäžvï0-Ïäê—-^²´¥¹iÝzÑþ©:ìâEuÈ'ÅGþò!n»ómƒù*Ù|ÁQé|F;ï¡C0ßЫ]p=µ,6l }@n€ljnYºd‰h¿¤ýð/êà¿~‚ߺx¿Áè¸MÞ´¡qΫɀx£^â!„’‚͸QÆæ5vQ&zÑF0á§…7è=M¢Ýzjè…„spx3÷Lw +‚kÛ%¸×ñØ„¥`›Õ ˜G7ÕxÆ`u_µºo0ž¡nŒ³ªqÿáoN?¦6à¹Ç¦¿ùFá1ÀDþ>€ÁUªßh¨€e(g³Ú aá¢ÍŠÀ6á›[¹ù±Çà¿Ç»Žõê×ׯ«_c½¥žPÃq¦NÄýqb‹Z£.SÔüž‹çá‡(Ý!ÄOz@ÕŽTÒÂs-Â" µèuÁb$OØ`=å²L­áR›Æ”„«t™$i/Xˆ…ç¦ +± "©HU<ô¦ø]<º}Ûn¾fTë¨k§w3ýh†ùFÍh‹3Ê×ÏŸøØ@]l‚À§Z·­3µx®áQ ‡¬äo+©Ñ;Àè½\±p!,‚žºôÚk,x2|º±\øï °2ŸŒ ù\!WšÇÏfû7øJ<â}y?Þ_¨E³Å:¿ÿÚ€Åh™ïb¿Åþ‹v¢þ6p‡@Æ€H[:‚Ó ƒŸ§Y‰ˆð +îõöL`dbÁا–Ý}ò¾y§&†=ÓîôU¯îÞ½{^3¸|}ÆœæÔáÇû%|öÆ]Û«Õ¿2ú7ƒÌk€þ^¨Ê‹ò2}ð2Å£ÅajѯZ”µakÄUŽ'£½<ñô ˆT¬Ä3X/FS6xå¸9 g€ÑÖ8—.t-rX>Q_T\ …ðààiNŇt[«DöÁ´FIòš'Õß©ŸM{{fÎ;导}pûÞM[žÜ0á•ꚣyŸbãÃ$"¸mõ_ED¼Ù/¡¹ñ¦sªjêÃ#_P”÷ößÿ4Õí"ó6Ð+¼ß"g 6"Ä”ŠˆAjUÅ"=6Ê(@ÔñFæ{ 9îäÈH ;5œ‹Ø¨‡êÌ#‚xR¡ö6 ÞhÊCehzI^¸ŠÄ}È@<7Ž7åâ\‡ç‘¥ØÂÔC†žh£ êkˆ¨rX ž>}´cšÑ~žœhOÜ©¶àü7™Œ¶€ŒŠ÷@4ÍÆûI¶eÖ@¿ɳźÂĵ E¦UÒ¶ ï,“X‰AÖvÜ]2ÖnÄJm„dm»L͘Ú1HmÓäCr9 £ï.*ˆoGKÌä˜k8\=¥~9íÍÒ)¯ÝóÌ»ï>sÇã9ÂéÝê#‹zùþ¦þ]QŽõ‹?°yóðHæWÿfæWÂÑdg¸‡ˆLËŒ¨ÅKl ðÚnm1®]°*ªð ò !Áþàh@‘.0Ws¡ýB— +9=¡cøw‚œà ÇD |7Ö<Ý’OÌrޏI S¨[ +Iðâ¶-ߺu9XŸùhæ;'-CößóÔ+«êeœ…ý3%C=ñøK/=þÄ!nnkx¤ú•ú天ê—ýTý樦ãíAZõ'èT)ÈED…NÁÆŽØxðÈ„ƒƒ%X¸µ±¸÷L… |‰-ð%$¹øNûd‹ÄOHF 3È>´O”@g@88 ‡ì$¯u||«‰ÂéÜk‹šJ€÷_ <^Éxë°áÎàp”ØԷž&hUÔ“ñ>ÆðÞŽð‹<9¸sKˆ?¬« ëo»Ä˜ë¶Yv¥»²yšm…'º«‡`¶a¡áy¸;€~p+Woß¾zõŽíêöÅkÐ?¨®YôÈ“ê×_­~½mÔš%‹×®]¼d ÷Ö¦††M.kØ”«ì_øüï~÷üÂýJè‘Æ3Ÿ}v¦ñ.¨]¼¸wnÏ7M>Lo¤`_¼ ù¶ÈÛù´Â+¸ÅºÆkU„â„BCLLm€w„úTý»[k¼Ú|ßð{Íÿµ€×ßj –vÛÛ?·ЛALÇí®Ä%jº‰Ý„>ÊÜ<´eðþYQ¯cëÇ°Ž°©Ï©ŸdnÆÃ\ ºùŠ=÷.lùë§Ø‹·­êAÜz·>Qš®€â¼É‡±šC€Ó,.áw@xge r‘=®jAžzˆ+'OÒPχ©Zæl¼E8= ›¶ðKÐXºØ€È KCh1ù2är ËFNÒ|@uœqç$gÐ4á,à ¢±ÎÞÜf¼ažž8,ˆÚ, +©Ïa$âSæP8Z})AK+\+þ‹®Õ]Kë9€…³×¿áu×T#WÔ5êÚø½ø=JÇœ/œ%O¸ø`‚tHÜÌ < +£lhK謼ƒO¢Ÿ3Gá\SÉ;¯ìvåvnÜÇ;ûˆ›¤aDØ ¸£Í‰›‘ãpª(@Ê)ð;ñS’È…|‰r¨ò.¬µCØ{ À}6'/’+Ô¸jÜ\Îd° nè%½,vö Ò‹¼èÁ#ÏeË­MÞk@¢~&½ÀËAØàÇ€|Q¤ŸG,Fê‡=X´lwÕYaÐîªô_½l=·hú{`®~ž½{eÄÜ>Özî{¡=ÂìŽ2ùT+–Ÿ:b}ër„ -i V‹`ew&õ2{ã5íXø85o¨·Œ 7.yN¾›¿ÁݪåÞµ’LTd8å¶,òâéêÑ»g)R +Z*µÂòíÚÃ÷×?´zÞÜF.dè£3öüáOÏØ<¤ñ‘íÉÎRõô¾úó{®¦¼ {>¶èÛÒ)óÕ3ª­ .[þ«E8ûåSøžú1ãÕ7ÔÏ8߯'·=¼jû6uÔØŒoßyçÚ˜Ì%Š×‡ÏÝs8kÉÊg‰úë×·ªÿ3³´|Ò•3–ÌŸ3^>€GÏ_а·eú§õê·êïDÊ+}_Ír½áLA6X €éÑ\Ûd"#9‹,A‚*Ò›z‘uôd1RÍaZ5dEC½ C¦Ü¦Õ /œºdï^Xè<é¾p‡#Mï÷+FZ0Hµ` g‘,: šŒf£*´ +é%¬ãD¢ç½°/—‹'sYƸ”»Ïæî'Õüé>]^Î-4nà6’fÞ[Krè*ƒ„0î°z™‹Pë?á’~¿¼ãîå§s‡/Ù{­^ .bñë(ÄÒK@»V‘ +į0_Ô$ë›ì‹p“üL°Í ã<|ƒdð|bõ(À·P'DMˆUЩÛd¬¤øý–P Öršªt6"Bº°¼xò±ÇžTã>ëÖ¬Y§8þâµ…÷7mW¯\ïøŒ;ÚñAÃÊUK¹uXeõ½U;^{nŞʱ ïü ´æÆy! +|€/èô3=nÞ+7Ùðãh/æo[å'ùšP¼§Õ¢è +GZu-þ‹°?èÑÅ•—h¯~ÜIŠUrqñ ¤^ÁVŒ_,™ùÅê3ê<¼ OXö…0ýôÝÓÔ·Õ?ªgÔ·§Ý}rÔ(¼ƒ$ð֑̆Â>cÔ¤ZuœUF‚¯)èy;[ÅjLƒX³?߃1Ìû"BØ9ãµWÁ «©ÇÔT˜g?nVKÕ,µ@ˆ»>ûàXƒ½w¨ëÕ…ê¯Ôfæ“©WÂü:»ØÄsMh‘®‰F°^‚ü”7R–œjkë”Wüþ`ÌÎrO×q”ìëðãÞîHâ¾iFSËôÝçwwÂøzí´»àóÏ@à`Àe ¸Fm1tv”Lé¨â²:ö½K¡ŽÚÝ1¹dIs§@”äTà›ˆ“Îþ¸m¯£É¼F·*ˆC¶þ|¢¯Á +Éõ¥ö ím2UO1—Ájàj\R佻˗S=ÀÙëÔO[Ô'Ô:¼O{K•Uí+ÕËêØÛïÙy¯Ùѱ`ÂD¼—ã +¼qTúîÎW£¾§þ^ýM„›vaãmŒÓS×Ä=ãE²„ ·é±›µí,ƒzñû³ga lKÔ^N}—ûó»ïv„ý›¹¢k}(—]°ñZVGˆ}=ËQp¬¸eeoYØJ›Õ°Ó/h…­Fa« Rèà]ƒLßhVKJuz8$5 ûÐ"£ “\(ö€yÅHT()l“Ó”eÊ75š¶šl«èZ}÷Äù±ÉË*`¢µêWWw7¿ÑÉ“™¬^𥳗΋d!äÛÜž3UÉAÏêEH.DE i [Î ¥µ¦®—ngHS˜ý:Bá#žæ9½Î‹ë%ôÒ â +ýu#¹ta¸n"7ƒ›ÍÍ–pË…FÝ:îQÝEÎ>RЋþÄWÀ3K>¤—ÐGì- ä +ÅR¼1…8ù4Á):%§q:ɇÄ iŽPe\IV +‹R£qyL|L:@~-½EÞ’þ@Þ—>#ŸóŸ ÿ#~M¾¾c¦Þ‹¦Þ ÌÁ!ÔÇ2©nÁ|‡?ñSÿÑ‘He»‚›Ó1ªý<÷ÛŽ~¨Ónf²wqN#3@#žÖ¬´÷%ñN}¼”%-$ y^S0Äw¹?´ß ,?½[ƒ! #zÕÙŸØ$ÄÙ0§£'Âée=)YŸ*KÑÃuˆFŠY à‡ÉÀwµê­)ßézº[µ»3y¤+£ýUfÊù\BCž“œ§ä!Gr‘’"EÊŠÜ_ —q÷sõÒ\y!·XZ,¯æ¼xl ØŸ„á¥ë¥ï‡’\]ž¾X7S?[7üàC¤ ?J<ÙZ +Gë±a”{¸/žà¾o© Ž© Ú„Óí:ò͵>Bp;âѵ:õ,‘ù¹Î ÉFël6È3RX U±Äð%—j×J©qìÍd—v1­ +¦.É?»MŔʸi!'‰X/:°Ÿ˜Ž3ÄIx²XŒËĹâRü Ø„7‰[ V†5¸h8¶rÍmꕎ™€íõ`þ£k}ø®ƒÿ§¾ìL·^“5i5<_K"ñuX}zÝjxÔE%²ê]”æ®Ø7‰:§v`rîÆêsx0¾O]®Qߢ5V!SmU?Q?U[ñ(ì‡ýñ¨mêêºÚÁÛ`} +dw,âb±È vú@¢áÈn•uO£Q²†#OÍkijÁªxNƒÅìHvÜíxÖ!°¸Ô¿yˆÜt× +^«>´iÓCêmøëÃëê»B\ÇoiXöÈŽóg?ø¸c'å…ú‹(ÛÙÛfå,Øh2š±ÉdLµs|€9¦ “¿²]_Æ¢ ·mìU=cTR· +VÉëÆ:íÕD¹ LÆòÕB¬ÿðÊÏ÷¾ËÆk¿U?øÖ,Ûqe"cj»ú°;®ç/=PzÎ9ò<"‹6ºà±Á‚'U䑃ðŽ&½g“i‘DbƒÉË,Ⱦ¾¼-ÙS0òŒÑm”Ó6-ö¥ì¶ÓmF=ˆÓÞ9ƒX*8Ï HÀ$ï@ìÉyo>Eà.’D‰‘R¤.R¯ ĹtœÎ• +u|0Çc¹¸\Ú n‚§²RŸ·}»Êv…(4 ë+y(¥~؉3¯Ž^yß¹wñ;µ/éX¡>ÒÔôwØkõ¯ÔR¼ yzÇ +áôû|è7¾ãrÃ’%K©MÒz5ݧ…~åj2rf¤Ós’̥ʆ `Þ‘ãqÏu>M6¾ ­‹€ä¬Wlö—P¨¿¯¹¯äëÚËz® ~®X´xtÕõâóH§‹êþž¾Xƒähêàè¸èñÑDËåX¡ øEMm­žð£jŽß½ýù9;æ}üõõâÌ/Ö_ª~æpæúßÅÞ/û“°í­AÎ.,öísæÀ™¿ÄÇý.-}ù¯*îöéûÚÓG.DÒ{ ìŠî[Ðh§YÔœ¹Ò§ ³žºÐ~ÙQ‚¶ŸVT:V_Ò!»¾äôÁÈ +.$X²êú*ýV½~*q½ùù/;.ë¸ ÒµÓ´º„Ñ~ð)Ñ0Ÿ 9^:Îf@B“y•-²ëäÛ ¢¦Ø»Â:{¦9˜W– Ù`F­„†m ŽL³”ýÇö¾ùÆÞcê‡`Ÿ¨‚ó­»ròä²²ý.õœú>îÃ)ˆ^tFñ4ÎG´HOèž[Æ(•ðøYD@H€ŒÐ½þ'ü=q&KGu|:"½ÉH~¤0…Ì'Kˆ$"‰ÓñÔ{r~¼ŸÐEâH.š"DEwJĉÜP~¨0H…Òp—Ág#Å<”+–pe|™0͆eÑ\~®P'.Ôm@ëÅh°X éa=Äî8rŸÁú}ÇÛ໽ùÏ!qÂhBÒ[q½3Cðˆ§¼Ÿ¬'~²Aæü0ÝM!Ò ö.¸B®zÛ2¦Ê∇ :£AÖë´}# ™¬§\»F.%$Ü:Øvž;—€ˆÅÞ¯DN8ò*Ù.÷Â!êㆠýåx9“+¤ÊN9›ÉÝ#Ìóåznw¿°@X(7sMB „ôd¼(ÐMÐXâA÷$=Òó²lDf?âà:_£Õ¬ð!‚"*’¢ Ó‡ËŬ˜‡rƒÉ>Qˆ× Ô'’ñæt”ŽGs,gR!à¦êœ:§~„<Öè4;Í“9ˆñÆ,s 7ƒðÓ…|1_Ê×é‹ä"ÃC=w™Ã× +sŹÒ]•î>ããó2®,çWKõÍëù­ægÍwÒKED¥¦Ça#Žƒ›N:O¿N¨+TðÝo¨ 1;™X¯]ÑÞvæå%NÑÄ‹H6‚êƒ5Î Ñ™› +–É2P*‡ø1ûR³&;=Yšª·tY® +‚³’¬—:ÿszÂ# éô¼„=/bN&"¶À¿<šI¼è ®ÁugT…CgÔ»Ô¼?q×Ö£Äöo¸úŽ¥„îźÑ>ã"‹Å뜽\Ù1æz"ȇ*’.•&3"'ñN¼‰¤gÞÄþÃ&„dª.CΓÀÅsñ ¹tÎÉ9§îîá]1÷·–³za?,Gâh2ßFœ2¬YÉ}¤JÞ*Ó"„qîF=ƒ·àGÏt\9TlâJÚ¿‚UàÛZ{ð9˜å`«þ,ÿÔÑ¢{ª< i@åp*D™VÙh}If¯?DíÕÜ[çr·gÞé=—|¤hH/µ”l”N/ƒ¼ˆŸÎjˆ3 IºdÃH2Z7Þ0‘äéJH™®Ò0‡Ü§[`Øjðrçé :RÃ7µg‘·¯ßNöµÏNoº^¹{¿¦ó}ÿdÑüö§…ß%îçv¡ç`éJ†#]ge­‹ºo*`µýx{¯|•Di=7dÞyåŠ +ð¿modð{¼ Ÿwüðÿ“wÁ¼àÝìe0},®¥°Þf{(®ñN+Ù…÷ W4œ][ã,Ú,ºëí¾b[ã‚–°(ÀUª“¿üRôüæÏ"ßHëµd§p–ùp§ ®-’?wœautÌ¥Ôä0|T¥Ž:ÊÀ;Nâ•j×—êñ%5÷T÷–_£]à÷y+­%Ð ¿À~ÞóúÕ=š¾ìæ¯p+ÄèÛÏ©Ç/ çy2óÖs®®*¤¶Ë’geG>ZÙ‘b+±ßFõX¢>ˆg™õoôåëÁ'F ÃÎ(ß`ƒ·ÞŒvy‹Í6eY𡀃a­¶UÞFäM|Lz!˜è<Ó")ÇOÿÕô²Éö«t'ÛJ/gE|`|P|p¼šå t9ƒŠ3Äš˜”œ¥d…d…fEUE- ljnPB–†®Žj‰ºäê䔜¯ä‡TUW)U! ƒ/T†øtWv;‚ê,¤†ô(-s¯|¸gQåÆƒ­­É‡—ï9ÖqsO­Ï?SüÊ”ÿ½Â%–ÔO¯9óBtfÇ¢Ý%¯?ñòkö+ccwGEµÓ|õðjèòÕÛœ¾ä Ñ¢?èãXeiõ_ï‹ìö‘>FQç—ÎrÒ„«¬¶p¾‰:r9þ@~Р– xºß¿ª˜½ÌƒÄšnn¥@>yê‘Gž¢GÇß«?ŽnÜ8^ÿÜàƒ¹¸c/ƒƒË.*P«ßÀçpAÑNÀÓ}uä"ÈÐ%;ýÑ2¼œ7/3-—ÚøƒÞ­´pg7¡Qži~Öö î•–äÿ~™.Kü­þ ýWû·ø ¸[Ò×¹wÛUÀ#Ç=–õü‘#Ïg=6nìö©ÅôÅâÄ'ø{úô9âÄù>}v‡‡AflÇƒÃØZ ð⧆V_~‘Ùó  [enÅë!ÝF:n¤ÍnH d&–Ðɯ¶ü¢e&NŽ­L¼º×ËÉ­­ƒŸ»ÿØ tãØýÏu¼ œÛ¹¸GpÓ¾½´³¨À:øŒ(P.ºðZüòDþ¨Êù¿~™n¹àØ……ƒFü’ÏA{«qU€¿ƒÓ9th g·¤0Û\›¯^Ò^Þ^ÕÞÃE'V¶þ.ðJ Œ’q2—ìHöb¤8]œ>F®D•¸’«tTúë§ÞKY¢í—ï,‚ +HŒí¿ }¿ñÄ‹3ßž^ø»{Ô«êÛ8ºýc,µrÛ—o:hæ¦Myåíþý÷öŽÁ·a{àáêmë_Ø»…ú…8`ø7Àk”ç ¬Ø¨Û%â´Þ,–9 IzAg²2=©Ÿ“©S6hNÙÌÚlËq[ûж6»¶í8¾gK`?Ì8àtd9ZtIHb-©HÍ‹ûf_áX§¾wpß¾½/‹ž³J ÛãÈ{ã^zšòZÍ姯 ¨döa¾Æ@½}™‡×A 9ÖuXÐò²_`¤/ÒGŠv»’ÍÞßjêÐvASõ4{óZÑ{aï–Þ7Y‘·•ëZ›ÜŽ]ªb×^¡'¶7­Û¾}]ÓöVU½V°çŽ;¶dÿú…¤ý÷ÿ¦½ý7÷ïOjånçܹwÞ>wî¯êÇêçAÏÇô~ùÕ; §CŠDßvž^¸›ò÷äEŒ¿ýÁòõˆ˜±Ø`¶µ×ËrqÔ7¦³e?3ü¡ô4Ýÿ¿?ßÁêÐa6 eÝÀÍ|_ÔzÿýM{L}¾îõ#ܶ޻¸-[·¼²­£AôìØR\ô%µ¡×aò¹0/}§ØVF¯ðϡÜ€u$JcC BZýûZ%d³ètb–MgÉ +ð°ÆJ!íí—´·®C‡^¸Ê +’T ñáYáUá«Ã[àójø‡á7Âõ •ÚëÖîºÙ¥¤MI£Ó^[üì+«ëw¬žóÐŽƒ“÷Í÷4Yqÿì¿LUöñÍTe¹-O<úê“ |þÞÓïGò.<ÐÀž6søÖ6sÁm3/ä;~ëàn¶Ç?±˜šæßë˜ÏñŸã!´£ƒÆVZ/´[î vGÚM{þœaɾõ¨^\ -Ð-Ð/ê L Ì , ¬ lõöß+¾¶ž»qzl ¬Y·ç馵{ö¬½‚íêå+S¿Ä6òáÅ£G/~öÎÛŸoVßQ/©_€3OŸí‰oc±ñøÅm€#ÜþîØØj^…_&‡!.Žd²[6a½pÁz->þ%ˆÇS#:™ãJ%z¤5veÜmîübgÇ^QÞÝ-—ÀuH-vwúm†Ÿ;×iµ¬òÙ÷p ËtFBÎÓ-z»ñ;r~ßyÁí +äôwŽsÇl®¦+’nmíÌx:öv ãE»¿ý‡[·ÈhÀϹ¼§hk0s«þ°$‹°ôK·Ó0Â|#ÄíSÇi ~!Ëc«Õ*-ÇéR)o2:8#fóSÀ©CK=bÈ vÛ±W:öƒB• +›¯r¬·a¾(tÑU—›à*ËMè*ËA÷\æXáCs¯ˆÖ®º\¶¿Î,é°ú»÷æ`_`¹ +€tŸY9nç”+¦¯KnÛþõ§¼9«äHÁâUÅO;ŸÞð—ß”¼À'ïíÕ+'Ç™bî½qÅæaa¯ wǘ¬KxÓâ-{\ûÎÒ}%l_™¢YÐYÈ.dÇu ²¸ –`µ›©¯`IJ‚kÙ«m`„û¬cifâé5„æ)‘h†bÃsp½ºtLÍË/Ÿ~¢¡AØ¢¾ÑØÑ²bܦ­¿çòñ0×ïÁ_Lf~ÊV±]žj•Œ{¶ÁOyÆÇJwPeOÒôêBB§»ªt¼FÝ•‡­[%еÁ{©»z¦µuøsu¯¿ƒ‹q;: +¶n}eW½eOIá²ÓUoœ4Ö‘×Q7×2D$ÒZ†Hk¯Ò2!‡It×°Üm}í™C÷ðv®¯ÿI¡;ÅÍähk·[Ãmãtt"=ѳš¸ñã#-nDóŠn€“Á|¼ŽÖ®2HŸ.Œº\”‹óHŸ¥+A%¸Œ”ñ3„R1_W‡jq=©çë„yâR´¯ + ².›Q3^Ïm"ø Âzq§ð”¸O÷šîCÝ Ý0w­ +‡Ýþ&ž†§½©ÞuÏoÏ!{®·0É ñ_ÂD­ž8QÖ“‰´ž8ñGÕ_½E=Qû¢î×±wîÜ1hŒ¤œÅÚ³L®­=nþþËeHì¼!p^œ—*3¸ !]vÊwrw +å,¹‚«Jä¹ ¹Â¡ÛÈmÖɇ¹ÃÂo¸·Éo…@Ó‘7²Î ‡“ÑÁù/ÞOð×ùë= #}{ÆE‘>BC¥]”>\1„“È@~ .‰Ö¹Q$wò©Ú»ZÝýy„Ö©s¹,þ![Ì–²tô9òDC!*ÂÅÜLRÌÏfŠ3¥ +}a†±Ò\‡êð\n>¹Ÿò] ΓH÷éæêèëåÙ†ùÆúöؼ­Ç븵d3ÿ¨@ßšlÔ9ãš[Í;мÛFžæŸv‰»¤§uیϚÍ=G^æ_Zõ¯šÛ¸7Éqþ]a.Ó LÿÃa–Ûúé'g>ý¤U={æo_íh&3éq½…4·Ïv4tÄ€‡;Óú:“·^¢'Ç&6Änƒž²M/cz2È 2z(Lª,ñ˜×q®˜„Ñ­ –έ]6­綺®‚\›Íû¦ð7«Äw­pƒÌó²ï#åÛù~òD~’4Y.‘gãyül©V~ˆ_,oä·òë¥GäÕò¼‹–ß.=)·È2á°ƒq½Ÿ!šD +úÞÅ4'‘AB‰Ö›ãM$]HÓ68MyÔZ¹<2IÈó¤\]®>Ïeª4݇˜Å뤧ñ6iŸé·¦M7Lqt»ƪW`–|‘zÞ}F=¤:ƒŸW«ÏàhÍçw|Øñ:nUGq£9/õ^ÜÈ|äÔ—YðJçpIÇémÈBÙŒÅl³ ‹Éf4!z2›Àp60ÛT“AoE¡¼l6¦¿•õ`­: o1XÝÐ1¶º±Ý mÐg\w½›±õxñw“5 +_x'Pž_‘ õÄä%{›¬¦0ÓS†<^gš¢Ÿ"Ï”L MkMv`i³Á✕· +Þ²§ÁÓègö³D¡pˆ¼ +¯Ѻ^ú9ÜnŒ2õ6÷¶(¶Aà-pñ|¼p›<Ð0Ðx›)Éœd‰·¥ 'vrNâä. LÕ§É#Mæ ‹Ó–ƒîÀwpIŸò™ò™¤ŸV8јgγdÙJp W*—™Ë,ù¶zÝ}æû,+Ѓú¥†¥Æ¦æ–ú&C“q“y“e›a›ñióÓ–}¶ßÚ>´Ý°ƒ,3Ö–iɘ½àÖŽ[wÿÚY™9‰!êÍá–¾3oÓ¨e9ü¸öud–—'Cžud©G9ýtÚ~p0—TÝ.t˜ìt#kåiƒ¶ýÑm ®ßz1+iKhûÞRu*õ‰‘ÜH.C :‹Á‡øëúèÃ@’¤‹7P~¥1~ ×M"yº» ù8Ÿ+!ù|¾0]·À°Ðð¬Á¿G±ú^2³#“{¡}>÷BG1Ÿ¿³ýìÚ$‚þm¤îç½a=Žæ8ûûZ1Bïgu%„ Ã.=Ú…_Ó;vy<aÔËB¸—/ +”Î)¾Ãe‹A‘´µçºRm£k’kohû¥6º¨€¼ÀžÔ¹ 6)ï‡P:U ™ÉíØý£h ⪪°=|4W潇}»kÖ÷ßÞxÏ®o‡x(÷ΊÊ)¹½²zÝ_®¯m¬iºòÁÚÆÉ}óØÃ¾þoþæ¡É”6^ Ä{Å@XkÚ_Äè9öK>íïFÐÓm!{w‹ß û‹<ÝyÑàáïe´èü _ /(ùîòG»Â^ó·ì²=àëç°`HÁüavù;†ƒ©ò鯉›U’x3 ÀSnÉí£6× ÐxãbÈ XÜÉ!/Ê.õ¼ÓWMœRY9eâªôäoŸšõð°aÏzêÛäWr¿Ùü°¿ïÃ}óð¤Æµ\iªi¬]ÿåëØžj|R8Kú¡@æ´b«¿yð›ý=6ƒ%ÈJ·ÖYOµŸºd}M“+{FEÒOgáØÛ‹~CálÙóÉòè´É{¦Ó«µ² 3?:-wé·üˆ!ípÎD5üIí,4Â=OÔÀŸ¿qM8‹öS˜Òçh„Pn‡{íp¾‹ÒBq†óÛl~tãе›¿ˆêaì!¾Ý ç{ùKè^î=GÛ‚â’Ðë\Ò³üZ[:†Ñûü'¬ÿ!ÚŒ†ë>¨’„¡Aðl/øµåÂymó‰h²à·óôLç9…¢wП‘ŠWãWð%NæörÇᣒþ¤…Ïäÿ"zŠÃÄÙââÉO(eJùÒ2éXÈ.Ô]Õgë÷ËAr‘üÃm†Éàñþlôƒu]º©Í`.5ï4ÿÑÂYr-XvZþb­µ6Û«}®ù¦ûîöýÂZ¦_›ßÇþ#üúÿ> 4 :`wà”À  +šü+Ų*äZ¨=tyè»LËHêƒJ‘‘íôÞH½ ïà¼àLÿ•,°Üúº'¸Úr³ó®6Áã[W<0ŒÔÚ<´»Ú2rù®¶ˆdn‰«­C6±Ö6 @‚\m“ý±^S\m3ê?dº« >kȳ®¶ ñCÞ‚1¯‡añlvÚÆÈ sµ9¤Ã_ºÚî«®6¼¸PW[@>\º«-"O®ÜÕÖ¡PîaWÛ€sm®¶)b0 rµÍ¨tð7®¶y YïjÛnÈKh8ªDUh.ªFeì¯Õ"õB…(Î (>‰Ðš=” +}jQ Õ¨ rw3Pô…V +šewªaWÅp.†1³á»zÊ?bÖ³æÀL³a.ú—µ* 7Å£Æük3Ž€ÖL—‹ê G!ô-`ЊوF‘P*à» +úL¸eÐOñ•0{{©åðʪ¹Õe3Jk•^…ÑJB||¢2}®’ZV[S[]\P£dTÆ*)³f)Ù´W’]\S\=»¸(VþÎÐthNÁìò™•3”Ô‚Òï8¢xfAnRXZP1£¸F)¨.VÊ*”ªºé³Ê +•¢Êò‚² +À¬'‰5p[<¡ .R˜Jt4*+ïùqC~LŸ\ÆíàQ%ã`ð<‘ÖZr‹«kÊ*+”„ØÄA=AÝèVs•0hšLk]çž·¤²XT GLîµ µÁ(>E.³F,Œ­„s5H²˜Á«f2¸Å0•ÖÖV Ž‹+ ³ëbk*ëª ‹K*«gÇVÃãôn¸uÄ­§ßµúŒê]1ÓÝbРJ4úRMýeôB OæBŸR6² žU1ºj™®S®U³Ô:(ÔÙ7qòf:ºì«®‡}}5t_Ó­h×t Zݹö]K—Qߟñ‘”÷øå}Ö­åÝEs<‘Y«–Ý¡ZXÎx}Ü« ü3\(eY ^9ƒÖeMe §Rö¬ØE× 6K…Kê1.¹kÒÒfÓtLÓ÷†W%“~_å²Xm†J€ZëÒ±2—0§eÌZ†ÅÍúTÈúQ=Ô »!ÐÞîš.3ƒ×t/´›–„2ÉѱEì\Ãð*„1.údf… ¡å J-{âæO ´f¹,©W'Ž]3P¯Eñ¯ýÕ´ŸÎØÅz§ŠYMÌPÈF»±)bÔ2]›OkÙSmùfˆqYs!`VÇ h<™Ãt ”y¥ZgÊÙ½î¹i¨î¡•¶uŒ‡1ݤCÛåLžš¬ån¤FÇ|1tÆ1¢0Èš=h°Ë\\í)ý¦ÚÍ9 ÛªN®exui]Es?ÊÔ nk(a^½ÂEaq·‹Ø7#†)'fBBOëã–ÕãY.Ïæ–P!›»ˆa\æÂt0³Îvô/·V2ÏÐ%ƒî¾¨‹ßõпÖe 5=úºm¥‹cÝ}@÷q +£¹€a.3ßÜS×4nh±¤àäYÉ¢ â’}9;wù#‹Z‰hd-pQÛƒS?4–òd®+¶h³Sž—0‹\š4‹éiuç SÊÓ¢n2ï®uîZÀ"b󳨕ÜIQÔʫ¢7fôˆ«ÚLnZÀ´GÓ]÷7ó§æŸÒäÆRvQÐ¥aLF?ƒžóÜÌ[áã’÷,6®ì{¼¹Ü)jæg ˜_é‚ë¾SÓ©‘n{¹9z»ü\1£Â=ÓFUz‹xÚI÷Í#dx掶¡Ý´L³™Ì›âËtfï•Ýp­sÙ[OfÃÓ²[p¬ÝÇø\á²ä*øhÑ«€yÔâÎÝå®áì¾#ßÒRJ™‡Wعƅc1Ó¤ïÓ·¯»•ï.b‘ ‚ɽ;¿nÅU¹çºËð§Új óšîXÝemnK¢™Ã¬ÎÜ£Ú5¢'Ä*¦Ñ÷À÷ —Ä´xHµJîôªÿNOõýTMwÙH­+–trjJcóŒGãàŠÎ3®rÐ$È#³Ù³ ¸§@— OráŠþ}ðL.)ì }ʬq´)Äñh"ƒ¥ÁȆo +;îPØ +»¦Wc ÿ8€EǦ¡ÉlŽ4€60m +{,ÜÍ„sš«1îL„kÚ‰hªÍGÿJy³:Žâ¢aš÷»fí‰U›ÑÙX¸Êø£\Oé_DÏ`ð(þ1,?¢íq.<5Îe3è”G2…90ÊdWôîD8gA¿ ŒŸ)Œf ÛqŒ†tx®Ñ’Æ0Ð$¡a4œýåõ<ÖƒþMöÆ:SŽ«g “#¥gOgÃzi˜wI™¶» Äºx©áAùŸÛ9óF&|Fû«ïT6)ß ×­;#Š·Ì¸1‘Ñ—Âø0žÍÊúQ.R~fvj\v7© gü¢r£˜`3¥0ŽL¸%%nhÝ¥s+í;gÉèKcœÊd½'Ó FçM3­Ã]¼Ö`jz¯éDf7îg4RÉÞ³¦¹t*…ñ®'TN“þ]ThHq}ïÆ³.ésI×O›9ç\™Äl1õJa²žÐi#éÌ~Ǻ0ŸØ©a]>`¢K?ÇwbÖ“¿n;r÷û1¾Cƒåž»§G0}Êta8¡“Zùàj¾+ âZ![çÔvúíž‘»{ÖØ•vÏ;cºùÚæ…G²¾å7õ뺫­–´˜ÕµÖéž»Ýj…í^k¹¼;ëíÊ>4ß­­‰ºg½E,?×rÀšÎ¬¤’å•™Éö´+¦W¹j'•=Öytæûc:çrÇ¢.XZ^YÀ²:[Í-¸ùýJþÎʰŠÅ{m–9¬]ëÊL(}u®¾ôþ¼›VÃîúÏwe ÜRnZn•9tç5“w•k-UÆ8LóÉXÜjä^—uñ„r@«»•ß$õ.í£Ð£›« +”3ºa^Äx-#­†Gç”™¿r׸þï«N¿tÍú¿©$÷¨ÝœyýûêAò-ëAʸ$ÿ¨zPÏL¾°N]µwÏWA½U…Eþ?«+)ß©+Éÿ]©[]©«Âðÿͺ’Ü#ÂþßÕ•ä[¬ÖþêJò-ëJ]ýgêJòÔ þ3u%ý«u¥®·N¿d]©ËÞzÖ•¾/ú~uI[Ÿk™Ä[uIF=«K·®nügªKòpWéÆÁÿî*“Ìtì»Ù̾Ê$ÿW™ä›ªL]kÝÿd•Iþ§U&å?Ve’ÿ…*“òo«2ÉŒ¹u4ÃVãv +<ÿÏÕŽä[Êüÿªv$§v¤üŸÕŽäï­uÕ€þýµ#ù_¨ýÜoíÈíY¿?¢|·â#ÿ„ŠO÷*Í/Yñ‘VÅç»k¶ŸVñ‘»U|~¨îðKThj¿߉º* 2›‡^Å"”Î6hÑ­jt³[çþ8¥WMq±2½xVåœèXåGll‹UFΚ[UZ£”•WUV×)%Õ•åJJuñl×&0÷l#]¶‘®û4²Ü5{nqu¢¡Ö¹OîûƒÿäïîÛûÑ[þ”›f.«‘ ”Úê‚¢âò‚ê{”Ê’›¡ÈrVquyY Û4WV£”WÃ\3ª *€ô È‚aÀ±êÅ1Jm¥RP1W©*®®•ÓkceÀ‚¥–¡gmi±›O……•åUÐv¨-èÀåâŠà^(cIh4+R +jj* Ë +`>¹¨²°®¼¸¢¶ –âSR6 „Ô‹Bd” •%µs€ý¡Ñ “êâªêÊ¢ºÂb¦¨ +›^W[Lq{ ˆ1Ϊ+¢˜Ì)«-­¬«dÊË\Ѫ5VغèOɉQÊ‹)Õ2SšÒ˜nsÄÐ9ã*«•šbô.T]äß45EÀVQF×ÊëØDsJA±¾3€Š¡¤®º&,f‹*•šÊ¥¦núÌâÂZz‡ÒWR9 ”TXYQTFé¨,Ë9®`zåìbF¦E N%¨¨¬1Ôhw©Tªº4@{¦Ô”Ìš%O/vq Ð+)èAgeèEµR^Y]|K²•Ú¹UÅ%0Q¬†TϧåsÁZ`xQYIU´‚Yµ zРEEŒruÔ@ ª¯ºYÕ2¨¨¸¦lFCc†f«0ˆjhA!©¡#ÜøÔÜ<)ÃŒa³n À5ÆG4@¯bÖ\¥¬›šË”œêbú¿ºf}i£†2’ÊÅmÅ sÅÕlМÊê¢%´ÓCéÜîr(5ÛPÆ2L¦Ë^¦ƒ%Q¨u ʓٕeˆßW £TUyLŸULh´dÚ»„RZP«”ÔÄâŠ<¡Z×¥ÝEJ]E‘ á.Te†œFáIµ¦rµj&6*¤eõ`+îŽU…÷ÌÂÀ+*eªªÿšRõ˜ + X<«„"5*MI?.G™0>=gRJvš’1AÉÊŸ›1"m„š2®Cc”I9£ÆOÌQ GvʸœGÉÌ›‘@sƳ¡.Pi(°±iÙÃGÁeJjFfFN^Œœž‘3`rÙJŠ’•’“1|bfJ¶’51;kü„4€1ÀŽË—ž ³¤M"ÐðñYyÙ#GåÄÀ ¸#çd§ŒH›’=&F`ãäl…u‰,†’–KO•’™©¤fäLÈÉNKKûRîŒ7~lšœ>~â¸)9ãÇ)©i@JJjfš†2<3%clŒ2"elÊHJŽ{ÚM#§‹202m\ZvJfŒ2!+mxm3²Ó†ç°žÀ{àD&CwøøqÒî˜7 Ÿ{ŠyÒ¨46ÿ g˜1òǹNÎøìœNT&eLH‹QR²3&P‰¤gt©<ǧ3 ˜ü¤Âç—ʈÞû®v@/:ÚEàˆ´”L8¢7ä}A»Òî+,®ª¥ºí2nÍ527ªùΦµšY†«ÝcMK`Y,êhÞ­+`Óp£¹^æ>@»!i®·hv1xÀêJ*«åJêLæ”Õ0K‡X^©Å<¥¦`L£¨±^à+ fÁ°šN4{”ì†UÕe0dNuY-8¥ îV—Ís…ájW˜b(]ÐYºœƒ†uqMD©²ÙųæÆBßjË&e%•Õå.Òû +k»S…Ze^TY+WVψUd™e\?;uú±?yøeò Y˃”Ÿ’É]yòó ù»yËÉ2H5î˜q‹µ+a‘N®¤¸s%ù¿#W’59üÛr%Y3ØŸ•+É¿`®$wåJÊOÌ•äyÁOÈ•äïË•”Ÿ+ÉÝr¥îæÛ#]‚xNâ—J—dWº¤ü¬tIî.[7þÒ)“\Q©üì”IþES&Ù•2)?=e’oN™”Ÿ’2É·L™”%e’sRrÇŽOÑNõ“²#¹‹òŸ“ÉîìHù9Ù‘Ü=;R~Rv$ß2;R~NvD•µ‡¡t&>ò÷&>Ê¿øÈ?œø(?"ñ‘YâÓ3wøç M­»¿“% r,œbÎoãXÝî8âXí¬ˆ½Õ‹eïW«à^Ï·…?ü ø9e÷”Å•³º/¶ª´*Îå1Òo9‰öèDSÐ-þµr 7®«äš'ù6‚|“@¾n&ÿ0“¿«äªJþ7‚|e&k&W"È—¦_ªär3ù¢™\ºFþzüJ>L>K%UòiùäÂá“fr:^˜@Î'œ¿F>Ž#©ä/*ù0üÙ“|ÐLΩä¬üi>9óù£JÞ‡îïÏ'§ONÏ'§F’“¿÷Nªä÷þä=•üN%¿UÉoTr¢™?$Wɱ òn9ª’#Km‘ò–iSÉ›*yC%¯«ä5•¼ª’WTò²J«ä%•²‘ƒË"„ƒ*i}ñ%¡U%/˜*¼øyq!àשÎ䀓ÿuyA%Ï7“ý*yN%ûTò¬Jö‘gÌdÏÓž"òôn»ðtÙm'»é]×ÈN•<¥’*Ùn'ÛTòäfáÉò„™<^DZ KK3Ùª’-…-*yÌH6?ê+l."n² +ú’MV²Q&T²¾Ù$¬WI³‰4Á ¦f²n­YX׋¬5“G®‘5«_Ö¨duãTaõKdõB¾ñá¡q*itòG‡T²je¬°J%+cɃ@æƒ)dÅrƒ°Â“,7¸ÑPD–§–E¥6ò€J–,¶ KT²ØF©d¡J¨ÄyãWóç ¿RÉüùäþ"RŸãê#È<•ÌUÉ}f2ÇHfˤN%µ×HÍ5R}Ü{T©¤R%*™BîQÉL[ª0s)SIé|2.JTR¬’"•ªdºJ +“ükdš‘LUÉ*™¢’¼É²wL–É$/_aRÉUÉD˜yb*Éq Ø*Lð!ÙžäŽÑÂ*É2ñ*7Ö*ŒSÉX+ÉTÉx2F%£3¬Âh’h2¬d”‰ŒTIz3Ik&#T2œë+ ¿FR_")cˆS%É*v»]æInjn·“ÿW-™d%A”æS +e7mÓˆ2ˆÚŒ2*ƒÂB9K]Ã¥KïãÙz‘š'pë3v‘ù^F¼<ì9¼~‡ì/³°Û&f—³ÝDf›°Y[³‰X[ž:<:VKkVÂÒ²˜[³pÌ-³iÅÌ"¦VLÆ™™xƣ،3F1ÃAf†o 2ú™5ýÌr/Ü ·!=ÍÙ‹¹ñts:¡ãi;ZÚ`K¸Î¹:ÒTÒ.= mª!¤º”6© ‰p!Äjˆ…šf­‰Þ =ç‚«¦Æ UuWS¬pQÊj+ § 'ž’Š%ý€::E(*/N " +BðøÏ`òPøë~Eû+ !’endstream +endobj +98 0 obj +<< +/Ascent 759.7656 /CapHeight 759.7656 /Descent -240.2344 /Flags 4 /FontBBox [ -1020.508 -462.8906 1793.457 1232.422 ] /FontFile2 97 0 R + /FontName /AAAAAA+DejaVuSans /ItalicAngle 0 /MissingWidth 600.0977 /StemV 87 /Type /FontDescriptor +>> +endobj +99 0 obj +<< +/BaseFont /AAAAAA+DejaVuSans /FirstChar 0 /FontDescriptor 98 0 R /LastChar 127 /Name /F2+0 /Subtype /TrueType + /ToUnicode 96 0 R /Type /Font /Widths [ 600.0977 1000 500 589.8438 1000 518.0664 518.0664 317.8711 500 317.8711 + 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 317.8711 400.8789 459.9609 837.8906 636.2305 950.1953 779.7852 274.9023 + 390.1367 390.1367 500 837.8906 317.8711 360.8398 317.8711 336.9141 636.2305 636.2305 + 636.2305 636.2305 636.2305 636.2305 636.2305 636.2305 636.2305 636.2305 336.9141 336.9141 + 837.8906 837.8906 837.8906 530.7617 1000 684.082 686.0352 698.2422 770.0195 631.8359 + 575.1953 774.9023 751.9531 294.9219 294.9219 655.7617 557.1289 862.793 748.0469 787.1094 + 603.0273 787.1094 694.8242 634.7656 610.8398 731.9336 684.082 988.7695 685.0586 610.8398 + 685.0586 390.1367 336.9141 390.1367 837.8906 500 500 612.793 634.7656 549.8047 + 634.7656 615.2344 352.0508 634.7656 633.7891 277.832 277.832 579.1016 277.832 974.1211 + 633.7891 611.8164 634.7656 634.7656 411.1328 520.9961 392.0898 633.7891 591.7969 817.8711 + 591.7969 591.7969 524.9023 636.2305 336.9141 636.2305 837.8906 600.0977 ] +>> +endobj +100 0 obj +<< +/Filter [ /FlateDecode ] /Length 677 +>> +stream +xœ}ÕËjÛP…ṟBÖRœã}KÁri ƒ^hJçŽ}’ºÄ²íAÞ¾{y…ôPJöþ…tÐ7ÛÓ«ÛëÛ~sè¦_ÇÝꮺ‡M¿ë~wWµ»¯›~RfÝz³:¼ÜþWÛå0™æá»çý¡noû‡Ýd>ï¦ßòáþ0>wo.N×»ëúkùãx·ì÷ï/wOë·“é—q]ÇMÿøŸWîŽÃðT·µ?tg“Å¢[ׇüاåðy¹­Ýôßçþ¼õýy¨Ýìt_ˆ^íÖu?,Wu\öu2?;[tó¸YLj¿þëY™óÌýÃêçr|y÷,¯EvizÖ´4­M[ÓÞt4}Þô‡¦/š¾lúªéë¦?6}ó§Kã/¿4þÒøKã/¿4þÒøKã/¿4þÒøKã/¿4þÒøgôÏNMŽlúsdÓŸ#›þÙôçȦ?G6ý9²éÏ‘MŽlúsdÓŸ#›þÙôçȦ?G6ý9&s¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¡_àú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~¥_áWú~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~£ßà7ú ~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwú~§ßáwúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àúþ ?àú_6ÄË&À®ÀÚ{]D«ã8æŽ:íÆÓâÁÊÙôõu}»§ðû "‘£©endstream +endobj +101 0 obj +<< +/Filter [ /FlateDecode ] /Length 18175 /Length1 35128 +>> +stream +xœí½ \U8~Μ™¹3sßÀ½pyàQDÑð‘E$Äg>@¸ +\â¡™š¶½feššå#33×HÝ2S³¢¶‡nµi­™™›nm‘µýìE0þ¾ç̽<ÌÚ¶ÚÇÿÿù1ÎÌ™™s¾çûþ~ÏwD!dFËAã'¤¤M›¼æ^¸óì“J«KjåÉJ(BxìÎÒù êåO÷“âò`/*¯S}%7ï „øÐÇœª…å«~¿ín„x>4©Â[RÖknÞP„2¯†ç+à†åqÛpÝ×=*ª®þî@j\øËª|¥%—Ý7¡‘Eðüùê’«kŇže…ÁµZSRí­‹»ÿc¸¾ ¡aéµ¾ú† 'Ð4„®«¥Ïkë¼µ‹K6×w!dxa~· €Oa-Ì­ŸÉ;¨œ  +Œ!"ÏqüG¨æÂŸQû¥Gq"TSAyvR‘zá‚¢…àu†jüa1Â.\@úF˜MHÄ +k-C¿ö#¤À¾"2 ÉHAF˜ÁŒ,ÈŠlÈŽ‚P0 +AäD¡( ¹P8Š@‘( +E£À6Å¡xÔ¹QO”€z¡DÔõAI¨/JF)(õCi¨?€ÒÑ@4]†2Ð`4 EÃÐåh8ò L4DY(å Qh4ÊEcÐX”‡Æ¡|4 +P!š€ŠÐD4 MFSÐTàü•h:šf¢YŒ€rÜ—£ýèïІ6£V4q¨îÒó\„Zàùlèy#.‚s5¿qð|)@p¸?𮂖›ß‚÷£}è,Œ¾/F ÓhoÆ) +ëkáü…Áe )|5?ŒßÅ_Çï‚|9j‚c÷&¿ž_Ä¿Î/BS(f8î´Áñh·gaÎ⎠çþÃñ:Bs1Ç(ÒW8Ûhš œ99á˜bˆå˹oQ ¾žÛÊ}‹ã1[ŽnÎ$Gøbþü­ð¸ƒ9ÒŸÄpœN{Çð:Àâ´XŽB?º-‚yZ¸¸½@ãAtè‚Ù¹éÜ"n:‰wâ}€1B7â|±a6Ö‰ëø)èå z“;ü(`ü¸Ý.öC_ó"ú‚äáb~år Ï)ÄƈAh5c¸(AdZzŠÐ+ Ïéô’Ä(´šO î·$À7¼á2Èl´žm+ñ^´íEõ@žOD'FIª­‰sç–5y®˜¢¾<5¶oÒE—ªÍ 6¡‚&óBuï… Søaj“ÙDÜRïŽ?ýcO÷M[0EÝ‹{egùÁfgÁÍ S I¯à6ÜÏÎbÏè¬M‚þå7©¥êm¶ÛâßfóîK5][Í— [ÀŽ (f?âqO`ƒˆ{>‰%áVŽG)ÍG[ú!ÛÑ–£-©ÁöX»;Ö[Σ¶zÑvV[m°|ûe˜ÈtÇÀáfÐ{í3€*LwÎí@U¢püO ëéçð=‘¡†`«ñžh‡LÐ +»9&:*2"<,Ôd·Y-f“"Kº$ÃÅÕvôp(Å}è™´£Ca6ÿ1“Øàþ$ÖËöø`¶§Ç²ÃM²¢K%'K°C{2GjMÅ'‹µo‹OkÇ£s´³¸°hßæ»jKñuÚÒcÚ¹w´ëðRº¿ƒƒŽáëÚ·kç@IÑFí-ACÀgößX煮îˆ0{hHcT>L”“â9aµ¸*)tuتˆ•A!I+!fôÂQ)H°ˆ½zØe‚ }"‚ JCÕ4ÛÑæ¶£¡CÛB‡mk¶eÀ? °¹nŸo±}uΞ[Ph†þ((##Ç“dœ`Æ¡ö„dœ>`àpÜßWz74;BD±[àì ¶_z’ˆÍxÉ'¾úOÆ~þÊWâÍe/{៪9ÿÍæQó¿˜<¹@{ ÷’±8d8‰½÷<ºûù£¥¸­wŠ {=óÔÞ-døìô!#µ'´OðˆY#AÓF\øÐðø##D˜ˆ"ýQ©'=ÂîrÃÂ"ÜèSòqk{=.>`Z´&2|ýW¿>2‘²,YûI1rÉê`{¯¹È>÷5¥›rÂv&íüÐ3ìŽí\*X—Á&|F÷©q8Nû§  +Òœ@v|\O`¸¾þ±]®C»ôûÊ7ztÞ¸QûÞ~{ß¾wÞiwŸ$¿ÿýò}ÇŽí£ûèüüÑ£Çås‹êê-ª¯[´í/œ8q`ÿñ¶·DóñÞ}÷ÀãÛ×Õ/YR_·X×ëE>·ý=Q*è™jÂf£U–«$KI}“Sø>©VŒQ/œ˜`…xíNM0ÏûÊ!ˆ¾;ªï&K솨Ã×[ÖðrBoÔGÎS{D…ôîaŽ +å± ‡ö6ÛMýlm-ÍG¶Ø^:Ç”AW{Ó˜—Î|ý7`‘ Çt%4Cç–å3hvœ€wmMÛ%%`4Ãsƒˆ¬ÄÊ˲U±åcªAÄà]r”eŒLu£%Åèî9\nôÆ*ccSƦNÃÓ¸‰†iÊ4ãÄ^Ó“§§NI›•V†*¹9†bC¥Ri,ë¹,-I6* .cxBoc†1=25*5:=ÆÃ1æ&äöšŒ'sÓW&Ì!UFÇ ¼Ÿû;zô÷ÚÎcÄ×u%€A±>®¡ýî”vRç)¬SØ\ÙŸBÛ8:®4)z÷7iˆ… –еÀ…\ Ó±à§8Ð0a’ÒÌÄyþp*$ŸñœåýöœŽ}WMýÌì  +)~?ï –wXÉÇzëš0ä4…ŠÎ 0˜‰)èêDS±­'˜ÝF ÛP¬ŠììHvV.^w÷÷/p±¹9˧½}Íï´k° '^û*Ž€”"ŸqmVå²qyxtŸ¾-o]óÖŒÆ[/|ÈŸ{¡rÛ$‡»ˆ;8ÜÓÃMíöŒí\3CŠõÜÛT¨]2¬Ç@ šÆPÕ9ÍÉõÂO¹×‹çÏÚV°ãmĺ+Ïb—ö’ö­vR{7âì9Ïs§¯÷ÿpG´–¾}žÝ߯ŸvþøÚ)|+®Äuø•ê&Øß9ੈ²=¡‚‹#ï"„s ÀNi8ÁÈ`{OÏÁÁûuËÆvÆ3ðxK€?AÌ—Å£4ÊÓ+Ì”°‰;¢QS_°òè5©aÙDz„[{‡;zÊá $ÜÚ3¶G*ø8Ê¢ @Šx¾å Í|2ºsÌMÓ‡±º­êæ¡ö€„"8ÐØÉ½RÕÐP5¯®N[|Ëm8ÄlÅá·ß²æ~p‰ïûÞ¹ÿËÒéÓfÏž6½”[?¿¦¦±±Æ×¸4qûÒ/½xpéöÄÞîzÿÃß¿ëž8µ¸xêÔYÅTös6 È>L—½ÁµCuî*~èâÛzçw¤ÛŠâÂÍLöÍmmgtBš=H×ÃA ÅA2w\OÀ=2}[áŽCö¬uÓÎjá lÀ=°G»MÛWy/õ–ƒR”—Çâ> ï´4l|÷8N›¯­ÑîÔ¦Æp箿þw7Üð»ë¯gö|ù|1[ãEz,Ü6´›ß& +–ŸÁ°Ä‹¥ÕóèvWj÷Ñ/Ö–h[! 18×A¬¥pèúÓí – ÖÒ€»[!<[‚+ ‹ °…(¥G^?Èo!úR¸þÌ[t¸4/&œàÿºã± 4MÈÄ=ñš±¨ÙIˆÌ9p:N´ %Í­šÀ‘#ÚíÉVüz ~]Çñ8NN~ZÍèîIñ^0$–Þ°Å6K +l;.pR«FN|úÞg?ÄG ?ø?à=f @:ð NÇ!ƒ¼HŽ´jýZ´~­àÅ(.ËÁæbÀ¢P,íIˆ +8^tYÑѶ»­«L›‚—G߮ЂŒÃ]HæœÖÄEã8¶Ž?J£?€h¨Ûø ‚ÍÃbÙ²°[ÌJ£v`Å@ ÀjîEí¾s=éÅ“®Ø_¾ðåç1ÊŸéÉê­ËÛ¯péìÁ¦'çŒ9îÝëÏξjÆåcû>G«ºY[oð Á~Ç¡*Ï`“• HÍ×ßæ’#È^kÿTy¯Ë‘º×úÔ {O\‘ß@^¸fêŽzE'†÷Ê êÕ;qÀ°|š.@\M°iC_ +b‘Çn5Ÿ³ÏN³û—;ô˜Š»˜/¦ÖÑÓ¿HHﯯvzö 4³ì:ÔÉS# +ùø¸ zÞ=°ð‚®“ÁœâÉðÊiSçΛ6¥¯‹¾qêŽwþòØÔ£ß\|çà!³´·6vïÃó¼e˜¬¼¶mê¼%Úñ5Ok{—-»é–k¯Åã÷|€k§íÓÞIà"ݹ⚅˗kÓFÿîå—[ F]ß>6ø•ÊϽæ†aCfk¯üa•ö}Ùì93 6—̹~Éœ{Rã%‹oylÓì³×jÿÐŽR¾*‰ àƒ ÐÒ<% +†(Q”(I@h›‹â †]Š"ó¯ ºïr‡¦GË”e’R®,EKñRi©Ü Ü¦lPž…íuØN)¶ )RŽ1¹¥Þ²jÊås„QÒhy +™ÊO&‹¤’/æˆÅ¦´_Ã7 +‹¤ùVþ&á&éVy-¿ZX%­“Ÿ”ž–_C/â¹× ÏKGäãèmü6wÜpLzWNa‰‰Ål#|NûÖYÚ.¿Æ%jKڷᵇ±MûB8ÖÚ‡ss…Ô® Ú9É­À; V4**ð¸ƒÐÓÖƒâÓáËå§ÃDXLE›¬rXo Îh“Óa38]$üo-­ÙEÝœ`ËHõÈ(ΗWÇãØŸVÙƒ =ëJ„eˆž[]9sæ•ÇþÚÐØÐøWnôâ[´÷´·Û¯ãFàA8´œ¬,Èw…ÖÜ^?»´¤D[ȹz<¿ü/G…cû_¯^Ë|J9Äéà \hˆ'Üd“‘“˜! ÜIl;åõdMxp_û„Û`5HòµfÛ‹:Ö©{R"fEpx†›­Ÿõä@_9SK f LŸû·%ÚíZÞƒ—ümî¼7êÿÔÒò§ú7æº oÂ^\Ž7]6H{-7Kûöã´o³r™¾ŠŒ¯¡(Ùã@‡äåøS✠+úÚ’‘S&úʼO€{©» +\`»ßX1°(T_™ŠÀ1nØm-ß}ûYûWx5.ÂãT–—W^­5Á6—ßÕvÕ'§ÞÿÇ—4xµoyTûÆÛPBùxð§#Jõ„ð‡ÄƒÜ!´\:¤`A‰"bb¢lnÈ/õI›¹À\kD-ºíç­íuÜœöµÜ–ïO@wRûöít§ÏñÌ!£>{Çf(ú,ýaàÆZc7ðÀµ¥Ðí7ûeÛÊüüXÛî ˆ7üéÈGFG¸ú™ú£”à¾bŸh˜ R|'›Ùr6¿¨©´wÍŠöºÁ±ùeÍw—5M¤…Ö»´Wfß· +Û.ùfñß¾ûâäÐ9‰ŸrS|99Lè•øA*ôQ9Ú…Ï?Ó4« ÇÑ ŒÂ׌ߗ{‚¹CÒA-WDà„£v³Ä`èù¡ÀƒÖ’¬‚U´¬R¹x¿Á¬\Ä}|w­±¶¿LYÔþG.lx!0 ùç䆱5qÒSè9ŽÎëç±MÖ¢±M¶¢+a™‡ç²©ú‚?iÁ”{tåÏXø ¸©IøïÚ½Ú¶óÚ6m¥p¬í4‰iíÃgµ îï÷wµ-öDÒ7p!qS⟓¸~¼¡ŸîGXaB§:£ËZ˜g´hmv€""£K¼ ÂcÄIxªX`œƒ+Äbã>ü¤hqqá†Ë¹þ†\ Þc˜ÈM3”s•#=fït(Ú;¹\­¶Ÿf#ƨÍÿ>|’Xî¯'¥y‚ÅAh§i}К0¹¯uéëèsQ=ÉcD®Ð`›+Å5Ü%€o÷;Zu$ÚIyÓóÏ7=ñüóOà +¼F«€ÇZm^Ë×ÚZ>ÕÚ0ÿi æq¨V¦­ÒVkex=ž‹çáõXÃb¸‚‚Q?Ó´Ó`܉–ËÁf–l}y£†¢;ž@´aE¦]È)é4ó`«±‘g?·Gá~° øP£owŽÕ^sM-˜À'Ÿ¶··òµYÕeeU:?´cŒVf{ÂÍÎȶӰ­±DZlvõU÷åûD‚ËUuï Æœ$™P”Ëd‹J@)Q“LÌå¦bóÓð‚fƒiªqªyjÈ”°¹ÆJóÕ& ä$Äw0-„¾?~~®ˆÖVhåx-ž¬YõΫ¸¯v·öaÓó÷c#ð\EY̽»M»wºÖ$òÀÜ/.è¼õÇlÄbv$ºÙ“ÎEº¢œ¡Î°¨ÐP§ÛåT‚ÑNYÜiZª8ƒÃˆ-Ò%"Þ ¾=Ô&œF¥;+ 24Ãož”Ïì T—…¿þ.!,’ÚfLDX„+<<""¿K ƒ¶`‡Ú,ƒ…C–52Z$9• ÿ‚:¼IÚyêòSŸô×oÖó'}Ygõ[Ä™…·Þ¶¢íó°¾zF{Ü×uOoØð4YÚvö‚ö*ˆ‡é4±õ’ˆ®÷$Z3æ¢0'ÐâD$â(èà¦X «t,°åùžÒþe8àKÇìIBœS¹&Ž÷CBq ãC…I•F¡Qx4Íg cÄÉx +Y!ÙAÇ*øz|¾_ßþŽ–¡rŸßÚG³Ã’VÒx‰«=cÄ(ˆ— ñRhT{y ànÈN(+„p\áLÐè0¹H*DF%“ÖyúWF2SùÉET†WŽm2Òƒ©3±9¯i—Ž®—¢ü0Q@{ˆ`lŠ[Qa ¡v°0\è¯¤ÂæaÛLn¦P¢ìåžPš` 8¼, FÑääÂH(ïRˆn 7%p½HßK€%£)Å2òƒ4>MHS ©Rš<ènÉ%9üh![c¤ë¿iÜ42‘Ÿ(Š…†Biš<Ñ8ÃäC>ìãjH_aðJò<¥ÆXiò™ÒÕò|ãÕ¦[ 7J·šžâö‘'ù½ÂÒAÓØ€d˜pà0 _ÁázØ9\£mÔ²`ý}^ËY½À£;ÄIß÷wë2KâX.ù…'›¢Šr ‹r#2ÊQ’ŒèI‘!ØKQƒzHè—YQ¹aŽxÅðYË`ÄÁ07+•”½ÓæšßkîÈ…ü²ê²4ï&²NQM‘ˆ•ˆ2’cˆMN!=eUJú˳H¼‚,“Ÿ%M²]æDÈBe%‚ áC¤¥‚u3ßCtK}@®é|º4X)0{Ñ\Ñg~šûÿiŸlÓ™¦ÿ#mœ¨ù´‡´­Z5´,øÈLrð½\k»ˆ5ãZ¹Z >Myv¡|Í)Vyzu·KÂIH¢ú,1»9?Xp¢Á9 ÅfÊ+c‹Á~ž¥ºA½R9WÀƒÕ*°@…(Nܸ7IǃIª2^ñ)T¹ˆ?9¶‡à‡!µ+Â[ÛÿÈ2ã.¤mcûí\£.{úžã*–ÓAF@¨½J {Î-÷$‡Èi!ÜŠ +¸”Ñ*‚K v¬»¯à‰ˆQrpÉM¥$CnÌ!£¥ñÆId–Ñg´ês{l:Ž­ç?j;E.ký”ĶAJù÷¶AÚÉ¿“Wtœè»æ(1„~-ä±ñÍâ®=kTbG6ú9Îá4öNÓ_À¤ïH›¸jí#ìj_)†œnÝxú¢wÓww¾{pÌYÚ¿<ÉYŠ+¿«Öç½½‡Ñ¿‚9·ÀÜIÈ3C9ÌŠlZL_ÈÂÎ}Û¾»´`ö/O ÅtÞ×É6á,ó×a…ð-7,€K>¬O –q<üÃägû‡V®Íù’ƒPÓþd@oq})H‹â‘Fë·–=”ý\ +]%=œŠ±é4½ÕN<ýfò縕b9ôË +¿„^ä1¬%Ù _ ÒU¼ˆ¿púó°y.³ºl*¦/qO8N¸=¼‡7k+Ärí|µžOO¹ð!ÿ"¿b’=àé-‹ÈcD}]Ü`yîŽy-rCü«ö5&JÂ̲Ù8,†˜C†ô¤¯ ЦÙõ„Vô·s_ŸËðç!y) ÃÔa±ÃÆ©ãbg¨3bk á¸V½6¶6áõŽØÔb¯þ>ö€z Ö‘32Ú3!º ¦4º8æÆèe1+£ïŠÙ½1fWtSŒæ÷$ðå/£öˆíßíu!·¹öª+¯ðÞF+£÷\·ó8¶â¸·nº³þ¥‰õ7àlÆßæÉwwuâÍí×m-ŸñÚæ÷FNŸœŒí‘QŸ3žì€¼`&èŒòÐË<â&ÓV´É±ÆújÄCao¸Èˆ O˜I6‡ÛX ‰[l²wtçR÷Œ.ަ%­.ï<{Y œ@µ› ú… ëë-Z4fOãëXѾ~½qÏm.ÿhÛÆÛݰáQîØìÚSZ;lO͘½Y Ñõ™Ê+äåBƒ<áè0>Ê[›*›ìü¦PS¸a„… é^o;Îö 6+b)­µuæq¬æÒ¥òƧ箿ú‘GVÝç)úýdíMm;¬S¦<ÆÓÞKK}üO마‰Áƒ°¶A1ºM§ïŽA›Î¯ÐMè ‹}“ð†´Æò*~ˆ„ðÈÌyÂG‡P~Q¡®æÌù3-¶3übë?&UŠMÿ4'éÊÀ­ô„1»_×¾ÆÊë »7SþÕ-\HösS¾kÙ\:çb[W(éÐoAáéWšh¤'9þ‚åÃÒQaƒ ¿¶!èUÓšÈ'9Ì(‹3[‡D2 ›»¼¤£Ü;G+•Qã(šŽXý%W'ãT$è((mϘ×ßqÕgK–B®ù†ö8‹ã°„‡iw-(®øë_~íµ#³´–Ô~8‡â ‰mŽ _0oÛ†öw¹Ñûæ=ú@ûq¾xˬâZ>f3@ßÅ6³ûÒ6sæŸÛŒãŸØÌýwlðÐMÆïÇZ÷ëë 7¡M¦WiÝo„5Œp ¹¨î÷ä àá®QtÞõûŠ®%?î¹K–,h\¼¸Ô$–ë§´÷µ§ñ(²è±M›£;FÚËZ l/ãËpl—é¸ìÐ& 3ï†x¢:ãÝ«–5ø}²; +b‡E½.‚íÌ™‹Cž»ƒþÜ ¸ ª\åK—ô` ý¸ÂÞæö=¢²¥Kr@Ñ|E<ê¯'1mÔqóç.a 2×îˆ÷­k¢Xæâ¦K4àÖ%}éʵà.Q™úêXn„Þ:ÀeaGHÖ}ó² }ÛûtåQà–¿ûºCŸH ðÎù‹Ónå'"‡Œ÷Z^•wÑŒ$[5Ž`jû8ŽyCúåX³ÿeúŪJZRf¤Ü¶Šò+{÷’ Þ½HŠÓñÄÃím|ñÞ/è¼s!oš ó& ÷<³‰³FÇD ¢A’^íÖëo,§ +9ì8¶ÁÎop¿ÚYƒ›Qh 1ÄëÅ>jn9Ó½ +÷Ͳ‚.õ 3ýrÙH«3eYVd£Ñd4ËV!>Ün·„Y“¤d9YI6&›’͉j†4D¢ 16 6•Ç(cŒcL£YMvŸ´OÞ§ì3î3í3»-¢Å`‘,²E1™‡'ÎJ”iý¡K‘Žw^üy^¤£_ð¡õoÏ*/[2Ô¾ÕZ}Ÿ-™wº¡rnnõðÏo+}r¾/RSû§÷I6ÊñÛ½'>Û œ‘šb–¢7?¼kG4åkÈs‹ð ä,Õžp‹ YÉ&;Þ-mBŠd”9ŒÀd) +éòêŠÆ6±ÜÅÂr—æÎÜ¥¹…þN{—å ކÓ_"V›Õ^ÀG1WLtBi0 q¬hJcçúã«´—Oß«9úÄ®]ƒÚóæÎt=qŸÀ_Îtp#ø‘/fï¬# ¡Á¦!o„®±áÝA"‡‚­fû(ðm¶pÝ>t·|&­Ó³E,£yt0è_­~‡êìíÈ_{nÄœÅ+þPýükx·£öJí³ä›DÄ÷ܱ–Kü~ãfæÝ0‚4Zp>"zëâz éV<Õ­¸áçÕÓß"³±J­2LâʹeÜ]ÜF®É¿‚í Ø>`Û°¹Q(` Á8’„ó=Q܇$òÑœA2øT)å`ˆÞ|±tµx3¾…Ü,Ü"®F«ñZ²–_%¬·‘'ñÓ¤Gg}1Âp(vâ9Ú(m_ÜÖJÄï7ê>a4ÄÐ( ß„wQ}q€^_@ë‹h}qÀÖO]²¾H V¿EiñoEUÒ¹t!UÅŠˆ³”*e©Â(À¦…7•7`¡ð¹êäB`Ý$ØD€ ’C§±'îÁõÅÞR¹‡ÑmJ±¤£t<€" I—™†[Fqz9q”4Ú4•Lâ'IWÊEÆ©¦Y–r®˜Ÿ-‹Å†b©L)66rµ|½P+Öj¥¥ÖBon•o2ÞnZa¹Wºßt—e+÷(ÙÊ?*<,=*o5n7HÄ'¥§M/áfò*ÿŠxŒ;NÞáßÎJ—ÿfüÄt%W¦ÿp¬ÇŽÆÓ÷À}`Ÿ®×د-ѵ¾çÚ¾ßH¸6ͯ¿ +Èψzr~¬¾ØµÙYk y|J1° ØØ½ØD!kæ£?¨8þ ¾ nD¶‘Y%)r2TIòäB2]žMªäzr|YF–Ë+äûÐ]ä.rŸ¸^Þ o$ËM„nÏÊt;D‘×å×å7Èä”|Jþ€|@>—?—¿A_oÄ r(&o’ˆÌEò‘R°c¡ó}¤ +š" PRÍYÜX~¬”¥øÌ7¡eÜ +þVq…´L¹­âÖñkÅuÒj屉{–V¢zõ:ÿºtHy½Áâÿ"ž’ÞP>BpŸó‹ŸK(ß¡¯ÅÁzÑ3‚e¼ 5üG­ +/:û1^çmÚmßi7røxmÎk?Õþž­=HíÎk» ›ôŒ´F!+X™ÉL,ôëõ‹Ýz¹YÝ…³ HÙ ½KÐn›Õb”%ê“ÀÓÛBë"/ý#…ÐŒÀöË??&1˜Õ–N—EË¢^®» œÖ½Ìmíéâ¶ŽúÝVe" aArb‡à†B±‡ÚØed 8Ðaɰf£l<†Œá³…lqçoânno1ßb¹[ Kµæµ–mÜv²Ôò¨õx?ÙË7ÉMʳƧÍO[^æ^3¿fyÅú6w–Ëèpm¬¯–†cø¸¾fʵÄQî½öÊ ¡`í÷í÷ŸYõéà±1ú,Ÿ|—Ñ­žpVšeEZ7DÍÝd­Ïò¸[}öpÚÖg³¦r“ •\¹a!7ßpƒa¥A5—l¸G`#ØŒ=I¢”b¤…Û1Ò,ãÒƒÒä€j, +K’ÉÅH ”qÉ›í»¸ü6'—ßþ_ÜÚ¾îjåÊت¶‹ßë¤h¦§ŸËê0Šn9Üæˆ2 +j,Ar³ñ4jÆ8šƒrG¹L²"ôps!HuÙ«à¶zâ|Í™hæ¤ÿ +ý*‰¾½Ìп eEÁQtü>´ñ0Øû×(úA%¿ëø»/WÝ9lØU/¿{<{ùÄi5¾©—WŸnzîÐêÆS «<÷øéÉwn}àNWÄë·®˜Lyy- +$FÑßuy’UI9Þ¦ÿ¦êaý÷£>úPŒúI¨½Åž´w„Ód•"Œ—•T‚"š]@oüÖfûCîHW¸ä°âø …Ç8 U6òŒà´¶¡‡õ4‘ØbýóPFuªà'sP2î ÛIÉ줻 ”sÇÄi>ß´‰wät¥½zòŠ­ëïŒpÝùÀÖ;'Ÿ~ü¹#«N5®>ô\ýæïNBXhÆ{@+"L(˜$ÂôHp4²Fë¿t´å½Û!Æxì  LBOºÑee:M°Ct…swÏ[a‘ ÖûgNÙ1{Þ.¸²èÕ¤GIáù‚ìÁ"!â°q:šcYíq3÷î¼íñý³¬C¿B1û”ãƒÖ73çoêÚvZÈs ¯„¸Ž¿€ Õø[ì7uߥYøÿAçO1•s¯@ºÇð}Ð ât»p7Ú(ñh„ØŠqõè² 5Á¾…OA‰ðü8QP5ðÃç79úÆçn4ö`ß û­°O‡ýAØ—ú¯—À>—ôGga¿ŽÂìüú;Åèf±RDÚ/œBåâ8/Ðwñn¸Þ…ös­t¿°\ì÷¡Ÿákx÷EÀ_8©ŸÅDxöZ)4¬¥p`Jo¡ab” ¼v¡E8‚¦SZ(Îp¾æ&À>S(GS„}h§ Õh +µkïB;¸ƒt¿°_(ÔÛÒ$´Þ–êãh?ò%Œè| EÀ³Â c(C£…Dm¿Â‚ Ý‚yz¦ôÃô=a+@ èm\Œ?à^åþJld(¹ æŸÈßHÂOâo࿦ ë„OÄ,ññcÃC¹áÃ^ûMš&‹ò…W +”{•—”/yÆ›ŒÛgL#L3MgÌCÍ7™›-½-­¬³­Z¿¶½aŸkÿSPXÐà ¢ ùAk‚v ® Þ"†…l9ï˜æxÐñ²“w–;opž½,ôÚÐÃa‘aS\¼k¦kkxÏ1⯑F¶FMª‹:íÔõ “|Ô]L }6䡿/|ÆÅ™²8Ö Ý[ƒÓüm ù·þ6û;› ˆ[þ6En‚¿- w­¿-"+÷˜¿-!;wÊß6Ò¿”ào›ƒèu³¿mA†¬ð·Á· yÏß¶#~È—ˆ¾Ïƒt §²Ùi#'~Íßæ„?÷· R±æoóHåúùÛ +ãÊümEs·ûÛŠãžñ·h0÷ÛìLÆúÛT1ÄíoÛsH³¿mGÒÑHäCµh!ªC•hªMQQ/TŠᜆRaë­ÙÐCE# Oª‡½yQ ªFIp7Õ@ÿdhe¢*ØTTØ«ž]yáì…1óáX=•Ÿ1ëÀŽY‹`¦ù0×\S½)%0æ_›1 ZsaÜ$Ô=J¡o ƒæe#JE*@©c-ô™ p+¡Ÿ +ã}0{ {¦ 4ÒW»°®rNEƒÚ«4QMƒ%¸:{¡:¢²¡¾¡Î[R¤æÖ”&«™UUj!íU¯zë½uó½eÉʆ¤C‹JæWÏõÕÌQG”TüÈÀ,ïÜ’IjiEIÍo½ZRçU+kÔÚÆÙU•¥j™¯º¤²0ëNâF`=ÜÖO(©‹@L„FøªÊ~lˆÚÙ­Ë`õ™ÄdQô1þ¦Dú£AðÀ[W_é«QÓ’ûê9·ïÅp)ؾ—¤œ× Á¯ž\Ê}5ÀÏbJÒ"ŒR`+óØ0’a¬Îu v/ƒWÇ$àza ªhh¨œ’R@ç7&×ûëJ½å¾º9Þä/<Îé‚A@¡JýCӡϨ’z™¢{FZ}©Zÿ6ÊJ!‚' ¡OY Ïj] Ì0(×êØjJêü‹8y1ÆØØÍŒ¶KÑ®«D ´ºrí‡nA øå›ò³\Íoïà.-ïNš+á‰ÂZ ìÕÂjÆëypÏøg¸PÊ +¼j­Ó¸*Nì™×O×6K_êI~¹ëÒÒgÓuL×÷$†—I¿†¯õ°>ƒ 6øu¬Ò¯% †Îiųaq±>•²~Tuè´·Ž»®Ë^fÿºîÅuÑ’8&9:¶Œë^¥0¦ÄOŸÂ¬ 4´šAi`Oü)‡V•ß’zuàØ9õiÿÐ_]ûéŒ<¡wj™Õ”Á ¥lt›2FAÓµÙð´=ÕçP~b†$¿5—f ŠÎ“L*˜Wjðs¦šÝëJQ€†ºnZ©cÛÈx˜ÔE:´]Íä©ËZéâAêatÒБÔAg +ó *ƒ¬Ûƒ»ÒÏÕîÒÿiªœÓ±­íÐè†W§ÖuR´€ñ£úgͰ†ræÕküz»ÌXÆŽtŽ$v¦œ˜ =J<½O@~å,éž- ¡R6wøÒé`fE~ìJ¢y†NtõEø¡'¨þ ~k¨ïÖ7`+ë꺎SÍ% s…ùæîº¦sC%%?!O‹‚ª_öÕìÜé?~Ž,X$¢‘µÄOQr7NýÔXÊ“…þØ¢ÏNy^Îp,ókRÓÓºŽ;:¦”§e]dÞUë´„EÄJæ3ªØ•ÒAQÔʫ¦ 7æt‹«úLZ´G×ÝÀó§þŸÒÀRñSЩa%LF?ƒîó\ÌKá–ä—wWù#Þ\éNó³%̯t ܩïÐÈ€½\=¼~?çeTfZÀ¨*cãã.ã:è¾x„ÏÑ6®‹–é6“wQ|™ÍìÝ××F¿ôd><­¼Ǽ°$mðÇÚ»6=z•0êíÑUî:Î;Ê%-¥‚yx•ëý8z™&ý˜ž|Ý¥|w‹5Lî]ùu)®*]8×U†¿ÔVëýù»ê§$`mK¢™CUGîQçÑb-Óèypœã—˜©V)^õßé©~œªÙ~iðÇÃòNFÙlžñ(®è<ãáªM†<²=Ë…{*äq…ðd\Ñ?y™Åä’ÉžÐçqÌ'C›B&2X:ŒB8RØSá…­²kz5úç,:6Masd´ €ÙxhSØãànœ³ýý舑pg"\Óö(D³P}>ú‡7‹˜íÐqÓ"¸ß9kw¬rÙŒÌÆÁU!ÀíJÿÈg.ƒGñObùmçûñÔ9WÈ SQÈæHÀ(]Ñ»á\ý&0~f2šuló 9ð\§%›a KBÇh$ûc¢SYúgF‹èLEþžILŽ”ž,6žÎ:–õÒ1ï—2mwBIöóRǃòRÇÌýy°©Œþ"ö‡L©l2~n@wF1o…qc"£/“ña<›aëG¹Hù™×¡q…]¤2’ñ‹ÊbžÅfÊd™pIJкJçRÚ¡tÌ0ŠÑ—Í8•ÇzO>fCÿÜŽ;º>æ2ZGúy­ÃÔõ^׉¼.ÜÉh¤’½fÍöëT&ã]w*¨œ&3ü;©Ð%é?Žì³Néçû¥À§ˆÍ\t ®Lf¶˜Íze2YOè°‘f¿ãü˜OìаN0ѯŸã;0ëÎ߀úýߡà +ÌÝ]‚YLŸòüNèà†ÞCù ¸ºïʆ¸VÊÖ9 ~»{äîš5vf£]óΤ.¾¶k& {áQ¬oõEý:ïê«%=fu®uºæn—ZaVÇz.Èz;³Ýwëk¢®YoËÏõ°¾#+ñ±<Ðב™,`O;cz­¿vâë¶Î£3—°ØŸÔ1W uÂÒóÊ–-ÐÙê/ÁÍPÊV†µ,Þë³,`ífBékô÷¥÷¯¹h5¨ÿüPê%e åR™CWþ×1y×ú×R•ŒÃ4ŸLöíCuY'O(ôº[õERïÔ> +m0º¸ª@y0§ æeŒ× +ÒkxtN…ù«@ë¿_uú­ ÜÿKõ ¥[=èâÌëßWR.YRÿÃõ ågÕƒºgò¥]pê¬uzþ¼ +ê¥*,Ê­®¤þ ®¤ü¿ºR—ºRg…áÿ›u%¥[„ýïÕ•”K¬ÖþêJÊ%ëJýgêJÊOÔ þ3u%ý«u¥Î·N¿e]©Ó޺ו~,úþxuI_Ÿë™ÄÿZuIAÝ«K—®nügªKÊOpWíÂÁÿí*“Âtì‡Ù̾ʤüW™”‹ªLkÝÿd•Iù§U&õ?VeRþ…*“úo«2)Œ“ꆭÎíLxþŸ«)—”ù«v¤ü v¤þ×jGÊÖŽ:k@ÿþÚ‘ò/ÔŽ~ +î¿·vð¬?Q~XñQ~Aŧk•æ·¬ø(¿ªâóÃ5Û/«ø(]*>?Uwø-*4 ?€ïA•…ÍC¯’ÊahÑïÚè—qÓ©½ê½^u¶·Ê· 1Yý_Á%«£ªÖVÔ«•Õµ¾ºo™Z^ç«V3ë¼óýæ`_Ý5ê_ÝuFQ:gŸä­+QuÔ:>ÝSúþäòÃü~ö÷êE3WÖ+%jC]I™·º¤nžê+¿Š¢xëª+ëÙ7t•õj…·Î sÍ©+©Ò“€v †Çêæx“ÔŸZR³P­õÖÕÃßìàX%° D-¤èÙPá ð©´ÔW] Ýi‡† +€\öÖÔ÷âKâX™ZR_ï+­,ù”2_icµ·¦¡¤âS^YBêE!²ê_yÃ`\"äÎ[[ç+k,õ20e•@XåìÆ/ÅAé6 Ä\ZÕXF1YPÙPákldª+ýÑêtVØÆzèOÉIR«½”j…)H}ER—9’èœ)¾:µÞ r€Þ•€ªŸü‹¦¦ÈØZÊèEg›hA(ÖP1”7ÖÕÀ„^6°Ì§Öû’ÔúÆÙs½¥ ô¥¯ÜWÊF *õÕ”UR:ê+J€+™í›ïeèZÄèP‚_ˆ¡^¿K¥RÛ©ú3µ¾¢¤ªJ™íõs Ð+)éF§¯ô¢N­öÕy/I¶Ú°°Ö[^%ëHuZ]²¬†—U–WRE+©jÕƒ-)+c”묣ZRx5V•Ô)t¢2o}圆ÆÝVaÕÐ’RROGð©¿x& +R ÃJª. À?&€G'4@¯¦j¡ZÙEÍJN—þެ/mÔSFR¹Ìà :ç­cƒøêÊêÕ¸;Œ£s(qÔlãË@2y~{™íK¢PA”'ó}•ˆy¯n‹QKjkÁ¼JfWyév€LJ§P*JÔŠ’z€è­éƪuÚ]¦6Ö”ùîDUaÈéþ”Të}UÔª™Ø¨JÔ*ê=ÀVkKJç•ÌÂÀk| +UÕM©ºM PôV•S¤Fg«9ãó‹Ô ãsŠ&gf«¹Ô‚Âñ“r³²³Ô¸Ì p—¤NÎ-=~b‘ += +3󋦪ãsÔÌü©êØÜü¬$5{JAaö„ ÊøB5w\A^n6ÜËÍ™71+7”:Æå/RórÇåТñl¨Tnö +l\váÈÑp™9"7/·hj’’“[”0¹B5S-È,,Ê91/³P-˜XX0~B6ÀȰù¹ù9…0Kö¸l _0µ0wÔè¢$T7“”¢Â̬ìq™…c“T6H.TY—dÀ`¨Ù“èà £3óòÔ¹EŠ +³3ÇѾ”;£òÇËVrÆOÌÏÊ,ÊŸ¯ŽÈR2Gäeë¸)#ó2sÇ%©Y™ã2GQr“Ðn:9ìPè€QÙùÙ…™yIꄂ쑹´|Ì-ÌYÄzïy Ý‘ãó'd_1n@¿ÀIÊäÑÙl + þd˜1òó\ +§h|aQ*“s'd'©™…¹¨Dr +ǺTžãs˜L~Ráåûñ¥2¢÷~¨ЋŽö˜•™'P4à†Ò­/hWöÕ¥ÞÚªÛ~ãÖ]#s£ºïLbZ«;PáQ5`¸ú=Ö„°–Å¢ŽîÝ:6 ÇIºëeî´"‘îzËæ{ÁÖSWâ«S|Ô™,¨¬g–!°Ú§Ç<µ¾¤ +&ƒQÔŠX/ð•%U0¬¾Ín¥‚am]% YPWÙÎD-i„»u•×øÃp?L1 +ÔN +è,ÎAÇ¿Î[_ Qªr¾·ja2ô­£±ŒaRYSî««ö“ÎØWÚ08*4¨sð2_ƒâ«›“¬* +˸~uêôs?â·Éƒ=RI¤tæAê/̃”æA~'_Ê ÕbÆ%Ô΄Eù5¹’È•”ÿ\IÑåðoË•Ý`U®¤ü†¹’Ò™+©¿0WRºå¿ WR~,WR~®¤tÉ•ºšo·t â98‰ß*]Rüé’ú«Ò%¥ºlÝø[§LJOýÕ)“ò›¦LŠ?eRyʤ\œ2©¿$eR.™2©ÿJʤeN7f!p\÷ŒXA=á}Bàè{ôÁ2)±£™Ø‘ŽwÝ—,dc‘Ñ%°#ÏzFÇî`vDž Kȅˉ¦‘¶ï“„6|ŸDZ5òÝ·£„ï–oG‘oZÉ×ùJ#ç5òž!_jäùB#ŸG“sù¬E>ÓH‹BZ<ü§Ÿ(§iä…ü½•||·SøX#µ’¿µ’³pqV#g4ò¡Fþª‘Óù@#§4ò~+9ù^˜p²Œ¼FNlŒN”‘w»…w[Éq7ùË›ná/­ä·C„wœäíc6áírÌF޾eŽªä-#ù3ôøs+yà¿é&oÜkÞˆ'¯ÿ)Dx½'ùÓ‘ áO!äH9 G‘×BÈ«¯<#¼ª‘W^ž!¼ò yeÿ²çÂÝÂË3ÈËþnò’F^,#ÍwÙ„f¼Iž×Ès9ôì`áP+yö÷³ƒÉÁáÂÁ4r`¿]8Nö?cöÛÉ3ûLÂ3V²ÏDž†ÉžÖÈ^<å O‘?hdFvkdW(yÂEšœäq€óx+Ù §­ä÷Ðÿ÷dœv,!id{Oò¨F¶iälÕÈà +Ù¢‘‡6[„‡4²ÙB6{øMÀ¨M­d# ÙM6ÀiC+yˆ0’< ‘õ÷?#¬×Èýëf÷?Cî_Ư[áÖÍ ë<üZ¬íX£‘û’Éj¸:Ús¬‚¡«Tr¯‰¬„[+Ç’{àtFî>Üí$wÙÈ +7¹S#Ë5r‡Fn×Èm¹U#·ÜìnÑÈÍnr“FnÔÈ iäúÕäw¹N#Ë\d©B®ÕÈ,ÖÈ¢VrM+Y¨‘ó· + 42+ilˆ[IC©o%uKÈU©õ% ¾$RÓJª[IU+™§‘¹©ÔHE©I¨H#s4RžF¼eŠàÕH™BÊ<|élE(5‘Ù +))v%«I1¶ Å2K!352C#ÓázºF®œ!\©‘ip5-‚LÕÈ”V2Y#“àÚsa’F&j¤(šL!…W¸„ÂVr<¸ÂE +Æ»„‚V2>ß.Œw‘|;MòƆy2vŒ]BÆäZ„1v’k!£[ɨœa”ƒä„ìV’5Ò"dYÉH ‘éF´’L€™é&žáVÁ£‘á—[„áVr¹… j†9ÉP3RFk$#„\¦‘AÁd`z¸0ÐMÒ„éá$ý?@1 BÈ€e|ÿ4“Ð?„ô÷ði&Ò/u«ÐO#©?u+I1‘ä`Ò7i°Ð·•$9ÜBÒ`Ò§Œô.#‰éå ¡v!!šôT‰;šôˆôéMâí$™…¸Vk%±^ !1 +‰Ž&Q‘.!ÊM"­ÁB¤‹DîŸq7a&á®±Bøâ‚I]cI˜FBíÄ ³9[‰î9Ü$¤ŒÛIFìpm׈­ŒX-6ÁL¬‡x‹X–ñfxbn%¦4bÒŒNb\Æ+f¢xxY#’F E5"(Dðð|+!e„ƒQœÞË,`;Af‚÷â²—ã>ÿÿøAÿmþ?QèÿƒÑmendstream +endobj +102 0 obj +<< +/Ascent 759.7656 /CapHeight 759.7656 /Descent -240.2344 /Flags 262148 /FontBBox [ -1069.336 -415.0391 1975.098 1175.293 ] /FontFile2 101 0 R + /FontName /AAAAAA+DejaVuSans-Bold /ItalicAngle 0 /MissingWidth 600.0977 /StemV 165 /Type /FontDescriptor +>> +endobj +103 0 obj +<< +/BaseFont /AAAAAA+DejaVuSans-Bold /FirstChar 0 /FontDescriptor 102 0 R /LastChar 127 /Name /F5+0 /Subtype /TrueType + /ToUnicode 100 0 R /Type /Font /Widths [ 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 600.0977 + 600.0977 600.0977 348.1445 456.0547 520.9961 837.8906 695.8008 1001.953 872.0703 306.1523 + 457.0312 457.0312 522.9492 837.8906 379.8828 415.0391 379.8828 365.2344 695.8008 695.8008 + 695.8008 695.8008 695.8008 695.8008 695.8008 695.8008 695.8008 695.8008 399.9023 399.9023 + 837.8906 837.8906 837.8906 580.0781 1000 773.9258 762.207 733.8867 830.0781 683.1055 + 683.1055 820.8008 836.9141 372.0703 372.0703 774.9023 637.207 995.1172 836.9141 850.0977 + 732.9102 850.0977 770.0195 720.2148 682.1289 812.0117 773.9258 1103.027 770.9961 724.1211 + 725.0977 457.0312 365.2344 457.0312 837.8906 500 500 674.8047 715.8203 592.7734 + 715.8203 678.2227 435.0586 715.8203 711.9141 342.7734 342.7734 665.0391 342.7734 1041.992 + 711.9141 687.0117 715.8203 715.8203 493.1641 595.2148 478.0273 711.9141 651.8555 923.8281 + 645.0195 651.8555 582.0312 711.9141 365.2344 711.9141 837.8906 600.0977 ] +>> +endobj +104 0 obj +<< +/Outlines 106 0 R /PageLabels 145 0 R /PageMode /UseNone /Pages 134 0 R /Type /Catalog +>> +endobj +105 0 obj +<< +/Author () /CreationDate (D:20260121160842+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260121160842+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (ABI for the Arm\256 Architecture Advisory Note \205 SP must be 8-byte aligned on entry to AAPCS-conforming functions) /Trapped /False +>> +endobj +106 0 obj +<< +/Count 31 /First 107 0 R /Last 122 0 R /Type /Outlines +>> +endobj +107 0 obj +<< +/Count 8 /Dest [ 7 0 R /XYZ 57.02362 765.0236 0 ] /First 108 0 R /Last 115 0 R /Next 116 0 R /Parent 106 0 R + /Title (\376\377\0001\000\240\000\240\000\240\000P\000r\000e\000a\000m\000b\000l\000e) +>> +endobj +108 0 obj +<< +/Dest [ 7 0 R /XYZ 57.02362 716.2236 0 ] /Next 109 0 R /Parent 107 0 R /Title (\376\377\0001\000.\0001\000\240\000\240\000\240\000A\000b\000s\000t\000r\000a\000c\000t) +>> +endobj +109 0 obj +<< +/Dest [ 7 0 R /XYZ 57.02362 546.6236 0 ] /Next 110 0 R /Parent 107 0 R /Prev 108 0 R /Title (\376\377\0001\000.\0002\000\240\000\240\000\240\000K\000e\000y\000w\000o\000r\000d\000s) +>> +endobj +110 0 obj +<< +/Dest [ 7 0 R /XYZ 57.02362 491.8236 0 ] /Next 111 0 R /Parent 107 0 R /Prev 109 0 R /Title (\376\377\0001\000.\0003\000\240\000\240\000\240\000L\000a\000t\000e\000s\000t\000 \000r\000e\000l\000e\000a\000s\000e\000 \000a\000n\000d\000 \000d\000e\000f\000e\000c\000t\000s\000 \000r\000e\000p\000o\000r\000t) +>> +endobj +111 0 obj +<< +/Dest [ 13 0 R /XYZ 57.02362 765.0236 0 ] /Next 112 0 R /Parent 107 0 R /Prev 110 0 R /Title (\376\377\0001\000.\0004\000\240\000\240\000\240\000L\000i\000c\000e\000n\000c\000e) +>> +endobj +112 0 obj +<< +/Dest [ 13 0 R /XYZ 57.02362 574.6236 0 ] /Next 113 0 R /Parent 107 0 R /Prev 111 0 R /Title (\376\377\0001\000.\0005\000\240\000\240\000\240\000A\000b\000o\000u\000t\000 \000t\000h\000e\000 \000l\000i\000c\000e\000n\000s\000e) +>> +endobj +113 0 obj +<< +/Dest [ 13 0 R /XYZ 57.02362 399.8236 0 ] /Next 114 0 R /Parent 107 0 R /Prev 112 0 R /Title (\376\377\0001\000.\0006\000\240\000\240\000\240\000C\000o\000n\000t\000r\000i\000b\000u\000t\000i\000o\000n\000s) +>> +endobj +114 0 obj +<< +/Dest [ 13 0 R /XYZ 57.02362 334.2236 0 ] /Next 115 0 R /Parent 107 0 R /Prev 113 0 R /Title (\376\377\0001\000.\0007\000\240\000\240\000\240\000T\000r\000a\000d\000e\000m\000a\000r\000k\000 \000n\000o\000t\000i\000c\000e) +>> +endobj +115 0 obj +<< +/Dest [ 13 0 R /XYZ 57.02362 236.2236 0 ] /Parent 107 0 R /Prev 114 0 R /Title (\376\377\0001\000.\0008\000\240\000\240\000\240\000C\000o\000p\000y\000r\000i\000g\000h\000t) +>> +endobj +116 0 obj +<< +/Count 5 /Dest [ 68 0 R /XYZ 57.02362 765.0236 0 ] /First 117 0 R /Last 121 0 R /Next 122 0 R /Parent 106 0 R + /Prev 107 0 R /Title (\376\377\0002\000\240\000\240\000\240\000A\000b\000o\000u\000t\000 \000T\000h\000i\000s\000 \000D\000o\000c\000u\000m\000e\000n\000t) +>> +endobj +117 0 obj +<< +/Count 2 /Dest [ 68 0 R /XYZ 57.02362 716.2236 0 ] /First 118 0 R /Last 119 0 R /Next 120 0 R /Parent 116 0 R + /Title (\376\377\0002\000.\0001\000\240\000\240\000\240\000C\000h\000a\000n\000g\000e\000 \000c\000o\000n\000t\000r\000o\000l) +>> +endobj +118 0 obj +<< +/Dest [ 68 0 R /XYZ 57.02362 678.2236 0 ] /Next 119 0 R /Parent 117 0 R /Title (\376\377\0002\000.\0001\000.\0001\000\240\000\240\000\240\000C\000u\000r\000r\000e\000n\000t\000 \000s\000t\000a\000t\000u\000s\000 \000a\000n\000d\000 \000a\000n\000t\000i\000c\000i\000p\000a\000t\000e\000d\000 \000c\000h\000a\000n\000g\000e\000s) +>> +endobj +119 0 obj +<< +/Dest [ 68 0 R /XYZ 57.02362 437.4236 0 ] /Parent 117 0 R /Prev 118 0 R /Title (\376\377\0002\000.\0001\000.\0002\000\240\000\240\000\240\000C\000h\000a\000n\000g\000e\000 \000h\000i\000s\000t\000o\000r\000y) +>> +endobj +120 0 obj +<< +/Dest [ 68 0 R /XYZ 57.02362 135.2236 0 ] /Next 121 0 R /Parent 116 0 R /Prev 117 0 R /Title (\376\377\0002\000.\0002\000\240\000\240\000\240\000R\000e\000f\000e\000r\000e\000n\000c\000e\000s) +>> +endobj +121 0 obj +<< +/Dest [ 77 0 R /XYZ 57.02362 580.2236 0 ] /Parent 116 0 R /Prev 120 0 R /Title (\376\377\0002\000.\0003\000\240\000\240\000\240\000T\000e\000r\000m\000s\000 \000a\000n\000d\000 \000a\000b\000b\000r\000e\000v\000i\000a\000t\000i\000o\000n\000s) +>> +endobj +122 0 obj +<< +/Count 8 /Dest [ 79 0 R /XYZ 57.02362 765.0236 0 ] /First 123 0 R /Last 127 0 R /Parent 106 0 R /Prev 116 0 R + /Title (\376\377\0003\000\240\000\240\000\240\000T\000h\000e\000 \000P\000r\000o\000b\000l\000e\000m\000 \000a\000n\000d\000 \000H\000o\000w\000 \000t\000o\000 \000A\000v\000o\000i\000d\000 \000i\000t) +>> +endobj +123 0 obj +<< +/Dest [ 79 0 R /XYZ 57.02362 716.2236 0 ] /Next 124 0 R /Parent 122 0 R /Title (\376\377\0003\000.\0001\000\240\000\240\000\240\000T\000h\000e\000 \000n\000e\000e\000d\000 \000t\000o\000 \000a\000l\000i\000g\000n\000 \000S\000P\000 \000t\000o\000 \000a\000 \000m\000u\000l\000t\000i\000p\000l\000e\000 \000o\000f\000 \0008\000 \000a\000t\000 \000c\000o\000n\000f\000o\000r\000m\000i\000n\000g\000 \000c\000a\000l\000l\000 \000s\000i\000t\000e\000s) +>> +endobj +124 0 obj +<< +/Count 2 /Dest [ 79 0 R /XYZ 57.02362 435.0236 0 ] /First 125 0 R /Last 126 0 R /Next 127 0 R /Parent 122 0 R + /Prev 123 0 R /Title (\376\377\0003\000.\0002\000\240\000\240\000\240\000P\000o\000s\000s\000i\000b\000l\000e\000 \000c\000o\000n\000s\000e\000q\000u\000e\000n\000c\000e\000s\000 \000o\000f\000 \000S\000P\000 \000m\000i\000s\000a\000l\000i\000g\000n\000m\000e\000n\000t) +>> +endobj +125 0 obj +<< +/Dest [ 79 0 R /XYZ 57.02362 315.0236 0 ] /Next 126 0 R /Parent 124 0 R /Title (\376\377\0003\000.\0002\000.\0001\000\240\000\240\000\240\000A\000l\000i\000g\000n\000m\000e\000n\000t\000 \000f\000a\000u\000l\000t\000 \000o\000r\000 \000U\000N\000P\000R\000E\000D\000I\000C\000T\000A\000B\000L\000E\000 \000b\000e\000h\000a\000v\000i\000o\000r) +>> +endobj +126 0 obj +<< +/Dest [ 79 0 R /XYZ 57.02362 176.4236 0 ] /Parent 124 0 R /Prev 125 0 R /Title (\376\377\0003\000.\0002\000.\0002\000\240\000\240\000\240\000A\000p\000p\000l\000i\000c\000a\000t\000i\000o\000n\000 \000f\000a\000i\000l\000u\000r\000e) +>> +endobj +127 0 obj +<< +/Count 3 /Dest [ 87 0 R /XYZ 57.02362 543.8236 0 ] /First 128 0 R /Last 133 0 R /Parent 122 0 R /Prev 124 0 R + /Title (\376\377\0003\000.\0003\000\240\000\240\000\240\000C\000o\000r\000r\000e\000c\000t\000i\000v\000e\000 \000s\000t\000e\000p\000s) +>> +endobj +128 0 obj +<< +/Dest [ 87 0 R /XYZ 57.02362 505.8236 0 ] /Next 129 0 R /Parent 127 0 R /Title (\376\377\0003\000.\0003\000.\0001\000\240\000\240\000\240\000O\000p\000e\000r\000a\000t\000i\000n\000g\000 \000s\000y\000s\000t\000e\000m\000s\000 \000a\000n\000d\000 \000r\000u\000n\000-\000t\000i\000m\000e\000 \000e\000n\000v\000i\000r\000o\000n\000m\000e\000n\000t\000s) +>> +endobj +129 0 obj +<< +/Count -3 /Dest [ 87 0 R /XYZ 57.02362 279.6236 0 ] /First 130 0 R /Last 132 0 R /Next 133 0 R /Parent 127 0 R + /Prev 128 0 R /Title (\376\377\0003\000.\0003\000.\0002\000\240\000\240\000\240\000S\000o\000f\000t\000w\000a\000r\000e\000 \000d\000e\000v\000e\000l\000o\000p\000m\000e\000n\000t\000 \000t\000o\000o\000l\000s) +>> +endobj +130 0 obj +<< +/Dest [ 87 0 R /XYZ 57.02362 244.6236 0 ] /Next 131 0 R /Parent 129 0 R /Title (\376\377\0003\000.\0003\000.\0002\000.\0001\000\240\000\240\000\240\000O\000p\000t\000i\000o\000n\000 \000t\000o\000 \000a\000l\000i\000g\000n\000 \000S\000P\000 \000o\000n\000 \000e\000n\000t\000r\000y\000 \000t\000o\000 \000d\000e\000s\000i\000g\000n\000a\000t\000e\000d\000 \000f\000u\000n\000c\000t\000i\000o\000n\000s) +>> +endobj +131 0 obj +<< +/Dest [ 87 0 R /XYZ 57.02362 146.6236 0 ] /Next 132 0 R /Parent 129 0 R /Prev 130 0 R /Title (\376\377\0003\000.\0003\000.\0002\000.\0002\000\240\000\240\000\240\000S\000a\000f\000e\000 \000o\000p\000t\000i\000o\000n\000 \000n\000o\000t\000 \000t\000o\000 \000a\000l\000i\000g\000n\000 \000S\000P) +>> +endobj +132 0 obj +<< +/Dest [ 94 0 R /XYZ 57.02362 597.0236 0 ] /Parent 129 0 R /Prev 131 0 R /Title (\376\377\0003\000.\0003\000.\0002\000.\0003\000\240\000\240\000\240\000R\000e\000p\000a\000i\000r\000 \000o\000f\000 \000v\000a\000_\000s\000t\000a\000r\000t\000 \000a\000n\000d\000 \000v\000a\000_\000a\000r\000g) +>> +endobj +133 0 obj +<< +/Dest [ 94 0 R /XYZ 57.02362 230.3117 0 ] /Parent 127 0 R /Prev 129 0 R /Title (\376\377\0003\000.\0003\000.\0003\000\240\000\240\000\240\000S\000p\000e\000c\000i\000a\000l\000 \000c\000o\000n\000s\000i\000d\000e\000r\000a\000t\000i\000o\000n\000s\000 \000f\000o\000r\000 \000C\000o\000r\000t\000e\000x\000-\000M\000 \000b\000a\000s\000e\000d\000 \000a\000p\000p\000l\000i\000c\000a\000t\000i\000o\000n\000s) +>> +endobj +134 0 obj +<< +/Count 10 /Kids [ 4 0 R 7 0 R 13 0 R 63 0 R 68 0 R 77 0 R 79 0 R 87 0 R 94 0 R 95 0 R ] /Type /Pages +>> +endobj +135 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1201 +>> +stream +Gas1^9og5P%))O>n@22tMF;me[V;3T5uD/+hqB,0@schDg.#ptMuVL`k5LY\bfcN$%4_$C9I:>h!/&F;_>;@pu`0j:/cK.Ik`u1V^0gS4ONPpLBX&t`BOGgl]`)a1Kk.8*Mo8Zk`4WU/\FLU0*B!&slGZl2U_sqIb>a.KRNiSk$Mg$MFo]c\f+f>Rnk,>o5Zh=k9ZDJcX5Z/n@X5*/f1nLdW0\&Lu[oF[RT9ta3`/hh0o(te*XBoK$%]=IAGE:FuZXKgV`9k/J=K43OKOIkT6@33lQ?@dtK&R'FF,"[\7>W79$doAf&WZD2l-_YiU*O7S9#@reD*k_q-=t;hUD0kWPgW.;Lc4@98XEne?CpAfFC2aAq?Q]l/@EJEFHY]fR3?QJW:$-&M_rq4SnOL+eSR([>KXO3%]Ou]l80Mr%kD3j%ieW$c[O@^W_0**G^SIf1,W0>7X%mMu31W^A>#AVhJ?#'RRs]Q?0u?MH@pu`soXIFc5BB>[5S&9=-Hb.+#'YDL#S`'h^^iBlW`T\c+cpm!0E/2_^?Bkt(7l:[^F>)b(7-/?'$MYQ`L9&^+r:Z++QAXDCN@4rUK\TkA(?bDtHf*_Vgm!OKcmO`P^*Ki0$a3GR7qNtQHSEU:Omk[Yi$=la[0R#V"(A?dMd!s*=AiYU9-\GIH7AT>LlQXe(mg(DG3S:"kmE=ul6IlS!6`E1_%PMb?XL4g(f_:YaLcggcb^a)]Hgk)"En'DGp.~>endstream +endobj +136 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1232 +>> +stream +Gb"/&bECjC']&Lbga/02MC$$DUSm[M$E9Ic\[%Z`YQ\(IfSh*)+^^il)%6Y[Bp^GL8W"u89@pmJkNG*'_8@@;A%i.lb2AcX.8U6%&njN3aQDttp@.m/mOc>DGg)3c`-t)krkmo<)'>[Q])n-\[K`&MlPq9C(aTcPJ]%ScEej$k1SVD$OHf)bfT.REI4?'J5*d]0t]b!qe7%n@9Ot!L^l,>ENS6\S(]f??H)n?gGpoL&Kntt!tF"?N\5dLSMK%\U7:cKhf=GtnKZ;FG>djeZE2X+?Y'4/d\h4&5F>Y?^+r@hePlhJ%M9*KPd7Q93gV;>)!5m,B-X&>4P4Wn/9XKL@6u]Z:^e%*J=loDHM_+4&1bW*oW'8;;N%[16]L@lD"+LnGI:q4U4UE6Vu]n8>PGg&H[d-%&fK.usiE)2Yn(M`Wojri0i^fmm5!t/)+2F:jN.^2P,VV:"7C>)[EZ"0]d`]kQ/)60$m7U;AK8:D3Gj:<3Jd7GqiDl]'Z9a,ga,mKPHBdIMf3&VnjbtD,'DP97O?EW-.2Lj-3fYfL\idMNPk=-3_HM8"LAE*;0mY1"gh3MoML2tZ5]V2rt/r9H>FC3=QO5.rV8St[VX9@.leZ(qe&YR.S9qu%Bq$+ZKkC7/`IDU@7Q.X+I^s+jNK1s#J!9U-!5Cio*4M/3:n>h3fI`eVDl"%6eRne3k2+MuMflXO\fE@g[JK[JPHIS^:kr>\7s)`BYR]A/@T=<-8PIIuI^'p#p!a0hI%#0enQZ:R_g$:,'#8d#%jDoO=$9oTuem;fdC>E?r`p]Ts=?/#AJX'(K\_jObY^pg/h#.UPJKEn77)G@LVbr,g6#mHZ"1eR.c>-GX!endstream +endobj +137 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2507 +>> +stream +Gau0D>E@OK)1Bi7+St0+[Og]UWB_OPK5;--[S8L5>q@Z_ZqT60aHB1gj7qO+I&(:G^*HJ%71`T?nT)A8SaSmbk2RPmf6kco]/cA[`#0*KZ@VT_AA7uudOA_=`FT,<"b_Ihkl2<@_NEsq+e?r>8VH-\WH/8fY*t%[4ZH2XjLU*5W'_G!3c;5`K2Qo/OI0(=56/_?fDF^m&m[7I.[6(U-Kagg#`p?\CE$+FkZ)plD-&I@`F35o'_%u;V;(X$=J_FJjV!@V=kCU-&GkU\NA:>*Dt^@1$l:;5R/L8IAM$m'IYD';p@uG/aH(p-qrRXiqmsi./F%NY2U\99ZAbRZks]S/'$>!-rhgNC9b`'k[D^nK3R>t0&"2%Re@HSgD[dB<+Krqa,$14Sc`ib`U8g.qr5WE#^NRfJ_uX9[hrC4s)7Stc7>:eKhN"!o>H'Uf'\(.4IgTStRMd^k&3k-F;&R;2Do"<,GbgjI2C@`*YG@CQ!b?=?WiEP[pU`A6J_7RE15.6="hfTI:[r?(uj*EK1j1[dlEEH8+uITLrY;;WH2p6N:F1;C;%I;BU0f+dQ2(@ADJLrDK3RpFKZ&TP^,;4%Ja]AS]J#BCYaVH2%3sq-cU,"(d:rMeu=_V$TBWe!]TA7UkWI&JQtD_<ER:_OWrd3J];UL)GIsh/k\Bhg]dN/=_hR=<_rLWD4hmoV^ZpnjcJ2LFeM\o@G=4obAA-:_hH$H<9>*:nJf2>RN;TKYWN#O'8e(&_ZB7Z(Ki0e'$4=o_dH1oXk&R:jHF1qRWJFQeC5K>cc+KXC2JT5TN.f=NQkt-A-4h_Ds`OT.?q8JHp@T>>r@(p@A:Ygo^X3NdE%Z`pUGrj?M_pLkn4<_cXZ3&L9/Zl>c$M%aK)i&;Af]5uO).KOq'"A?gbLQ0[oCT$^#Fci`?o3LEufW=)&8HW@3ZkiEmleSTbK'hp'@VF9`(e4/%n>W*V*7Cto-@ENr^q!jN5TVOaEH44j"KG[n)%Y4je&1EVfOs&`R&(Iod.*$,,jA;n'sdF8O;u*FA"gMC9Y'\I/=@Nn*Fl/p3<;eXqgsmWpMsLi5mR;I$Z6]LDc1EZ7tPKUqCO0cP`N-n5b[P9ai'1%?Zlt6oRlat7&'`FAIBlU+dh"LdVK'%0`hL6@`_cutHWo*FSeR2S)##f&CK7!h&:7::[tbciU'spANeW],>clA&GpYID0j,8]g(ULlC'pigH;dadA>F[NUY!OQ$GtkWV!V^mFcY(9Y`kKdj+(:W!dkHIuV%hk2P45;Kp1T549LBmVNYO)dp:LEne:$L)_fU+PAJTBD.eAQ)A^5p]MDGlq>ES,?uj%31hWJfj%Uq[okU+^lMjN]Fa4;hdnk6^Gh'2rFN^U8jq;tS.*GE,45G"JJWf#TYTT-1qSaFk8<*;rbT>C#'a=@fpi!B54>q,~>endstream +endobj +138 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1459 +>> +stream +Gaua@9lo&I&;KZQ'mk$eN"@,IO/(_pdn\`MM:l)d'EuTl8O5p=&dp"Q>GG[&"RpCo$.C&sP4%b2"n=(e/NkdI>/8]&o76(V\Ai>u<\Vt!+MF>$ArW&e=?qnKrL6tAa+EZdB?1s`rgYoEC8!fXaOFq)d$FDk`/LEE2c#HAkOcQDY#V"Z\ZUeGsF-iUJUT'c.t?9^MJT0OkCU&9^[8jqrDQ\9Z>Dr,QV8LE5`8E+.f5TZ^l91Jjkc7&bK)Hh9jgtDpeY;^11P'W5p(*0F3tT$+8ps$EokMOHb\S11G5#nC?j.#!_GeUp`BqZpgDA5!;^$6ftNopb5%p>d=G]c,h"DUohoPn>DG&YkY*,.^0P8OCkq9pPMemHVFRQP(3L,.`cl&Vq;d2FMq/cpA:ikc"FVR64AM)h[)MK<=NenC:+dUQ_VkQ7[/k4L>C#%:r:+.k=2:BXVEt@f%"ZM.O)?LnGC%R?EGHDl)Ji.u311f!<;\DD=G_f.eS2Zm=?"FY*.7IO.^tSOTrROW.:j<\'lDZ/K5sJAXA*,CLd*4KsXT21!irc;XO-Ig2t4Ehh/)<('ms"c\t:o'!GKSX/f(g=.(G"4TuN>,5'*C>$M\Xl"nOh-.4$-0PLH:g99t(>fTDH>7om`'1;4'khLoe>0IVl7LM2dAf%uH-2e:U@_/kU:Jc_2NZH1Dm!Lqn*>t]k3KDJ3[]TGV'J:4o.aqS>'dTnQZ2EQa@ipV'Ch5#8=C(u08LI[RSJ:&]@'e?2HFZ?QB#u#@7,U7Z'c<0psOrga02B1VLE:Y\WR.a"V#d&5V1Y^^o[D@X#^Qmh^G8ugl(td\sGH$0NtuE:'&S`Xuj>$T5,Z[/iU)d.rS']lSe$nj%7pg!Hf#5DMm8PA>AG@k;5'jJVAH$-%MJ5V@bW`tHB0C#q[gb_s;Hd\E<5WYsP;N9NV)A1s'2]H6e3C+SEAV/kBdg^f94Z.fjc)JRJ)9m<"`>]D1EsP=&>imPo9],4ID:BKPD$OY&_YKJS,7'e[^:!X7PA_5-SJ1!2l]gEQ/+H\ZKOE/KkclWHm"gI>OZiYCCYtR)`];C6&0cp@tq>i!3M2R`;~>endstream +endobj +139 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2616 +>> +stream +Gb!#^8TWXr'#+6Eo[0'SE<'t.bXo8'P[?FBfqWb9,8BQ;U[X]Oh-t?`$7%Mm^Xa/e*]*-i"!Lr!A9g'Ta\i*<>-D]OP>C"biNL6>^RZ"iHu=jEBo<0-#R#a^lA"^u1U#0JLDq.-sk\oYdu&W,Rtl$7X@3OQCptADegcr67YQ2L`qQ&87`i!/-YJ=:ITLd@!?a_#F`Prca4dH&/=.SsF2Y#\P%sWVBYu3J;)\V/IQ'JZRL?;OKnj?[2c)*f8W#H"@PVH5JT![tCb1mpadY@`;o5rl.Xs3?%H&f)@0)Sts?K)FV5p\B;.@L'bMp7BeDB802a?MTfX#M"cg&2A2i.6^2hUb*t?"c?Y^c+AA#O="Zq?f_0@tplcjHN8bs=5XiqTkIua-84++')5`f93Src]=BY/':KAt+'j@-!"b+Bii9r(,*jMeEH&sL3IYrMW$*Eb";M5)ja^7Joab(Zj*GghVIM>L!e)o7R"L32S/%*b?D?!H=7$At1Vj86'q`L?^[=.44@#;iQ:8Os0)G_TfQ5196/sJhfZt:AV@)lAr2Bto%I]^.`Eh7dq0<%@W&?g+=1)6Z!]pWKc<'`d#9+dk!%%VNXh8i3+_BLXDCHaMO!iDmk\fX(R,)T2U6mh?+s(A`R]b^#rY0_U\!l"DhU1];3e,&i1.31p=LZVu.pW;qmVATX.D^s#[j`e*M2L]*bNYcL2Kk@N.Pmca.IT21U/+>l$c"VbuaPn6rRr4;SqWgd"`JTpih^7G&U$-%^(,u:_9Q,j!UQsO!&\e?&gbA>?YdG]8H&7Fe`$iQ:QZB&d^%V+c`1RM&F*J*Fl?`jknfp*'2H(;N#'AH)ff_,a8BtotnSobE&+EH\+i'A+>UY]t&9'(%lfM3>3ETsF#m]bS/$ApSdV-SIPida$LEPP9GR$Qmig&`O3AH:6*84jWR5\f_80*&>q?DlftWnY4qa'S86'a5-ge3\!XqcTKID>b,9kZS-=13K1Oh*I%DRbPf?7a"N@+q.%)oe6LROS!3LcWO-_

une'iDOq$FY$Gei@&DsEnUHW9H^7_7[++^c^Inn\@r'ag'o^)o>pC[#s-&_@(R(k!=L'=CG&Um&(r9(I?S70Lbndp0Ia96C/@])\J.n!KO[nUuh?N*=.s7^!:?rJ_Ap[W^As^1TPa0?%p%o;Z)LFMG:_cQ,3+HsJ:!*](4L1eVI&*42(fm"k,fm-,+G,iN\'\JT3HESkR7qTeTS)5I$ZlM=?\C=+H-*#F&%)r0:FDuN!"@m7:1[*D1`1*'ln.&PI\Sk[b3IE+RjakfG3OS@g@f+oN.:IJI/3M\#t7A(!TR;lA%3A)1stHM2PP_RBRNX41[(FM*hInQs-V\';2=MaFg!pc],[&sZ+nK0I>Q:6gFR)-<:#?Zbi1SdS4]Y'MWdq)3Rsl338>ldg#,hc'>,$>IRW8/G=%6tI(JeTu>l=go([r(~>endstream +endobj +140 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1861 +>> +stream +Gatm<=``Rl&:XAWkXVq0fpU70^>WR#:>%'8PKH7U8dT36-IKZp^ujOPB64t.aadISZ9XjZpG^Q.?SJ/Ja7RoC!%o)Wq'uQe@BflG&ec%)E3m(LI_dL*b9<)h2@#^L_XSSZVjGq@qX#J-;RT/%0O9D7XoT2Jj)$8Rp$%&0Z#&%S%M6Zkbd#\[YM*i$(@0=<_/[THUs9YkD3R2X=JnL@#&AD%:js#8^A-"gVKu`E"`A-5(TX09nLlFWWD,QkL%U@&]/J;oLq,-X3>rkMd<4FbObe(7MeFP;p^B)7\O^3`K\ts:]jUZc+cbrYrEpY$R);6ra?1/T&5iC8WC.DVli2$IUOqdJpJ6WZd\5OPpBUOtVP^,k[=dhsp$p).qJrJ'KaJ"#i_nHua\$RH1oEnhsf('epfL?HeuDP&P2;tQsr*5c2`G2,aAE07OVdojjAd.j@*:?drsE/^PACrZ=0#[k2qCMt_U7efP'd7pkSA[X2pAjIQG,bFoWZ;BmY`tj0Ds*q(!1]QQg=R]R=*2X+kCWE,d0dG`8NsOqqCer:+U4,ol40:C'Fi!/+fCXL%32I?9-jhdiAQr,+n0om$Ua2CuI>POpg'?24VYa#nopE0UpOkSm4hb?$B8b@j@+fq2#Ahf0:g@Iu5$_G`75JOiSs:ke_uP=A8>AkV@W/eN`E%;B7p.M&?br*F0cMeKK]$;k&P0,p:8U+/P?^A8M+e`[<>(&.Z])XbhMo"&]l*oj=Em%?f^.mU:9_mB4f_'k[W[C%Ft;[$q*mXgXq[GRlJZU3P:e#g691St6ZnU4r/Qi`X@;%?S2SNCFXA5:jqLV(LPe`]P@FYgoiSQUu(RI7U_h[d3*Y\"j5b>mX,bUs2aYGK"RPQKi5pdOU&6NL&HjkqL@`^'__L*R4I4@J.+7]9s+UT<5q3l)ZnT\55_sm7pZ$rU=C7Nq3[1d&Bac4X\KVD=*f%Oktc/nZhK4%e\_L[WZ'UK3M+XU>$7??mqSFIc;Arljk]=cM:t^^KI/eSegLWU#sMURHCQ7E'<7:*moRN8=or#n^8jlRO3Jc*9Y"r,3e4f9it4ciRI=*Cga]b3>ZTFZ^F832"2C0(@q>!G-]TGm_lAN@,uQ<;YVW@i9"0Za8Be^".tGV;HHJK)"Xs3r7.Yg-oVUp=H)pN@CsXuD:q;Eept!9=<@dd$+!='+4!rmfqmE781kQY:$o3s3r1m;=6?M5B+qr9U+2:L1=@t^b)nI"iF%A](Ztj^&X[!*X#4MQWD'*l&\OF$(7o9eE:H-n?&M)~>endstream +endobj +141 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2497 +>> +stream +Gb"/(gMZ%@&q-CUW(5+8b[L+G!-uq9Ap+Du2U5D,"GlZr-rq""\S3+8%>?;+*6#SBR;->L+]gI_77R;.+/pl,\0uJh\pd;Ihe;X@(@!:F*=sboggbq]K&mWCU_9BWr^nM-XJ@>1b#f@GlUaBd\+ZD*9j[.2Y,E==6F*Jq/B4NSl!cuYbk&6X]h6nVH@bjBE0;I*q[<`ElaU@?LG=c08[tnj*)?te2rRQh(-Z4e9TmYt#'NeCdCkg2;jB40d5C!\],Kt#-6l&W24b?'%0,Q+Ei1c*qo)1r\?)fo+H)HY"&E%DonLB=lT=!>LE%,-pF@-SH_ZS#BQ!H9f6X,=PI7343QI1Wl@W9@agG>pOsGmW)Vk&o#uMi5Z@r=8a>>JC`p1REOBW[(oRAUEI?/Thp[gV*5'_P`qS;<'#pAZn3qT-,[n=;C*d+;-2+lj?bt>;m6#YTu,c$pP!`hilR4hP$]Dt\CTQP2p=3NCp7GnXQJ;#rcj6$KtKL-"Rg)N^bAmn]Up=cOHN[PJm+Gg-s^4_r+YCKur'+0Ou,(L`KPI$Q9sEZR3=\`n&Ouq^AmojhKM]7Vl<#>c&VTE!5r&:F(k@7[fXL\^$J>Ce0aiV.L8-YZqs-7SXXK[%(M?1Q6Q.mnZG*1@dMR3]t1c\&d_@KC]0-9ZHq8%.7Rr%]t_XIDAMtVhu&fO-DYa[Admf'arS2R+F2>%pOX=Nc89*GfR>m=bDl34Y7g#2NT.Lb1/i?o]'5L^5Vj0&&]R.\Ho+"`@A\"[A<]0/#F@JS7pJEuNt`j8\_Jh3(2u!O2\%0Z\6"l>>[rOIBV\SK)7D<36CM2&3?&3nq]9)4e!).]YO\?`EhQ@7C_*&765`pmL+BT_3o>or\k)E>;_MNf5<_B?V[#WIR(,fC]4u@;O[]pjr;*VO0-KDAp.\u2tKPdj#5>O\.\t7@CXU'+Wc):sD;]1BP['+qT#E>FS"J7#<)Ll&O)\AUTn(30k?RfL6B/nbmaA1/8\0u4#X6VP"d*O/?>cEH=O0:gE6s67\s[[B?6:+8oa^8EQ(^h=0/9t,QBH]H[bHHP=)*pTHEG%K?+Rq8V'^aXZ-o^/NE&Fe$Lj*M]GNl[`cAOS/!JeEii$ij5%4m;=:(]^YUYDgCn_C5np[hdB_E!JF/(:VnMt,&G)FYTA9BMe_@4Q*Dgd;1Ms=)58iddnJK_q,CODsaK4Ho[6ZYhMa3GJRL98HT&A-d!U%r;@b;(,nbk%K3P4$tV]pN.2<@c)OP`j2SI*u.+'N3*2BKBWK=:0k-WTBAC!!Hj$QNfoWtpmNOJFLFZcBMm`tBhBeR1ja_nU`6KbpA#^68ZSm6eS%(3bOdcp*!-Eml;I2Z]4OQX2PTl\:HL)9RP,T])R(Z-G3om3%SSA7ssm,+lTKbh6fY>FsB:l-95.'d;k[~>endstream +endobj +142 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2784 +>> +stream +Gb!SmbDt=:(>\.(\9)>!7;p;]X%>?;"3GX2Abii`hF"(7'fp6!qMdR)aAP3__?g$33g0uE.=o'&5N;[D/5('f+A[C'A=g$_hfbaAZm+p!3<]e_;%+BBIRg#C!f_C-onCZ6ObN6A'%&TW45'Yst';p,pYcXmCL(&Pp5P4J)0YK/V-a>_U/0e]3P_FiV[9V2$SQUn/m!kL@WY[C(^_\nT1em-rS,JDmrl$5_i@s40rk4b/41Ep`Cup_'/6JLKC5"m^PY=Z[Zd5*3<9B'PB0UbRROLaBrhM#MIY=JfRh<-0Ju?EDKZW,D1M*Bk3NGR\Y`g8>%$YJk'FD#iM#%SkDe"-j_5kO+Kpj2U(jV&;0tHjEE;RlVoX2+BK4"OV(eplnSHHCa^Z3n7@cl3mp9^17M,?\X3iX'8DAh;TX*6C.b$O,:/US>4g0P$"&(7%--AiH;B/>u(Z6t1F1,W*GmSkS7:$<>&PhJX]ok37b=<.$bE=Y8GL$YLh=kh99Y&7G%gp9"YKL\2XMi:fQg&5&P=,\bQCP\_=15#e'3]]n5V`,/&[rFOp>(*nM2W1W>%(jUb='4-@c*nBCKG>LH5tnEWf&&kD!F\fWV(q\BIU3jV<>3rK)^[L)iO24X/9)[DaC1_VQ"BB"\u,d/i+R1>L>Ag2N/aa*U16mT<(l'Ccu?<)\&#A$%/PP$/q2[-;a(@JA+BjG91CSP*EBOD%MRF',,KapVKHrG:b&LoP"7-1g)eiC/rnlGC0C:3%,%@3/.dZjk'ks'o#I(k:Et_Ckg(UUJ;a@O658^t(%0QkZGJN,aIm*>gS"j)g4?1RX?_Be$FgKD*,,V,$\?dD;&pE2(<97]-nA1'DNQMHC10>Tej).mC$%iR:o#"-(HGN$_Xk%.bUiZOD0]<;.$1S:p3AlJrlJ4LQ*74>MSX::[aD=1f`c:_,S$03Z;8UUak.s2)ktCkp8::.1>[,'tN9"`RKTJ9U69VXZD%m_1i#1aJTY?-chXbi`sUnN>k2Fh7R6C`_M"(,HI6?g7H-KloU"nZb`((-tncB'17-ioMlDk_p2BSc&1G"fSZ+-u;C[M1rA\V1i/f`k;HBE/E+c=Bd">=g2K,#K?7*]\d]lh38hUkk3dm)BtA$G1t(g4$sAGfpJsgT!A+&qSGBm6;[rMmiM7XWUG;.@Y-9Nm?agT;*W&A2iXf:tI_$Gm<#@Gh^N[!pG(BCgVO&eeN2g]7JuQMPB7oMjMg`OoQ]K7EYZ$B"sOuqodSC81nmhpFA;>BqE%rs1)8%[EQg6]2NBs7pJ2kPB&>gM?S)mOf4n(oA4`E_S0!l:YZ6OYVN0*.6;9\C6Zl+'3T95YJj#Y7S`jpc:]=h@n?runjJ-8l*H(D!h0uHHhrARflf1%-"E'A4]bIY=W@/b%[NtA:ZXb#Pms=@mb?^O="$LRLHuM#Vht?`IrAd.W$RB'I(JDR\B^Q(O-q#>cZ_>Ua=BeGAEi!g3Vt<0rC:0rIBZ71OW$IZYWqC)J),2um%QP&">D\]g=#T4ud>hNtYG4"FMA0c)th!(>@Ho^uHdmj!;4#YDFt#kZG-H6K-61#E?X:3mn.S)fbt+;3=T_J:I\ajqT&77g49L5E#Z/X32D<'ce9I#16:@%YH+KqqBho#JQag!c-9Ih!Qg^mfrm@M'nClf/8S5/&+dH+[3.W1L"=jh.Is[c=f<".l)*8+$5PO>saueHcS>+nDYa*j:&BKXo+kQD*Z`l2;r8EXPfE)U#DUSGITrT0E@Y[69V$;;?6fp4$R(cX%uR-euu5;iS=ZA=3Im1YAP=f>.;W.s2/2ss,.#'SD-WU.qIPeCHq/UgZf9*%0pK?D_g!h*@(&)CH-EE(UsCr_ZJO1j?c&cD@lZUk6AitI*@LCW>Q^JFj[6aIn\HZ?`/"R6Ftn%";a1RIUc_q.Z*mk-V?3s^48*e_5@]b#;rMo,[IH=\_]@9SFl/^I:hsI#$qG81&~>endstream +endobj +143 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 3089 +>> +stream +Gb!l!gN)%>%XlZ)i)-)R(uE3S]K&`mQ)\AXfLjE+BI_OVRoW<5[aIlBdGHV('IhG(XGaEm][+;-EQ8WUo\*-@r7i`6a&DNCOqC%UC!5*q6#_V?*u"!hF,,b#$hUF_%`C1Ot.!mHsd+:'P3;NYN75tkP8cleu[-Kq""+N-XX?>t<^@"NXhp\k>iCUn:iHBZ=Qim>`j4sO9L@%lW`-:ki93W"P8.jT"p2Yd:EhEQ$^J2GGo\Yc.sM)]o`i4>/FY`2L:g=\q3PpX*Ple5ao[bMFqd&6YV!OteJ"4-NhfCAoL>@!0s0AhMRPtqRr.7E<_;t"G#ep02g$Ja+%nTQimJ%g5;'\WIA&p>gW/Z#,!qMV*]-lrrg2)C'jKgHeUKX&s-Ohq;8RV7`]i(7JmMAJpZAC1Ybb`YpD/WiT#n?N;Wle>_cQt3`,KR"]HElC_@06o/dL*T?:B[APV!]'c+Yt2(`kKc!h)Mkk26lL0jVjM@9O_b?HE,t2`;M[&r;MM!^/_(`2$uLuHVUi8%":\Rf".=AT'[Nf!WTDi2])uq9T:g#"pptlM*gZ%b/Y=uqXs(6Y:oBba-9>8Xk7ed:,g>8Lo2k<_)ilg=KXVVftLiTb4P7X$`MeUK&YL&)+)kZTL$[.O*a'cm;-Jc8f%c\Djb2a:EZFn&SU4KX]#'e,0==C.@aWfD?O!n$+HR0AlTBUZc6YLOlM3HL`)dR-7K9qe6V[LP/9pq-;,;4ko:E8?M?P,2X&!fW^@pAYmJk$],fgiZV[sV$pQJ^(C)KVP="+q%GS<8QFYt_OtW4@4p"YQOn_AcOrNE-W8QSM(D1q&1ogn4NW02e#N9>!e49pT@"1'\G-$'L8_l%p1!C`T$q&-8X;8&[hC&[O+nFrk&'pOYUP`Ti"0"uWbs8/rHt"N-IVhjqh^#"Z/2[7"_c)>2Nb)GmC_8(si_Yf@)02kP!V)of$l&nruZQMnG"\lhNA7`$eNjQ`0N8cRcICB)saa2dG'2C6`f[1]uPq](FFdF7\'0A:8TrcSio/aO^6+FFC5DIqPkbPe?t>j*NM#p\MF]H:T1XV(][-.J."q#];\;Db7gOj$jN:R'64<>>%Qdls@tq!PcI1$sRWR7T96/TmKe4LCO`PUZrN<+5L/:*Pd(_DMMg%/bD0(1-qIr:lBEDJ?tK][2(%YOsN#Z&t@,=kP-YaUPkg&bJt]eMnRJ30"(O2'":'rS8H2;]_]"IBi7\:$)]5U\c9\r-nCd(Y/jmGUEA3%hp]+karq/$+Q@ek^IdLMm;^:!D9lR@3O[\<]TtMiqg6QT:]Z>@&Ndo)YK.VE*CMTnAZ?H.EnX=Omrn)f]=SQ&cO9c4*c>m_js29eC34e?1qBdPNpOCb6cE!K`%C/V=)SC5M9Ce].;oj$M.;Op'fNYh*4fA:MABA&:c7*cIBPJNReUQ"'f9-GhNAWiH0d7ARoK4RB=n.Y^oo?]Y'91AG?Rf:TH)!*^fH,\%,R3!JI5aoeoa)S9DgNFU__3C9:cAOk>RG6`q#Mj44(L:I%[&24qI([4K].q"YWN%G&,\O^+2*Z'XqE`RqYB>]\7i3chkIM/:H0g)ImpKLBXB%g)UQVVg#5+r6h]akkqt<$0tfU[]t2K#?go0b#eUsp@@Ao]!C7s///,Q:O_hj.SqFQt;%Y^'9:f>A:%-fBfe^;9(Wt4M/-~>endstream +endobj +144 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 767 +>> +stream +Gb!l\95iT.&BF8:.H[!]9EDot0oamc+uQKLX&Q>^hU'op!M!]8I0]J.Y-KRc;/VYq=W@A3JD\-tT_Pa)RfVGW%3Ibi?l\GA=@P'E0B#jC\6H2>1+rsqBu-Le;NfuXqVa8jdrr.O7A)@:htlBs/jKGh'Mr;FqX%^)SjJTX_@b(nJdTrf0;[do.GW!GC;\Z49P5(K]is94=3Y#;:!1aD%qlZ8+=&jnokZ&BH,S(aX9IScL$4n5eM9"F,(d2*ZGh=\:4NQX$+4mJjKa[s%C.tA@+t8P?5U]FL.V0&DJ5ic2!)E4HlYk#Y`t77XI8s5o.:@FZ4+6fW]dI1gcYg;*MfX&?r8ZY=OP_;S@l#cYtCf@&HRN]'"/`I`?)/;8u3,nqoD8GugLhke^#/)t.$JU's#X`VX&dBWGO/#aVe"%8NML!A=&<_gO&U(:89.4/endstream +endobj +145 0 obj +<< +/Nums [ 0 146 0 R 1 147 0 R 2 148 0 R 3 149 0 R 4 150 0 R + 5 151 0 R 6 152 0 R 7 153 0 R 8 154 0 R 9 155 0 R ] +>> +endobj +146 0 obj +<< +/S /D /St 1 +>> +endobj +147 0 obj +<< +/S /D /St 2 +>> +endobj +148 0 obj +<< +/S /D /St 3 +>> +endobj +149 0 obj +<< +/S /D /St 4 +>> +endobj +150 0 obj +<< +/S /D /St 5 +>> +endobj +151 0 obj +<< +/S /D /St 6 +>> +endobj +152 0 obj +<< +/S /D /St 7 +>> +endobj +153 0 obj +<< +/S /D /St 8 +>> +endobj +154 0 obj +<< +/S /D /St 9 +>> +endobj +155 0 obj +<< +/S /D /St 10 +>> +endobj +xref +0 156 +0000000000 65535 f +0000000061 00000 n +0000000165 00000 n +0000000272 00000 n +0000000381 00000 n +0000000588 00000 n +0000000777 00000 n +0000000974 00000 n +0000001205 00000 n +0000001403 00000 n +0000001571 00000 n +0000001767 00000 n +0000001936 00000 n +0000002136 00000 n +0000002389 00000 n +0000002502 00000 n +0000002670 00000 n +0000002837 00000 n +0000003005 00000 n +0000003172 00000 n +0000003339 00000 n +0000003506 00000 n +0000003674 00000 n +0000003841 00000 n +0000004010 00000 n +0000004178 00000 n +0000004347 00000 n +0000004515 00000 n +0000004684 00000 n +0000004852 00000 n +0000005021 00000 n +0000005189 00000 n +0000005358 00000 n +0000005526 00000 n +0000005694 00000 n +0000005862 00000 n +0000006031 00000 n +0000006199 00000 n +0000006368 00000 n +0000006536 00000 n +0000006705 00000 n +0000006873 00000 n +0000007042 00000 n +0000007210 00000 n +0000007379 00000 n +0000007547 00000 n +0000007715 00000 n +0000007883 00000 n +0000008052 00000 n +0000008220 00000 n +0000008389 00000 n +0000008557 00000 n +0000008726 00000 n +0000008894 00000 n +0000009063 00000 n +0000009231 00000 n +0000009400 00000 n +0000009568 00000 n +0000009737 00000 n +0000009905 00000 n +0000010074 00000 n +0000010242 00000 n +0000010410 00000 n +0000010578 00000 n +0000011146 00000 n +0000011314 00000 n +0000011483 00000 n +0000011652 00000 n +0000011821 00000 n +0000012069 00000 n +0000012269 00000 n +0000012532 00000 n +0000012787 00000 n +0000013021 00000 n +0000013137 00000 n +0000013337 00000 n +0000013537 00000 n +0000013737 00000 n +0000014006 00000 n +0000014206 00000 n +0000014433 00000 n +0000014539 00000 n +0000014708 00000 n +0000014877 00000 n +0000015046 00000 n +0000015215 00000 n +0000015384 00000 n +0000015552 00000 n +0000015814 00000 n +0000015983 00000 n +0000016152 00000 n +0000016321 00000 n +0000016490 00000 n +0000016659 00000 n +0000016828 00000 n +0000017090 00000 n +0000017298 00000 n +0000018073 00000 n +0000037720 00000 n +0000037979 00000 n +0000039300 00000 n +0000040054 00000 n +0000058323 00000 n +0000058595 00000 n +0000059952 00000 n +0000060062 00000 n +0000060434 00000 n +0000060512 00000 n +0000060734 00000 n +0000060925 00000 n +0000061130 00000 n +0000061460 00000 n +0000061661 00000 n +0000061912 00000 n +0000062143 00000 n +0000062389 00000 n +0000062586 00000 n +0000062878 00000 n +0000063141 00000 n +0000063493 00000 n +0000063725 00000 n +0000063941 00000 n +0000064208 00000 n +0000064546 00000 n +0000065018 00000 n +0000065425 00000 n +0000065792 00000 n +0000066049 00000 n +0000066322 00000 n +0000066699 00000 n +0000067047 00000 n +0000067474 00000 n +0000067795 00000 n +0000068112 00000 n +0000068544 00000 n +0000068668 00000 n +0000069962 00000 n +0000071287 00000 n +0000073887 00000 n +0000075439 00000 n +0000078148 00000 n +0000080102 00000 n +0000082692 00000 n +0000085569 00000 n +0000088751 00000 n +0000089610 00000 n +0000089746 00000 n +0000089781 00000 n +0000089816 00000 n +0000089851 00000 n +0000089886 00000 n +0000089921 00000 n +0000089956 00000 n +0000089991 00000 n +0000090026 00000 n +0000090061 00000 n +trailer +<< +/ID +[<0293ba22517b7d9ef96f74d93d387f67><0293ba22517b7d9ef96f74d93d387f67>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 105 0 R +/Root 104 0 R +/Size 156 +>> +startxref +90097 +%%EOF diff --git a/arm_cortex_m33_trm_100230_0100_03_en.pdf b/arm_cortex_m33_trm_100230_0100_03_en.pdf new file mode 100644 index 0000000..a3278c5 Binary files /dev/null and b/arm_cortex_m33_trm_100230_0100_03_en.pdf differ diff --git a/dp.png b/dp.png new file mode 100644 index 0000000..92079cd Binary files /dev/null and b/dp.png differ diff --git a/drivers/0x01_uart/.vscode/.vscode/c_cpp_properties.json b/drivers/0x01_uart/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x01_uart/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x01_uart/.vscode/.vscode/cmake-kits.json b/drivers/0x01_uart/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x01_uart/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x01_uart/.vscode/.vscode/extensions.json b/drivers/0x01_uart/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x01_uart/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x01_uart/.vscode/.vscode/launch.json b/drivers/0x01_uart/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x01_uart/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x01_uart/.vscode/.vscode/settings.json b/drivers/0x01_uart/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x01_uart/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x01_uart/.vscode/.vscode/tasks.json b/drivers/0x01_uart/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x01_uart/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x01_uart/.vscode/c_cpp_properties.json b/drivers/0x01_uart/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x01_uart/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x01_uart/.vscode/cmake-kits.json b/drivers/0x01_uart/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x01_uart/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x01_uart/.vscode/extensions.json b/drivers/0x01_uart/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x01_uart/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x01_uart/.vscode/launch.json b/drivers/0x01_uart/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x01_uart/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x01_uart/.vscode/settings.json b/drivers/0x01_uart/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x01_uart/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x01_uart/.vscode/tasks.json b/drivers/0x01_uart/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x01_uart/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x01_uart/0x01_uart.c b/drivers/0x01_uart/0x01_uart.c new file mode 100644 index 0000000..61ac5f5 --- /dev/null +++ b/drivers/0x01_uart/0x01_uart.c @@ -0,0 +1,63 @@ +/** + * @file 0x01_uart.c + * @brief UART demonstration: echo received characters back in uppercase + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates hardware UART0 using the uart driver (uart.h / uart.c). + * Characters typed into a terminal via a USB-to-UART adapter are echoed + * back in uppercase, illustrating full-duplex raw UART operation without + * going through the stdio layer. + * + * Wiring: + * GPIO0 (TX) -> USB-to-UART adapter RX + * GPIO1 (RX) -> USB-to-UART adapter TX + * GND -> USB-to-UART adapter GND + */ + +#include "pico/stdlib.h" +#include "uart.h" + +/** @brief GPIO pin used for UART0 TX */ +#define UART_TX_PIN 0 +/** @brief GPIO pin used for UART0 RX */ +#define UART_RX_PIN 1 +/** @brief UART baud rate in bits per second */ +#define UART_BAUD 115200 + +int main(void) { + uart_driver_init(UART_TX_PIN, UART_RX_PIN, UART_BAUD); + uart_driver_puts("UART driver ready (115200 8N1)\r\n"); + uart_driver_puts("Type characters to echo them back in UPPERCASE:\r\n"); + while (true) { + if (uart_driver_is_readable()) { + char c = uart_driver_getchar(); + char upper = uart_driver_to_upper(c); + uart_driver_putchar(upper); + } + } +} diff --git a/drivers/0x01_uart/CMakeLists.txt b/drivers/0x01_uart/CMakeLists.txt new file mode 100644 index 0000000..6816052 --- /dev/null +++ b/drivers/0x01_uart/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x01_uart C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x01_uart 0x01_uart.c uart.c) + +pico_set_program_name(0x01_uart "0x01_uart") +pico_set_program_version(0x01_uart "0.1") + +# Disable stdio routing - driver uses hardware UART0 directly (GPIO0/GPIO1) +pico_enable_stdio_uart(0x01_uart 0) +pico_enable_stdio_usb(0x01_uart 0) + +# Add the standard library to the build +target_link_libraries(0x01_uart + pico_stdlib + hardware_uart) + +# Add the standard include files to the build +target_include_directories(0x01_uart PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x01_uart) diff --git a/drivers/0x01_uart/pico_sdk_import.cmake b/drivers/0x01_uart/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x01_uart/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x01_uart/uart.c b/drivers/0x01_uart/uart.c new file mode 100644 index 0000000..4619fce --- /dev/null +++ b/drivers/0x01_uart/uart.c @@ -0,0 +1,67 @@ +/** + * @file uart.c + * @brief Implementation of the hardware UART0 driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "uart.h" +#include "pico/stdlib.h" +#include "hardware/uart.h" +#include "hardware/gpio.h" + +/** @brief Hardware UART instance used by this driver */ +#define UART_INST uart0 + +void uart_driver_init(uint32_t tx_pin, uint32_t rx_pin, uint32_t baud_rate) { + uart_init(UART_INST, baud_rate); + gpio_set_function(tx_pin, GPIO_FUNC_UART); + gpio_set_function(rx_pin, GPIO_FUNC_UART); +} + +bool uart_driver_is_readable(void) { + return uart_is_readable(UART_INST); +} + +char uart_driver_getchar(void) { + return (char)uart_getc(UART_INST); +} + +void uart_driver_putchar(char c) { + uart_putc_raw(UART_INST, c); +} + +void uart_driver_puts(const char *str) { + while (*str) { + uart_putc_raw(UART_INST, *str++); + } +} + +char uart_driver_to_upper(char c) { + if (c >= 'a' && c <= 'z') { + return (char)(c - 32); + } + return c; +} diff --git a/drivers/0x01_uart/uart.h b/drivers/0x01_uart/uart.h new file mode 100644 index 0000000..2de82c5 --- /dev/null +++ b/drivers/0x01_uart/uart.h @@ -0,0 +1,100 @@ +/** + * @file uart.h + * @brief Header for hardware UART0 driver (raw TX/RX, GPIO 0/1) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef UART_H +#define UART_H + +#include +#include + +/** + * @brief Initialize hardware UART0 on the specified TX and RX GPIO pins + * + * Configures UART0 at the requested baud rate, sets the GPIO alternate + * functions for TX and RX, and enables 8N1 framing. Must be called once + * before using any other uart_driver_* functions. + * + * @param tx_pin GPIO pin number to use as UART0 TX (typically 0) + * @param rx_pin GPIO pin number to use as UART0 RX (typically 1) + * @param baud_rate Desired baud rate in bits per second (e.g. 115200) + */ +void uart_driver_init(uint32_t tx_pin, uint32_t rx_pin, uint32_t baud_rate); + +/** + * @brief Check whether a received character is waiting in the UART FIFO + * + * Returns immediately without blocking. Use this to poll for incoming + * data before calling uart_driver_getchar(). + * + * @return bool true if at least one byte is available to read, false otherwise + */ +bool uart_driver_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking) + * + * Blocks until a byte arrives in the receive FIFO, then returns it. + * Prefer pairing with uart_driver_is_readable() to avoid indefinite blocking. + * + * @return char The received character + */ +char uart_driver_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking) + * + * Waits until the transmit FIFO has space, then places the character into the + * FIFO. Returns once the byte has been accepted by the hardware. + * + * @param c Character to transmit + */ +void uart_driver_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0 + * + * Calls uart_driver_putchar() for every character in the string up to and + * not including the null terminator. + * + * @param str Pointer to the null-terminated ASCII string to send + */ +void uart_driver_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase + * + * Returns the uppercase equivalent if the character is in 'a'-'z'; + * all other characters are passed through unchanged. + * + * @param c Input character + * @return char Uppercase equivalent, or the original character + */ +char uart_driver_to_upper(char c); + +#endif // UART_H diff --git a/drivers/0x01_uart_cbm/.vscode/c_cpp_properties.json b/drivers/0x01_uart_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x01_uart_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x01_uart_cbm/.vscode/extensions.json b/drivers/0x01_uart_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x01_uart_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x01_uart_cbm/.vscode/launch.json b/drivers/0x01_uart_cbm/.vscode/launch.json new file mode 100644 index 0000000..89a3461 --- /dev/null +++ b/drivers/0x01_uart_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/uart.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDir": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDir": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x01_uart_cbm/.vscode/settings.json b/drivers/0x01_uart_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x01_uart_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x01_uart_cbm/.vscode/tasks.json b/drivers/0x01_uart_cbm/.vscode/tasks.json new file mode 100644 index 0000000..a44d0a0 --- /dev/null +++ b/drivers/0x01_uart_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/uart.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/uart.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x01_uart_cbm/Inc/rp2350.h b/drivers/0x01_uart_cbm/Inc/rp2350.h new file mode 100644 index 0000000..afce93e --- /dev/null +++ b/drivers/0x01_uart_cbm/Inc/rp2350.h @@ -0,0 +1,203 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[18]; // Other clock registers Address offset: 0x00-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief GPIO pin definitions + */ +#define LED_PIN 25U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_IN_OFFSET (0x004U / 4U) +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OUT_XOR_OFFSET (0x028U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) + +#endif /* __RP2350_H */ diff --git a/drivers/0x01_uart_cbm/Inc/rp2350_reset.h b/drivers/0x01_uart_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x01_uart_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x01_uart_cbm/Inc/rp2350_reset_handler.h b/drivers/0x01_uart_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x01_uart_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x01_uart_cbm/Inc/rp2350_stack.h b/drivers/0x01_uart_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x01_uart_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x01_uart_cbm/Inc/rp2350_uart.h b/drivers/0x01_uart_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x01_uart_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x01_uart_cbm/Inc/rp2350_xosc.h b/drivers/0x01_uart_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..41303ca --- /dev/null +++ b/drivers/0x01_uart_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,49 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x01_uart_cbm/Makefile b/drivers/0x01_uart_cbm/Makefile new file mode 100644 index 0000000..1ec6ba0 --- /dev/null +++ b/drivers/0x01_uart_cbm/Makefile @@ -0,0 +1,83 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C UART driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = uart + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x01_uart_cbm/Src/image_def.c b/drivers/0x01_uart_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x01_uart_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x01_uart_cbm/Src/main.c b/drivers/0x01_uart_cbm/Src/main.c new file mode 100644 index 0000000..c5b73dc --- /dev/null +++ b/drivers/0x01_uart_cbm/Src/main.c @@ -0,0 +1,53 @@ +/** + * @file main.c + * @brief UART demonstration: echo received characters in uppercase. + * @author Kevin Thomas + * @date 2026 + * + * Demonstrates hardware UART0 using the bare-metal UART driver. + * Characters typed into a terminal via a USB-to-UART adapter + * are echoed back in uppercase. + * + * Wiring: + * GPIO0 (TX) -> USB-to-UART adapter RX + * GPIO1 (RX) -> USB-to-UART adapter TX + * GND -> USB-to-UART adapter GND + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +int main(void) +{ + uart_puts("UART driver ready (115200 8N1)\r\n"); + uart_puts("Type characters to echo them back in UPPERCASE:\r\n"); + while (1) + { + if (uart_is_readable()) + { + char c = uart_getchar(); + char upper = uart_to_upper(c); + uart_putchar(upper); + } + } +} diff --git a/drivers/0x01_uart_cbm/Src/rp2350_reset.c b/drivers/0x01_uart_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x01_uart_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x01_uart_cbm/Src/rp2350_reset_handler.c b/drivers/0x01_uart_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..62b58d2 --- /dev/null +++ b/drivers/0x01_uart_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,80 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, then branches + * to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x01_uart_cbm/Src/rp2350_stack.c b/drivers/0x01_uart_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x01_uart_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x01_uart_cbm/Src/rp2350_uart.c b/drivers/0x01_uart_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x01_uart_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x01_uart_cbm/Src/rp2350_xosc.c b/drivers/0x01_uart_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..e4a58c2 --- /dev/null +++ b/drivers/0x01_uart_cbm/Src/rp2350_xosc.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} diff --git a/drivers/0x01_uart_cbm/Src/vector_table.c b/drivers/0x01_uart_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x01_uart_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x01_uart_cbm/build.log b/drivers/0x01_uart_cbm/build.log new file mode 100644 index 0000000..c38a16d Binary files /dev/null and b/drivers/0x01_uart_cbm/build.log differ diff --git a/drivers/0x01_uart_cbm/linker.ld b/drivers/0x01_uart_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x01_uart_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x01_uart_rust/.cargo/config.toml b/drivers/0x01_uart_rust/.cargo/config.toml new file mode 100644 index 0000000..1791d74 --- /dev/null +++ b/drivers/0x01_uart_rust/.cargo/config.toml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2021–2024 The rp-rs Developers +# Copyright (c) 2021 rp-rs organization +# Copyright (c) 2025 Raspberry Pi Ltd. +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# You might want to make a similar file in your own repository if you are +# writing programs for Raspberry Silicon microcontrollers. +# + +[build] +target = "thumbv8m.main-none-eabihf" +# Set the default target to match the Cortex-M33 in the RP2350 +# target = "thumbv8m.main-none-eabihf" +# target = "thumbv6m-none-eabi" +# target = "riscv32imac-unknown-none-elf" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the hard-float ABI for Arm mode. +# +# The FPU is enabled by default, and float function arguments use FPU +# registers. +[target.thumbv8m.main-none-eabihf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as a linker script. +# This is usually provided by the cortex-m-rt crate, and by default the +# version in that crate will include a file called `memory.x` which describes +# the particular memory layout for your specific chip. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the soft-float ABI for RISC-V mode. +# +# Hazard 3 does not have an FPU and so float function arguments use integer +# registers. +[target.riscv32imac-unknown-none-elf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Trp235x_riscv.x also tells the linker to use +# `rp235x_riscv.x` as a linker script. This adds in RP2350 RISC-V specific +# things that the riscv-rt crate's `link.x` requires and then includes +# `link.x` automatically. This is the reverse of how we do it on Cortex-M. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x01_uart_rust/.gitignore b/drivers/0x01_uart_rust/.gitignore new file mode 100644 index 0000000..03381c1 --- /dev/null +++ b/drivers/0x01_uart_rust/.gitignore @@ -0,0 +1,113 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,macos,windows,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux diff --git a/drivers/0x01_uart_rust/.pico-rs b/drivers/0x01_uart_rust/.pico-rs new file mode 100644 index 0000000..1b6702a --- /dev/null +++ b/drivers/0x01_uart_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 \ No newline at end of file diff --git a/drivers/0x01_uart_rust/.vscode/extensions.json b/drivers/0x01_uart_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x01_uart_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x01_uart_rust/.vscode/launch.json b/drivers/0x01_uart_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x01_uart_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x01_uart_rust/.vscode/settings.json b/drivers/0x01_uart_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x01_uart_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x01_uart_rust/.vscode/tasks.json b/drivers/0x01_uart_rust/.vscode/tasks.json new file mode 100644 index 0000000..4194af4 --- /dev/null +++ b/drivers/0x01_uart_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/drivers/0x01_uart_rust/Cargo.toml b/drivers/0x01_uart_rust/Cargo.toml new file mode 100644 index 0000000..f78ec87 --- /dev/null +++ b/drivers/0x01_uart_rust/Cargo.toml @@ -0,0 +1,39 @@ + +[package] +edition = "2024" +name = "uart" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[build-dependencies] +regex = "1.11.0" + +[lib] +name = "uart_lib" +path = "src/lib.rs" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +embedded-hal = "1.0.0" +embedded-hal-nb = "1.0.0" +nb = "1.1.0" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x01_uart_rust/LICENSE-APACHE b/drivers/0x01_uart_rust/LICENSE-APACHE new file mode 100644 index 0000000..8d99cbc --- /dev/null +++ b/drivers/0x01_uart_rust/LICENSE-APACHE @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2021–2024 The rp-rs Developers + Copyright (c) 2021 rp-rs organization + Copyright (c) 2025 Raspberry Pi Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/drivers/0x01_uart_rust/LICENSE-MIT b/drivers/0x01_uart_rust/LICENSE-MIT new file mode 100644 index 0000000..5369c70 --- /dev/null +++ b/drivers/0x01_uart_rust/LICENSE-MIT @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2021–2024 The rp-rs Developers +Copyright (c) 2021 rp-rs organization +Copyright (c) 2025 Raspberry Pi Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + \ No newline at end of file diff --git a/drivers/0x01_uart_rust/build.rs b/drivers/0x01_uart_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x01_uart_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x01_uart_rust/rp2040.x b/drivers/0x01_uart_rust/rp2040.x new file mode 100644 index 0000000..0cc665b --- /dev/null +++ b/drivers/0x01_uart_rust/rp2040.x @@ -0,0 +1,91 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + /* + * Here we assume you have 2048 KiB of Flash. This is what the Pi Pico + * has, but your board may have more or less Flash and you should adjust + * this value to suit. + */ + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + /* + * RAM consists of 4 banks, SRAM0-SRAM3, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K + /* + * RAM banks 4 and 5 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k + + /* SRAM banks 0-3 can also be accessed directly. However, those ranges + alias with the RAM mapping, above. So don't use them at the same time! + SRAM0 : ORIGIN = 0x21000000, LENGTH = 64k + SRAM1 : ORIGIN = 0x21010000, LENGTH = 64k + SRAM2 : ORIGIN = 0x21020000, LENGTH = 64k + SRAM3 : ORIGIN = 0x21030000, LENGTH = 64k + */ +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader + * + * An executable block of code which sets up the QSPI interface for + * 'Execute-In-Place' (or XIP) mode. Also sends chip-specific commands to + * the external flash chip. + * + * Must go at the start of external flash, where the Boot ROM expects it. + */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 512 bytes of flash, + * where picotool can find it + */ + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x01_uart_rust/rp2350.x b/drivers/0x01_uart_rust/rp2350.x new file mode 100644 index 0000000..bda94d8 --- /dev/null +++ b/drivers/0x01_uart_rust/rp2350.x @@ -0,0 +1,83 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 4K of flash + * where the Boot ROM (and picotool) can find it + */ + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + /* move .text to start /after/ the boot info */ + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + /* ### Boot ROM extra info + * + * Goes after everything in our program, so it can contain a signature. + */ + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + \ No newline at end of file diff --git a/drivers/0x01_uart_rust/rp2350_riscv.x b/drivers/0x01_uart_rust/rp2350_riscv.x new file mode 100644 index 0000000..84388aa --- /dev/null +++ b/drivers/0x01_uart_rust/rp2350_riscv.x @@ -0,0 +1,259 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol is not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `32`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > FLASH + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + __start_block_addr = .; + KEEP(*(.start_block)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + . = ALIGN(4); + } > FLASH + + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > FLASH + + .data : ALIGN(32) + { + _sidata = LOADADDR(.data); + __sidata = LOADADDR(.data); + _sdata = .; + __sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(32); + _edata = .; + __edata = .; + } > RAM AT > FLASH + + .bss (NOLOAD) : ALIGN(32) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(32); + _ebss = .; + } > RAM + + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > RAM + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > RAM + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(start_to_end = __end_block_addr - __start_block_addr); +PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 32 == 0, " +ERROR(riscv-rt): the start of the RAM must be 32-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, " +BUG(riscv-rt): .data is not 32-byte aligned"); + +ASSERT(_sidata % 32 == 0, " +BUG(riscv-rt): the LMA of .data is not 32-byte aligned"); + +ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, " +BUG(riscv-rt): .bss is not 32-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(riscv-rt): The .text section must be placed inside the FLASH region. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/drivers/0x01_uart_rust/src/board.rs b/drivers/0x01_uart_rust/src/board.rs new file mode 100644 index 0000000..b6994ac --- /dev/null +++ b/drivers/0x01_uart_rust/src/board.rs @@ -0,0 +1,171 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; +// UART driver for echo demo +use fugit::RateExtU32; +// Import hal::Clock +use hal::Clock; +// Import uart_lib::uart +use uart_lib::uart; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise all peripherals and run the UART echo demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let pins = ( + p.gpio0.into_pull_type().into_function(), + p.gpio1.into_pull_type().into_function(), + ); + let cfg = hal::uart::UartConfig::new( + UART_BAUD.Hz(), + hal::uart::DataBits::Eight, + None, + hal::uart::StopBits::One, + ); + let u = hal::uart::UartPeripheral::new(pac.UART0, pins, &mut pac.RESETS) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap(); + let mut drv = uart::UartDriver::init(u); + drv.puts( + b"UART driver ready (115200 8N1)\r\nType characters to echo them back in UPPERCASE:\r\n", + ); + echo_loop(&mut drv) +} + +/// Run the uppercase echo loop forever. +/// +/// # Arguments +/// +/// * `drv` - Mutable reference to the UART driver. +fn echo_loop + embedded_hal_nb::serial::Write>( + drv: &mut uart::UartDriver, +) -> ! { + loop { + if true { + let c = drv.getchar(); + drv.putchar(uart::UartDriver::::to_upper(c)); + } + } +} + diff --git a/drivers/0x01_uart_rust/src/lib.rs b/drivers/0x01_uart_rust/src/lib.rs new file mode 100644 index 0000000..33cba98 --- /dev/null +++ b/drivers/0x01_uart_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod uart; diff --git a/drivers/0x01_uart_rust/src/main.rs b/drivers/0x01_uart_rust/src/main.rs new file mode 100644 index 0000000..925db4e --- /dev/null +++ b/drivers/0x01_uart_rust/src/main.rs @@ -0,0 +1,103 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// UART driver module +mod uart; + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the UART uppercase echo demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"UART Uppercase Echo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x01_uart_rust/src/uart.rs b/drivers/0x01_uart_rust/src/uart.rs new file mode 100644 index 0000000..c77cef4 --- /dev/null +++ b/drivers/0x01_uart_rust/src/uart.rs @@ -0,0 +1,251 @@ +//! Implementation module +//! +//! **File:** `uart.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Embedded HAL traits for UART +use embedded_hal_nb::serial::{Read, Write}; +// Non-blocking block macro +use nb::block; + +/// Generic UART driver that owns a serial peripheral. +pub struct UartDriver + Write> { + uart: T, +} + +impl + Write> UartDriver { + /// Initialize UART driver with a configured peripheral. + /// + /// # Arguments + /// + /// * `uart` - The hardware UART or mock peripheral. + /// + /// # Returns + /// + /// A new UartDriver instance. + /// + /// # Arguments + /// + /// * `uart` - The `uart` parameter. + /// + /// # Returns + /// + /// A new instance of the struct. + pub fn init(uart: T) -> Self { + Self { uart } + } + + /// Read one character from UART (blocking). + /// + /// Blocks until a byte arrives, then returns it. + /// + /// # Returns + /// + /// The received byte. + /// + /// # Returns + /// + /// An 8-bit unsigned integer value. + pub fn getchar(&mut self) -> u8 { + block!(self.uart.read()).unwrap() + } + + /// Transmit one character over UART (blocking). + /// + /// Waits until there is space, then transmits. + /// + /// # Arguments + /// + /// * `c` - Byte to transmit. + /// + /// # Arguments + /// + /// * `c` - Command byte. + pub fn putchar(&mut self, c: u8) { + block!(self.uart.write(c)).unwrap(); + } + + /// Transmit a null-terminated string over UART. + /// + /// Sends every byte in the slice sequentially. + /// + /// # Arguments + /// + /// * `s` - Byte slice to transmit. + /// + /// # Arguments + /// + /// * `s` - The `s` parameter. + pub fn puts(&mut self, s: &[u8]) { + for &c in s { + self.putchar(c); + } + } + + /// Convert a lowercase ASCII character to uppercase. + /// + /// Returns the uppercase equivalent if the character is in `b'a'`–`b'z'`. + /// + /// # Arguments + /// + /// * `c` - Input byte. + /// + /// # Returns + /// + /// Uppercase equivalent, or the original byte. + /// + /// # Arguments + /// + /// * `c` - Command byte. + /// + /// # Returns + /// + /// An 8-bit unsigned integer value. + pub fn to_upper(c: u8) -> u8 { + if c >= b'a' && c <= b'z' { c - 32 } else { c } + } +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + // Import dependencies from embedded_hal_nb::serial + use embedded_hal_nb::serial::{ErrorType, Read, Write}; + + struct MockUart { + rx_buf: std::collections::VecDeque, + tx_buf: std::collections::VecDeque, + } + + impl MockUart { + /// Creates a new hardware wrapper instance. + /// + /// # Returns + /// + /// A new instance of the struct. + fn new() -> Self { + Self { + rx_buf: std::collections::VecDeque::new(), + tx_buf: std::collections::VecDeque::new(), + } + } + } + + impl ErrorType for MockUart { + type Error = core::convert::Infallible; + } + + impl Read for MockUart { + /// Executes the read operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn read(&mut self) -> nb::Result { + if let Some(c) = self.rx_buf.pop_front() { + Ok(c) + } else { + Err(nb::Error::WouldBlock) + } + } + } + + impl Write for MockUart { + /// Executes the write operation. + /// + /// # Arguments + /// + /// * `c` - Command byte. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn write(&mut self, c: u8) -> nb::Result<(), Self::Error> { + self.tx_buf.push_back(c); + Ok(()) + } + + /// Executes the flush operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn flush(&mut self) -> nb::Result<(), Self::Error> { + Ok(()) + } + } + + /// Executes the to upper lowercase a operation. + #[test] + fn to_upper_lowercase_a() { + assert_eq!(UartDriver::::to_upper(b'a'), b'A'); + } + + /// Executes the to upper lowercase z operation. + #[test] + fn to_upper_lowercase_z() { + assert_eq!(UartDriver::::to_upper(b'z'), b'Z'); + } + + /// Executes the to upper already uppercase operation. + #[test] + fn to_upper_already_uppercase() { + assert_eq!(UartDriver::::to_upper(b'A'), b'A'); + } + + /// Executes the to upper digit unchanged operation. + #[test] + fn to_upper_digit_unchanged() { + assert_eq!(UartDriver::::to_upper(b'5'), b'5'); + } + + /// Executes the getchar reads from buffer operation. + #[test] + fn getchar_reads_from_buffer() { + let mut mock = MockUart::new(); + mock.rx_buf.push_back(b'x'); + let mut drv = UartDriver::init(mock); + assert_eq!(drv.getchar(), b'x'); + } + + /// Executes the putchar writes to buffer operation. + #[test] + fn putchar_writes_to_buffer() { + let mut drv = UartDriver::init(MockUart::new()); + drv.putchar(b'y'); + assert_eq!(drv.uart.tx_buf.pop_front(), Some(b'y')); + } + + /// Executes the puts writes all chars operation. + #[test] + fn puts_writes_all_chars() { + let mut drv = UartDriver::init(MockUart::new()); + drv.puts(b"Hi"); + assert_eq!(drv.uart.tx_buf.pop_front(), Some(b'H')); + assert_eq!(drv.uart.tx_buf.pop_front(), Some(b'i')); + } +} diff --git a/drivers/0x02_blink/.vscode/c_cpp_properties.json b/drivers/0x02_blink/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x02_blink/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x02_blink/.vscode/cmake-kits.json b/drivers/0x02_blink/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x02_blink/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x02_blink/.vscode/extensions.json b/drivers/0x02_blink/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x02_blink/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x02_blink/.vscode/launch.json b/drivers/0x02_blink/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x02_blink/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x02_blink/.vscode/settings.json b/drivers/0x02_blink/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x02_blink/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x02_blink/.vscode/tasks.json b/drivers/0x02_blink/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x02_blink/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x02_blink/0x02_blink.c b/drivers/0x02_blink/0x02_blink.c new file mode 100644 index 0000000..d05dccc --- /dev/null +++ b/drivers/0x02_blink/0x02_blink.c @@ -0,0 +1,57 @@ +/** + * @file 0x02_blink.c + * @brief Blink demonstration: toggle onboard LED every 500 ms + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates GPIO output control using the blink driver (blink.h / blink.c). + * The onboard LED on GPIO 25 is toggled every BLINK_DELAY_MS milliseconds and + * the current state is reported over UART. + * + * Wiring: + * GPIO25 -> Onboard LED (no external wiring needed) + */ + +#include +#include "pico/stdlib.h" +#include "blink.h" + +/** @brief GPIO pin for the onboard LED */ +#define LED_PIN 25 +/** @brief Delay between blink toggles in milliseconds */ +#define BLINK_DELAY_MS 500 + +int main(void) { + stdio_init_all(); + blink_init(LED_PIN); + printf("Blink driver initialized on GPIO %d\r\n", LED_PIN); + while (true) { + blink_toggle(LED_PIN); + printf("LED: %s\r\n", blink_get_state(LED_PIN) ? "ON" : "OFF"); + sleep_ms(BLINK_DELAY_MS); + } +} diff --git a/drivers/0x02_blink/CMakeLists.txt b/drivers/0x02_blink/CMakeLists.txt new file mode 100644 index 0000000..adcca84 --- /dev/null +++ b/drivers/0x02_blink/CMakeLists.txt @@ -0,0 +1,56 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x02_blink C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x02_blink 0x02_blink.c blink.c) + +pico_set_program_name(0x02_blink "0x02_blink") +pico_set_program_version(0x02_blink "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x02_blink 1) +pico_enable_stdio_usb(0x02_blink 0) + +# Add the standard library to the build +target_link_libraries(0x02_blink + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x02_blink PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x02_blink) diff --git a/drivers/0x02_blink/blink.c b/drivers/0x02_blink/blink.c new file mode 100644 index 0000000..d3f0af4 --- /dev/null +++ b/drivers/0x02_blink/blink.c @@ -0,0 +1,54 @@ +/** + * @file blink.c + * @brief Implementation of the GPIO output / LED blink driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "blink.h" +#include "pico/stdlib.h" +#include "hardware/gpio.h" + +void blink_init(uint32_t pin) { + gpio_init(pin); + gpio_set_dir(pin, GPIO_OUT); + gpio_put(pin, false); +} + +void blink_on(uint32_t pin) { + gpio_put(pin, true); +} + +void blink_off(uint32_t pin) { + gpio_put(pin, false); +} + +void blink_toggle(uint32_t pin) { + gpio_put(pin, !gpio_get(pin)); +} + +bool blink_get_state(uint32_t pin) { + return (bool)gpio_get(pin); +} diff --git a/drivers/0x02_blink/blink.h b/drivers/0x02_blink/blink.h new file mode 100644 index 0000000..a373a5d --- /dev/null +++ b/drivers/0x02_blink/blink.h @@ -0,0 +1,78 @@ +/** + * @file blink.h + * @brief Header for GPIO output / LED blink driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BLINK_H +#define BLINK_H + +#include +#include + +/** + * @brief Initialize a GPIO pin as a push-pull digital output + * + * Calls gpio_init() and gpio_set_dir() to configure the pin for output. + * The initial drive level is low (LED off). + * + * @param pin GPIO pin number to configure as a digital output + */ +void blink_init(uint32_t pin); + +/** + * @brief Drive the output pin high (LED on) + * + * @param pin GPIO pin number previously initialized with blink_init() + */ +void blink_on(uint32_t pin); + +/** + * @brief Drive the output pin low (LED off) + * + * @param pin GPIO pin number previously initialized with blink_init() + */ +void blink_off(uint32_t pin); + +/** + * @brief Toggle the current state of the output pin + * + * Reads the current GPIO output level and inverts it. If the LED was on + * it is turned off, and vice versa. + * + * @param pin GPIO pin number previously initialized with blink_init() + */ +void blink_toggle(uint32_t pin); + +/** + * @brief Query the current drive state of the output pin + * + * @param pin GPIO pin number previously initialized with blink_init() + * @return bool true if the pin is driven high, false if driven low + */ +bool blink_get_state(uint32_t pin); + +#endif // BLINK_H diff --git a/drivers/0x02_blink/pico_sdk_import.cmake b/drivers/0x02_blink/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x02_blink/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x02_blink_cbm/.vscode/c_cpp_properties.json b/drivers/0x02_blink_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x02_blink_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x02_blink_cbm/.vscode/extensions.json b/drivers/0x02_blink_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x02_blink_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x02_blink_cbm/.vscode/launch.json b/drivers/0x02_blink_cbm/.vscode/launch.json new file mode 100644 index 0000000..9834227 --- /dev/null +++ b/drivers/0x02_blink_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/blink.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDir": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDir": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x02_blink_cbm/.vscode/settings.json b/drivers/0x02_blink_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x02_blink_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x02_blink_cbm/.vscode/tasks.json b/drivers/0x02_blink_cbm/.vscode/tasks.json new file mode 100644 index 0000000..c638773 --- /dev/null +++ b/drivers/0x02_blink_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/blink.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/blink.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x02_blink_cbm/Inc/rp2350.h b/drivers/0x02_blink_cbm/Inc/rp2350.h new file mode 100644 index 0000000..afce93e --- /dev/null +++ b/drivers/0x02_blink_cbm/Inc/rp2350.h @@ -0,0 +1,203 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[18]; // Other clock registers Address offset: 0x00-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief GPIO pin definitions + */ +#define LED_PIN 25U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_IN_OFFSET (0x004U / 4U) +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OUT_XOR_OFFSET (0x028U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) + +#endif /* __RP2350_H */ diff --git a/drivers/0x02_blink_cbm/Inc/rp2350_delay.h b/drivers/0x02_blink_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..6860cf7 --- /dev/null +++ b/drivers/0x02_blink_cbm/Inc/rp2350_delay.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond busy-wait delay calibrated for a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x02_blink_cbm/Inc/rp2350_gpio.h b/drivers/0x02_blink_cbm/Inc/rp2350_gpio.h new file mode 100644 index 0000000..ca3595f --- /dev/null +++ b/drivers/0x02_blink_cbm/Inc/rp2350_gpio.h @@ -0,0 +1,72 @@ +/** + * @file rp2350_gpio.h + * @brief GPIO driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * SIO-based GPIO configuration, set, clear, toggle, and read + * functions for the RP2350 GPIO pins 0-29. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_GPIO_H +#define __RP2350_GPIO_H + +#include "rp2350.h" + +/** + * @brief Configure a GPIO pin as SIO output. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_config(uint32_t gpio_num); + +/** + * @brief Drive a GPIO output high. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_set(uint32_t gpio_num); + +/** + * @brief Drive a GPIO output low. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_clear(uint32_t gpio_num); + +/** + * @brief Toggle a GPIO output. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_toggle(uint32_t gpio_num); + +/** + * @brief Read the current input level of a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval bool true if pin is high, false if low + */ +bool gpio_get(uint32_t gpio_num); + +#endif /* __RP2350_GPIO_H */ diff --git a/drivers/0x02_blink_cbm/Inc/rp2350_led.h b/drivers/0x02_blink_cbm/Inc/rp2350_led.h new file mode 100644 index 0000000..12b56c3 --- /dev/null +++ b/drivers/0x02_blink_cbm/Inc/rp2350_led.h @@ -0,0 +1,72 @@ +/** + * @file rp2350_led.h + * @brief LED driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * High-level GPIO output / LED driver wrapping the + * low-level GPIO functions. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_LED_H +#define __RP2350_LED_H + +#include "rp2350.h" + +/** + * @brief Initialize a GPIO pin as a push-pull digital output. + * @param pin GPIO pin number to configure + * @retval None + */ +void led_init(uint32_t pin); + +/** + * @brief Drive the output pin high (LED on). + * @param pin GPIO pin number + * @retval None + */ +void led_on(uint32_t pin); + +/** + * @brief Drive the output pin low (LED off). + * @param pin GPIO pin number + * @retval None + */ +void led_off(uint32_t pin); + +/** + * @brief Toggle the current state of the output pin. + * @param pin GPIO pin number + * @retval None + */ +void led_toggle(uint32_t pin); + +/** + * @brief Query the current drive state of the output pin. + * @param pin GPIO pin number + * @retval bool true if the pin is driven high, false if low + */ +bool led_get_state(uint32_t pin); + +#endif /* __RP2350_LED_H */ diff --git a/drivers/0x02_blink_cbm/Inc/rp2350_reset.h b/drivers/0x02_blink_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x02_blink_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x02_blink_cbm/Inc/rp2350_reset_handler.h b/drivers/0x02_blink_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x02_blink_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x02_blink_cbm/Inc/rp2350_stack.h b/drivers/0x02_blink_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x02_blink_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x02_blink_cbm/Inc/rp2350_uart.h b/drivers/0x02_blink_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x02_blink_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x02_blink_cbm/Inc/rp2350_xosc.h b/drivers/0x02_blink_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..41303ca --- /dev/null +++ b/drivers/0x02_blink_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,49 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x02_blink_cbm/Makefile b/drivers/0x02_blink_cbm/Makefile new file mode 100644 index 0000000..7d06b2b --- /dev/null +++ b/drivers/0x02_blink_cbm/Makefile @@ -0,0 +1,86 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C blink driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = blink + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_gpio.c \ + $(SRC_DIR)/rp2350_led.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x02_blink_cbm/Src/image_def.c b/drivers/0x02_blink_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x02_blink_cbm/Src/main.c b/drivers/0x02_blink_cbm/Src/main.c new file mode 100644 index 0000000..bc4585f --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/main.c @@ -0,0 +1,63 @@ +/** + * @file main.c + * @brief Blink demonstration: toggle onboard LED every 500 ms. + * @author Kevin Thomas + * @date 2026 + * + * Demonstrates GPIO output control using the blink driver. + * The onboard LED on GPIO 25 is toggled every 500 ms and the + * current state is reported over UART. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_led.h" +#include "rp2350_uart.h" +#include "rp2350_delay.h" + +/** @brief Delay between LED state toggles in milliseconds */ +#define BLINK_DELAY_MS 500 + +/** + * @brief Print the current LED state over UART. + * @param pin GPIO pin number to query + * @retval None + */ +static void print_led_state(uint32_t pin) +{ + if (led_get_state(pin)) + uart_puts("LED: ON\r\n"); + else + uart_puts("LED: OFF\r\n"); +} + +int main(void) +{ + led_init(LED_PIN); + uart_puts("LED driver initialized on GPIO 25\r\n"); + while (1) + { + led_toggle(LED_PIN); + print_led_state(LED_PIN); + delay_ms(BLINK_DELAY_MS); + } +} diff --git a/drivers/0x02_blink_cbm/Src/rp2350_delay.c b/drivers/0x02_blink_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..70d7f16 --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/rp2350_delay.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond delay calibrated for a 12 MHz clock + * (3600 loop iterations per millisecond). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x02_blink_cbm/Src/rp2350_gpio.c b/drivers/0x02_blink_cbm/Src/rp2350_gpio.c new file mode 100644 index 0000000..f2fdb20 --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/rp2350_gpio.c @@ -0,0 +1,99 @@ +/** + * @file rp2350_gpio.c + * @brief GPIO driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * SIO-based GPIO configuration using IO_BANK0 and PADS_BANK0 + * register structs defined in rp2350.h. All register offsets + * verified against the RP2350 datasheet (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_gpio.h" + +/** + * @brief Configure pad control for a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_config_pad(uint32_t gpio_num) +{ + uint32_t value; + value = PADS_BANK0->GPIO[gpio_num]; + value &= ~(1U << PADS_BANK0_OD_SHIFT); + value |= (1U << PADS_BANK0_IE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + PADS_BANK0->GPIO[gpio_num] = value; +} + +/** + * @brief Set IO_BANK0 FUNCSEL to SIO for a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_config_funcsel(uint32_t gpio_num) +{ + uint32_t value; + value = IO_BANK0->GPIO[gpio_num].CTRL; + value &= ~IO_BANK0_CTRL_FUNCSEL_MASK; + value |= IO_BANK0_CTRL_FUNCSEL_SIO; + IO_BANK0->GPIO[gpio_num].CTRL = value; +} + +/** + * @brief Enable the output driver for a GPIO pin via SIO. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_enable_output(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OE_SET_OFFSET] = (1U << gpio_num); +} + +void gpio_config(uint32_t gpio_num) +{ + gpio_config_pad(gpio_num); + gpio_config_funcsel(gpio_num); + gpio_enable_output(gpio_num); +} + +void gpio_set(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_SET_OFFSET] = (1U << gpio_num); +} + +void gpio_clear(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_CLR_OFFSET] = (1U << gpio_num); +} + +void gpio_toggle(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_XOR_OFFSET] = (1U << gpio_num); +} + +bool gpio_get(uint32_t gpio_num) +{ + return (SIO[SIO_GPIO_IN_OFFSET] & (1U << gpio_num)) != 0; +} diff --git a/drivers/0x02_blink_cbm/Src/rp2350_led.c b/drivers/0x02_blink_cbm/Src/rp2350_led.c new file mode 100644 index 0000000..633a6ca --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/rp2350_led.c @@ -0,0 +1,58 @@ +/** + * @file rp2350_led.c + * @brief LED driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * High-level wrapper around the GPIO driver for LED control. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_led.h" +#include "rp2350_gpio.h" + +void led_init(uint32_t pin) +{ + gpio_config(pin); + gpio_clear(pin); +} + +void led_on(uint32_t pin) +{ + gpio_set(pin); +} + +void led_off(uint32_t pin) +{ + gpio_clear(pin); +} + +void led_toggle(uint32_t pin) +{ + gpio_toggle(pin); +} + +bool led_get_state(uint32_t pin) +{ + return gpio_get(pin); +} diff --git a/drivers/0x02_blink_cbm/Src/rp2350_reset.c b/drivers/0x02_blink_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x02_blink_cbm/Src/rp2350_reset_handler.c b/drivers/0x02_blink_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..62b58d2 --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,80 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, then branches + * to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x02_blink_cbm/Src/rp2350_stack.c b/drivers/0x02_blink_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x02_blink_cbm/Src/rp2350_uart.c b/drivers/0x02_blink_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x02_blink_cbm/Src/rp2350_xosc.c b/drivers/0x02_blink_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..e4a58c2 --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/rp2350_xosc.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} diff --git a/drivers/0x02_blink_cbm/Src/vector_table.c b/drivers/0x02_blink_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x02_blink_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x02_blink_cbm/build.log b/drivers/0x02_blink_cbm/build.log new file mode 100644 index 0000000..5289e0a Binary files /dev/null and b/drivers/0x02_blink_cbm/build.log differ diff --git a/drivers/0x02_blink_cbm/linker.ld b/drivers/0x02_blink_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x02_blink_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x02_blink_rust/.cargo/config.toml b/drivers/0x02_blink_rust/.cargo/config.toml new file mode 100644 index 0000000..1791d74 --- /dev/null +++ b/drivers/0x02_blink_rust/.cargo/config.toml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2021–2024 The rp-rs Developers +# Copyright (c) 2021 rp-rs organization +# Copyright (c) 2025 Raspberry Pi Ltd. +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# You might want to make a similar file in your own repository if you are +# writing programs for Raspberry Silicon microcontrollers. +# + +[build] +target = "thumbv8m.main-none-eabihf" +# Set the default target to match the Cortex-M33 in the RP2350 +# target = "thumbv8m.main-none-eabihf" +# target = "thumbv6m-none-eabi" +# target = "riscv32imac-unknown-none-elf" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the hard-float ABI for Arm mode. +# +# The FPU is enabled by default, and float function arguments use FPU +# registers. +[target.thumbv8m.main-none-eabihf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as a linker script. +# This is usually provided by the cortex-m-rt crate, and by default the +# version in that crate will include a file called `memory.x` which describes +# the particular memory layout for your specific chip. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the soft-float ABI for RISC-V mode. +# +# Hazard 3 does not have an FPU and so float function arguments use integer +# registers. +[target.riscv32imac-unknown-none-elf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Trp235x_riscv.x also tells the linker to use +# `rp235x_riscv.x` as a linker script. This adds in RP2350 RISC-V specific +# things that the riscv-rt crate's `link.x` requires and then includes +# `link.x` automatically. This is the reverse of how we do it on Cortex-M. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x02_blink_rust/.gitignore b/drivers/0x02_blink_rust/.gitignore new file mode 100644 index 0000000..03381c1 --- /dev/null +++ b/drivers/0x02_blink_rust/.gitignore @@ -0,0 +1,113 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,macos,windows,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux diff --git a/drivers/0x02_blink_rust/.pico-rs b/drivers/0x02_blink_rust/.pico-rs new file mode 100644 index 0000000..1b6702a --- /dev/null +++ b/drivers/0x02_blink_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 \ No newline at end of file diff --git a/drivers/0x02_blink_rust/.vscode/extensions.json b/drivers/0x02_blink_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x02_blink_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x02_blink_rust/.vscode/launch.json b/drivers/0x02_blink_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x02_blink_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x02_blink_rust/.vscode/settings.json b/drivers/0x02_blink_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x02_blink_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x02_blink_rust/.vscode/tasks.json b/drivers/0x02_blink_rust/.vscode/tasks.json new file mode 100644 index 0000000..4194af4 --- /dev/null +++ b/drivers/0x02_blink_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/drivers/0x02_blink_rust/Cargo.toml b/drivers/0x02_blink_rust/Cargo.toml new file mode 100644 index 0000000..dde36b5 --- /dev/null +++ b/drivers/0x02_blink_rust/Cargo.toml @@ -0,0 +1,41 @@ + +[package] +edition = "2024" +name = "blink" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "blink_lib" +path = "src/lib.rs" + +[[bin]] +name = "blink" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +embedded-hal = "1.0.0" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x02_blink_rust/LICENSE-APACHE b/drivers/0x02_blink_rust/LICENSE-APACHE new file mode 100644 index 0000000..8d99cbc --- /dev/null +++ b/drivers/0x02_blink_rust/LICENSE-APACHE @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2021–2024 The rp-rs Developers + Copyright (c) 2021 rp-rs organization + Copyright (c) 2025 Raspberry Pi Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/drivers/0x02_blink_rust/LICENSE-MIT b/drivers/0x02_blink_rust/LICENSE-MIT new file mode 100644 index 0000000..5369c70 --- /dev/null +++ b/drivers/0x02_blink_rust/LICENSE-MIT @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2021–2024 The rp-rs Developers +Copyright (c) 2021 rp-rs organization +Copyright (c) 2025 Raspberry Pi Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + \ No newline at end of file diff --git a/drivers/0x02_blink_rust/build.rs b/drivers/0x02_blink_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x02_blink_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x02_blink_rust/rp2040.x b/drivers/0x02_blink_rust/rp2040.x new file mode 100644 index 0000000..0cc665b --- /dev/null +++ b/drivers/0x02_blink_rust/rp2040.x @@ -0,0 +1,91 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + /* + * Here we assume you have 2048 KiB of Flash. This is what the Pi Pico + * has, but your board may have more or less Flash and you should adjust + * this value to suit. + */ + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + /* + * RAM consists of 4 banks, SRAM0-SRAM3, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K + /* + * RAM banks 4 and 5 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k + + /* SRAM banks 0-3 can also be accessed directly. However, those ranges + alias with the RAM mapping, above. So don't use them at the same time! + SRAM0 : ORIGIN = 0x21000000, LENGTH = 64k + SRAM1 : ORIGIN = 0x21010000, LENGTH = 64k + SRAM2 : ORIGIN = 0x21020000, LENGTH = 64k + SRAM3 : ORIGIN = 0x21030000, LENGTH = 64k + */ +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader + * + * An executable block of code which sets up the QSPI interface for + * 'Execute-In-Place' (or XIP) mode. Also sends chip-specific commands to + * the external flash chip. + * + * Must go at the start of external flash, where the Boot ROM expects it. + */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 512 bytes of flash, + * where picotool can find it + */ + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x02_blink_rust/rp2350.x b/drivers/0x02_blink_rust/rp2350.x new file mode 100644 index 0000000..bda94d8 --- /dev/null +++ b/drivers/0x02_blink_rust/rp2350.x @@ -0,0 +1,83 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 4K of flash + * where the Boot ROM (and picotool) can find it + */ + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + /* move .text to start /after/ the boot info */ + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + /* ### Boot ROM extra info + * + * Goes after everything in our program, so it can contain a signature. + */ + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + \ No newline at end of file diff --git a/drivers/0x02_blink_rust/rp2350_riscv.x b/drivers/0x02_blink_rust/rp2350_riscv.x new file mode 100644 index 0000000..84388aa --- /dev/null +++ b/drivers/0x02_blink_rust/rp2350_riscv.x @@ -0,0 +1,259 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol is not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `32`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > FLASH + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + __start_block_addr = .; + KEEP(*(.start_block)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + . = ALIGN(4); + } > FLASH + + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > FLASH + + .data : ALIGN(32) + { + _sidata = LOADADDR(.data); + __sidata = LOADADDR(.data); + _sdata = .; + __sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(32); + _edata = .; + __edata = .; + } > RAM AT > FLASH + + .bss (NOLOAD) : ALIGN(32) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(32); + _ebss = .; + } > RAM + + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > RAM + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > RAM + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(start_to_end = __end_block_addr - __start_block_addr); +PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 32 == 0, " +ERROR(riscv-rt): the start of the RAM must be 32-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, " +BUG(riscv-rt): .data is not 32-byte aligned"); + +ASSERT(_sidata % 32 == 0, " +BUG(riscv-rt): the LMA of .data is not 32-byte aligned"); + +ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, " +BUG(riscv-rt): .bss is not 32-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(riscv-rt): The .text section must be placed inside the FLASH region. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/drivers/0x02_blink_rust/src/blink.rs b/drivers/0x02_blink_rust/src/blink.rs new file mode 100644 index 0000000..3e52563 --- /dev/null +++ b/drivers/0x02_blink_rust/src/blink.rs @@ -0,0 +1,252 @@ +//! Implementation module +//! +//! **File:** `blink.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Embedded HAL digital output traits for GPIO pin control +use embedded_hal::digital::{OutputPin, StatefulOutputPin}; + +/// GPIO output / LED blink driver that owns a single output pin. +pub struct BlinkDriver { + pin: P, +} + +impl BlinkDriver

{ + /// Initialize a GPIO pin as a push-pull digital output. + /// + /// The initial drive level is low (LED off). + /// + /// # Arguments + /// + /// * `pin` - GPIO pin configured as a digital output. + /// + /// # Returns + /// + /// A new BlinkDriver instance owning the configured pin. + /// + /// # Arguments + /// + /// * `pin` - GPIO pin. + /// + /// # Returns + /// + /// A new instance of the struct. + pub fn init(mut pin: P) -> Self { + pin.set_low().unwrap(); + Self { pin } + } + + /// Drive the output pin high (LED on). + /// + /// # Arguments + /// + pub fn on(&mut self) { + self.pin.set_high().unwrap(); + } + + /// Drive the output pin low (LED off). + /// + /// # Arguments + /// + pub fn off(&mut self) { + self.pin.set_low().unwrap(); + } + + /// Toggle the current state of the output pin. + /// + /// Reads the current GPIO output level and inverts it. If the LED was on + /// it is turned off, and vice versa. + /// + /// # Arguments + /// + pub fn toggle(&mut self) { + self.pin.toggle().unwrap(); + } + + /// Query the current drive state of the output pin. + /// + /// # Arguments + /// + /// + /// # Returns + /// + /// `true` if the pin is driven high, `false` if driven low. + /// + /// # Returns + /// + /// `true` if successful or set, `false` otherwise. + pub fn get_state(&mut self) -> bool { + self.pin.is_set_high().unwrap() + } +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + // Infallible error type for mock pin implementations + use core::convert::Infallible; + // Error type trait for mock pin implementations + use embedded_hal::digital::ErrorType; + + struct MockPin { + state: bool, + } + + impl MockPin { + /// Creates a new hardware wrapper instance. + /// + /// # Returns + /// + /// A new instance of the struct. + fn new() -> Self { + Self { state: false } + } + } + + impl ErrorType for MockPin { + type Error = Infallible; + } + + impl OutputPin for MockPin { + /// Executes the set low operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn set_low(&mut self) -> Result<(), Self::Error> { + self.state = false; + Ok(()) + } + + /// Executes the set high operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn set_high(&mut self) -> Result<(), Self::Error> { + self.state = true; + Ok(()) + } + } + + impl StatefulOutputPin for MockPin { + /// Executes the is set high operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn is_set_high(&mut self) -> Result { + Ok(self.state) + } + + /// Executes the is set low operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn is_set_low(&mut self) -> Result { + Ok(!self.state) + } + + /// Executes the toggle operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn toggle(&mut self) -> Result<(), Self::Error> { + self.state = !self.state; + Ok(()) + } + } + + /// Executes the init starts low operation. + #[test] + fn init_starts_low() { + let drv = BlinkDriver::init(MockPin::new()); + assert!(!drv.pin.state); + } + + /// Executes the on sets high operation. + #[test] + fn on_sets_high() { + let mut drv = BlinkDriver::init(MockPin::new()); + drv.on(); + assert!(drv.pin.state); + } + + /// Executes the off sets low operation. + #[test] + fn off_sets_low() { + let mut drv = BlinkDriver::init(MockPin::new()); + drv.on(); + drv.off(); + assert!(!drv.pin.state); + } + + /// Executes the toggle from low goes high operation. + #[test] + fn toggle_from_low_goes_high() { + let mut drv = BlinkDriver::init(MockPin::new()); + drv.toggle(); + assert!(drv.pin.state); + } + + /// Executes the toggle from high goes low operation. + #[test] + fn toggle_from_high_goes_low() { + let mut drv = BlinkDriver::init(MockPin::new()); + drv.on(); + drv.toggle(); + assert!(!drv.pin.state); + } + + /// Executes the get state reflects on operation. + #[test] + fn get_state_reflects_on() { + let mut drv = BlinkDriver::init(MockPin::new()); + drv.on(); + assert!(drv.get_state()); + } + + /// Executes the get state reflects off operation. + #[test] + fn get_state_reflects_off() { + let mut drv = BlinkDriver::init(MockPin::new()); + drv.on(); + drv.off(); + assert!(!drv.get_state()); + } + + /// Executes the double toggle returns to original operation. + #[test] + fn double_toggle_returns_to_original() { + let mut drv = BlinkDriver::init(MockPin::new()); + drv.toggle(); + drv.toggle(); + assert!(!drv.get_state()); + } +} diff --git a/drivers/0x02_blink_rust/src/board.rs b/drivers/0x02_blink_rust/src/board.rs new file mode 100644 index 0000000..76ad980 --- /dev/null +++ b/drivers/0x02_blink_rust/src/board.rs @@ -0,0 +1,249 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionSioOutput, FunctionUart, Pin, PullDown, PullNone}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Delay between LED toggles in milliseconds. +pub(crate) const BLINK_DELAY_MS: u32 = 500; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Type alias for the onboard LED pin configured as push-pull output. +type LedPin = Pin; + +/// Initialise all peripherals and run the blink demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + let mut led = crate::blink::BlinkDriver::init(p.gpio25.into_push_pull_output()); + uart.write_full_blocking(b"Blink driver initialized on GPIO 25\r\n"); + blink_loop(&uart, &mut delay, &mut led) +} + +/// Toggle LED, report state, and delay in a loop forever. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `delay` - Mutable reference to the blocking delay provider. +/// * `led` - Mutable reference to the blink driver. +fn blink_loop( + uart: &EnabledUart, + delay: &mut cortex_m::delay::Delay, + led: &mut crate::blink::BlinkDriver, +) -> ! { + loop { + led.toggle(); + let msg = if led.get_state() { + b"LED: ON\r\n" as &[u8] + } else { + b"LED: OFF\r\n" + }; + uart.write_full_blocking(msg); + delay.delay_ms(BLINK_DELAY_MS); + } +} + diff --git a/drivers/0x02_blink_rust/src/lib.rs b/drivers/0x02_blink_rust/src/lib.rs new file mode 100644 index 0000000..3efff6d --- /dev/null +++ b/drivers/0x02_blink_rust/src/lib.rs @@ -0,0 +1,37 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `lib.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![cfg_attr(not(test), no_std)] + +// Blink driver module +pub mod blink; diff --git a/drivers/0x02_blink_rust/src/main.rs b/drivers/0x02_blink_rust/src/main.rs new file mode 100644 index 0000000..9c1a9a3 --- /dev/null +++ b/drivers/0x02_blink_rust/src/main.rs @@ -0,0 +1,104 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// Blink driver module — suppress warnings for unused public API functions +#[allow(dead_code)] +mod blink; + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the LED blink demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"Blink LED Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x03_button/.vscode/.vscode/c_cpp_properties.json b/drivers/0x03_button/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x03_button/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x03_button/.vscode/.vscode/cmake-kits.json b/drivers/0x03_button/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x03_button/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x03_button/.vscode/.vscode/extensions.json b/drivers/0x03_button/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x03_button/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x03_button/.vscode/.vscode/launch.json b/drivers/0x03_button/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x03_button/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x03_button/.vscode/.vscode/settings.json b/drivers/0x03_button/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x03_button/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x03_button/.vscode/.vscode/tasks.json b/drivers/0x03_button/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x03_button/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x03_button/.vscode/c_cpp_properties.json b/drivers/0x03_button/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x03_button/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x03_button/.vscode/cmake-kits.json b/drivers/0x03_button/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x03_button/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x03_button/.vscode/extensions.json b/drivers/0x03_button/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x03_button/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x03_button/.vscode/launch.json b/drivers/0x03_button/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x03_button/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x03_button/.vscode/settings.json b/drivers/0x03_button/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x03_button/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x03_button/.vscode/tasks.json b/drivers/0x03_button/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x03_button/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x03_button/0x03_button.c b/drivers/0x03_button/0x03_button.c new file mode 100644 index 0000000..372dec2 --- /dev/null +++ b/drivers/0x03_button/0x03_button.c @@ -0,0 +1,79 @@ +/** + * @file 0x03_button.c + * @brief Button demonstration: debounced press mirrors to LED + UART report + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates GPIO input using the button driver (button.h / button.c). + * The onboard LED mirrors the button state and every edge transition is + * reported over UART. + * + * Wiring: + * GPIO15 -> One leg of push button + * GND -> Other leg of push button + * GPIO25 -> Onboard LED (no external wiring needed) + */ + +#include +#include "pico/stdlib.h" +#include "button.h" + +/** @brief GPIO pin for the push-button input */ +#define BUTTON_PIN 15 +/** @brief GPIO pin for the indicator LED */ +#define LED_PIN 25 +/** @brief Button debounce delay in milliseconds */ +#define DEBOUNCE_MS 20 + +/** + * @brief Poll button state and report edge transitions over UART + * + * Reads the debounced button state, mirrors it to the LED, and prints + * a message when the state changes from the previous reading. + * + * @param last_state Pointer to the stored previous button state + */ +static void poll_button(bool *last_state) { + bool pressed = button_is_pressed(BUTTON_PIN); + button_led_set(LED_PIN, pressed); + if (pressed != *last_state) { + printf("Button: %s\r\n", pressed ? "PRESSED" : "RELEASED"); + *last_state = pressed; + } +} + +int main(void) { + stdio_init_all(); + button_init(BUTTON_PIN, DEBOUNCE_MS); + button_led_init(LED_PIN); + printf("Button driver initialized: button=GPIO%d led=GPIO%d\r\n", BUTTON_PIN, LED_PIN); + bool last_state = false; + while (true) { + poll_button(&last_state); + sleep_ms(10); + } +} diff --git a/drivers/0x03_button/CMakeLists.txt b/drivers/0x03_button/CMakeLists.txt new file mode 100644 index 0000000..b6442bd --- /dev/null +++ b/drivers/0x03_button/CMakeLists.txt @@ -0,0 +1,56 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x03_button C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x03_button 0x03_button.c button.c) + +pico_set_program_name(0x03_button "0x03_button") +pico_set_program_version(0x03_button "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x03_button 1) +pico_enable_stdio_usb(0x03_button 0) + +# Add the standard library to the build +target_link_libraries(0x03_button + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x03_button PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x03_button) diff --git a/drivers/0x03_button/button.c b/drivers/0x03_button/button.c new file mode 100644 index 0000000..7ea5a7e --- /dev/null +++ b/drivers/0x03_button/button.c @@ -0,0 +1,74 @@ +/** + * @file button.c + * @brief Implementation of the push-button GPIO input driver with debounce + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "button.h" +#include "pico/stdlib.h" +#include "hardware/gpio.h" + +/** @brief Debounce settling time stored from button_init() */ +static uint32_t debounce_delay_ms = 20; + +/** + * @brief Confirm a raw active-low pin read by waiting for the debounce period + * + * After an initial low reading on the pin, this helper re-samples the pin + * after debounce_delay_ms milliseconds to confirm the reading is stable. + * This eliminates false triggers caused by mechanical contact bounce. + * + * @param pin GPIO pin number to re-sample + * @return bool true if the pin is still low after the debounce delay + */ +static bool debounce_confirm(uint32_t pin) { + sleep_ms(debounce_delay_ms); + return !gpio_get(pin); +} + +void button_init(uint32_t pin, uint32_t debounce_ms) { + debounce_delay_ms = debounce_ms; + gpio_init(pin); + gpio_set_dir(pin, GPIO_IN); + gpio_pull_up(pin); +} + +bool button_is_pressed(uint32_t pin) { + if (!gpio_get(pin)) { + return debounce_confirm(pin); + } + return false; +} + +void button_led_init(uint32_t pin) { + gpio_init(pin); + gpio_set_dir(pin, GPIO_OUT); + gpio_put(pin, false); +} + +void button_led_set(uint32_t pin, bool on) { + gpio_put(pin, on); +} diff --git a/drivers/0x03_button/button.h b/drivers/0x03_button/button.h new file mode 100644 index 0000000..7bc1de5 --- /dev/null +++ b/drivers/0x03_button/button.h @@ -0,0 +1,80 @@ +/** + * @file button.h + * @brief Header for push-button GPIO input driver with debounce + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BUTTON_H +#define BUTTON_H + +#include +#include + +/** + * @brief Initialize a GPIO pin as an active-low button input with pull-up + * + * Configures the pin as an input and enables the internal pull-up resistor. + * The pin reads logic high when the button is open and logic low when the + * button connects the pin to GND. + * + * @param pin GPIO pin number to configure as a button input + * @param debounce_ms Debounce settling time in milliseconds (e.g. 20) + */ +void button_init(uint32_t pin, uint32_t debounce_ms); + +/** + * @brief Read the debounced state of the button + * + * Returns true only when the pin reads low continuously for the debounce + * period configured in button_init(). This prevents mechanical contact + * bounce from registering as multiple rapid presses. + * + * @param pin GPIO pin number previously initialized with button_init() + * @return bool true if the button is firmly pressed, false if released + */ +bool button_is_pressed(uint32_t pin); + +/** + * @brief Initialize a GPIO pin as a push-pull digital output for an indicator LED + * + * Configures the pin as a digital output and drives it low (LED off). Use + * together with button_led_set() to mirror button state visually. + * + * @param pin GPIO pin number to configure as an LED output + */ +void button_led_init(uint32_t pin); + +/** + * @brief Set the indicator LED state + * + * Drives the output pin high (LED on) or low (LED off). + * + * @param pin GPIO pin number previously initialized with button_led_init() + * @param on true to turn the LED on, false to turn it off + */ +void button_led_set(uint32_t pin, bool on); + +#endif // BUTTON_H diff --git a/drivers/0x03_button/pico_sdk_import.cmake b/drivers/0x03_button/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x03_button/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x03_button_cbm/.vscode/c_cpp_properties.json b/drivers/0x03_button_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x03_button_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x03_button_cbm/.vscode/extensions.json b/drivers/0x03_button_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x03_button_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x03_button_cbm/.vscode/launch.json b/drivers/0x03_button_cbm/.vscode/launch.json new file mode 100644 index 0000000..d6b78d0 --- /dev/null +++ b/drivers/0x03_button_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/button.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDir": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDir": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x03_button_cbm/.vscode/settings.json b/drivers/0x03_button_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x03_button_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x03_button_cbm/.vscode/tasks.json b/drivers/0x03_button_cbm/.vscode/tasks.json new file mode 100644 index 0000000..157b5d2 --- /dev/null +++ b/drivers/0x03_button_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/button.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/button.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x03_button_cbm/Inc/rp2350.h b/drivers/0x03_button_cbm/Inc/rp2350.h new file mode 100644 index 0000000..bea038b --- /dev/null +++ b/drivers/0x03_button_cbm/Inc/rp2350.h @@ -0,0 +1,206 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[18]; // Other clock registers Address offset: 0x00-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_PUE_SHIFT 3U +#define PADS_BANK0_PDE_SHIFT 2U +#define PADS_BANK0_ISO_SHIFT 8U + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief GPIO pin definitions + */ +#define LED_PIN 25U +#define BUTTON_PIN 15U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_IN_OFFSET (0x004U / 4U) +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OUT_XOR_OFFSET (0x028U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) + +#endif /* __RP2350_H */ diff --git a/drivers/0x03_button_cbm/Inc/rp2350_button.h b/drivers/0x03_button_cbm/Inc/rp2350_button.h new file mode 100644 index 0000000..b7d7dc2 --- /dev/null +++ b/drivers/0x03_button_cbm/Inc/rp2350_button.h @@ -0,0 +1,53 @@ +/** + * @file rp2350_button.h + * @brief Button input driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Push-button GPIO input driver with software debounce. + * The button pin is configured as active-low with internal + * pull-up; pressing the button connects the pin to GND. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_BUTTON_H +#define __RP2350_BUTTON_H + +#include "rp2350.h" + +/** + * @brief Initialize a GPIO pin as an active-low button input with pull-up. + * @param pin GPIO pin number to configure as a button input + * @param debounce_ms debounce settling time in milliseconds + * @retval None + */ +void button_init(uint32_t pin, uint32_t debounce_ms); + +/** + * @brief Read the debounced state of the button. + * @param pin GPIO pin number previously initialized with button_init() + * @retval bool true if the button is firmly pressed, false if released + */ +bool button_is_pressed(uint32_t pin); + +#endif /* __RP2350_BUTTON_H */ diff --git a/drivers/0x03_button_cbm/Inc/rp2350_delay.h b/drivers/0x03_button_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..6860cf7 --- /dev/null +++ b/drivers/0x03_button_cbm/Inc/rp2350_delay.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond busy-wait delay calibrated for a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x03_button_cbm/Inc/rp2350_gpio.h b/drivers/0x03_button_cbm/Inc/rp2350_gpio.h new file mode 100644 index 0000000..93a116e --- /dev/null +++ b/drivers/0x03_button_cbm/Inc/rp2350_gpio.h @@ -0,0 +1,79 @@ +/** + * @file rp2350_gpio.h + * @brief GPIO driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * SIO-based GPIO configuration, set, clear, toggle, and read + * functions for the RP2350 GPIO pins 0-29. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_GPIO_H +#define __RP2350_GPIO_H + +#include "rp2350.h" + +/** + * @brief Configure a GPIO pin as SIO output. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_config(uint32_t gpio_num); + +/** + * @brief Drive a GPIO output high. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_set(uint32_t gpio_num); + +/** + * @brief Drive a GPIO output low. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_clear(uint32_t gpio_num); + +/** + * @brief Toggle a GPIO output. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_toggle(uint32_t gpio_num); + +/** + * @brief Configure a GPIO pin as SIO input with internal pull-up. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_config_input_pullup(uint32_t gpio_num); + +/** + * @brief Read the current input level of a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval bool true if pin is high, false if low + */ +bool gpio_get(uint32_t gpio_num); + +#endif /* __RP2350_GPIO_H */ diff --git a/drivers/0x03_button_cbm/Inc/rp2350_led.h b/drivers/0x03_button_cbm/Inc/rp2350_led.h new file mode 100644 index 0000000..12b56c3 --- /dev/null +++ b/drivers/0x03_button_cbm/Inc/rp2350_led.h @@ -0,0 +1,72 @@ +/** + * @file rp2350_led.h + * @brief LED driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * High-level GPIO output / LED driver wrapping the + * low-level GPIO functions. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_LED_H +#define __RP2350_LED_H + +#include "rp2350.h" + +/** + * @brief Initialize a GPIO pin as a push-pull digital output. + * @param pin GPIO pin number to configure + * @retval None + */ +void led_init(uint32_t pin); + +/** + * @brief Drive the output pin high (LED on). + * @param pin GPIO pin number + * @retval None + */ +void led_on(uint32_t pin); + +/** + * @brief Drive the output pin low (LED off). + * @param pin GPIO pin number + * @retval None + */ +void led_off(uint32_t pin); + +/** + * @brief Toggle the current state of the output pin. + * @param pin GPIO pin number + * @retval None + */ +void led_toggle(uint32_t pin); + +/** + * @brief Query the current drive state of the output pin. + * @param pin GPIO pin number + * @retval bool true if the pin is driven high, false if low + */ +bool led_get_state(uint32_t pin); + +#endif /* __RP2350_LED_H */ diff --git a/drivers/0x03_button_cbm/Inc/rp2350_reset.h b/drivers/0x03_button_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x03_button_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x03_button_cbm/Inc/rp2350_reset_handler.h b/drivers/0x03_button_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x03_button_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x03_button_cbm/Inc/rp2350_stack.h b/drivers/0x03_button_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x03_button_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x03_button_cbm/Inc/rp2350_uart.h b/drivers/0x03_button_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x03_button_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x03_button_cbm/Inc/rp2350_xosc.h b/drivers/0x03_button_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..41303ca --- /dev/null +++ b/drivers/0x03_button_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,49 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x03_button_cbm/Makefile b/drivers/0x03_button_cbm/Makefile new file mode 100644 index 0000000..2939900 --- /dev/null +++ b/drivers/0x03_button_cbm/Makefile @@ -0,0 +1,87 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C button driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = button + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_gpio.c \ + $(SRC_DIR)/rp2350_led.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/rp2350_button.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x03_button_cbm/Src/image_def.c b/drivers/0x03_button_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x03_button_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x03_button_cbm/Src/main.c b/drivers/0x03_button_cbm/Src/main.c new file mode 100644 index 0000000..96d6079 --- /dev/null +++ b/drivers/0x03_button_cbm/Src/main.c @@ -0,0 +1,103 @@ +/** + * @file main.c + * @brief Button demonstration: debounced press mirrors to LED + UART report. + * @author Kevin Thomas + * @date 2026 + * + * Demonstrates GPIO input using the button driver. The onboard + * LED mirrors the button state and every edge transition is + * reported over UART. + * + * Wiring: + * GPIO15 -> One leg of push button + * GND -> Other leg of push button + * GPIO25 -> Onboard LED (no external wiring needed) + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_button.h" +#include "rp2350_led.h" +#include "rp2350_uart.h" +#include "rp2350_delay.h" + +/** @brief Button debounce window in milliseconds */ +#define DEBOUNCE_MS 20 +/** @brief Button polling interval in milliseconds */ +#define POLL_DELAY_MS 10 + +/** + * @brief Drive the LED to match the current button state. + * @param pressed true if button is pressed, false if released + * @retval None + */ +static void set_led_state(bool pressed) +{ + if (pressed) + led_on(LED_PIN); + else + led_off(LED_PIN); +} + +/** + * @brief Report a button edge transition over UART. + * @param pressed current button state + * @param last_state pointer to the stored previous button state + * @retval None + */ +static void report_edge(bool pressed, bool *last_state) +{ + if (pressed != *last_state) + { + if (pressed) + uart_puts("Button: PRESSED\r\n"); + else + uart_puts("Button: RELEASED\r\n"); + *last_state = pressed; + } +} + +/** + * @brief Poll the button, mirror to LED, and report edges. + * @param last_state pointer to the stored previous button state + * @retval None + */ +static void poll_button(bool *last_state) +{ + bool pressed; + pressed = button_is_pressed(BUTTON_PIN); + set_led_state(pressed); + report_edge(pressed, last_state); +} + +int main(void) +{ + button_init(BUTTON_PIN, DEBOUNCE_MS); + led_init(LED_PIN); + uart_puts("Button driver initialized: button=GPIO15 led=GPIO25\r\n"); + bool last_state = false; + while (1) + { + poll_button(&last_state); + delay_ms(POLL_DELAY_MS); + } +} diff --git a/drivers/0x03_button_cbm/Src/rp2350_button.c b/drivers/0x03_button_cbm/Src/rp2350_button.c new file mode 100644 index 0000000..53c18d0 --- /dev/null +++ b/drivers/0x03_button_cbm/Src/rp2350_button.c @@ -0,0 +1,61 @@ +/** + * @file rp2350_button.c + * @brief Button input driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures a GPIO pin as an active-low input with internal + * pull-up and provides debounced press detection using a + * busy-wait confirmation delay. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_button.h" +#include "rp2350_gpio.h" +#include "rp2350_delay.h" + +static uint32_t debounce_delay_ms = 20; + +/** + * @brief Re-sample the pin after the debounce delay to confirm press. + * @param pin GPIO pin number to re-sample + * @retval bool true if the pin is still low after the debounce delay + */ +static bool debounce_confirm(uint32_t pin) +{ + delay_ms(debounce_delay_ms); + return !gpio_get(pin); +} + +void button_init(uint32_t pin, uint32_t debounce_ms) +{ + debounce_delay_ms = debounce_ms; + gpio_config_input_pullup(pin); +} + +bool button_is_pressed(uint32_t pin) +{ + if (!gpio_get(pin)) + return debounce_confirm(pin); + return false; +} diff --git a/drivers/0x03_button_cbm/Src/rp2350_delay.c b/drivers/0x03_button_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..70d7f16 --- /dev/null +++ b/drivers/0x03_button_cbm/Src/rp2350_delay.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond delay calibrated for a 12 MHz clock + * (3600 loop iterations per millisecond). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x03_button_cbm/Src/rp2350_gpio.c b/drivers/0x03_button_cbm/Src/rp2350_gpio.c new file mode 100644 index 0000000..4689ccd --- /dev/null +++ b/drivers/0x03_button_cbm/Src/rp2350_gpio.c @@ -0,0 +1,121 @@ +/** + * @file rp2350_gpio.c + * @brief GPIO driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * SIO-based GPIO configuration using IO_BANK0 and PADS_BANK0 + * register structs defined in rp2350.h. All register offsets + * verified against the RP2350 datasheet (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_gpio.h" + +/** + * @brief Configure pad control for a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_config_pad(uint32_t gpio_num) +{ + uint32_t value; + value = PADS_BANK0->GPIO[gpio_num]; + value &= ~(1U << PADS_BANK0_OD_SHIFT); + value |= (1U << PADS_BANK0_IE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + PADS_BANK0->GPIO[gpio_num] = value; +} + +/** + * @brief Set IO_BANK0 FUNCSEL to SIO for a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_config_funcsel(uint32_t gpio_num) +{ + uint32_t value; + value = IO_BANK0->GPIO[gpio_num].CTRL; + value &= ~IO_BANK0_CTRL_FUNCSEL_MASK; + value |= IO_BANK0_CTRL_FUNCSEL_SIO; + IO_BANK0->GPIO[gpio_num].CTRL = value; +} + +/** + * @brief Configure pad for input with pull-up enabled. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_config_pad_input_pullup(uint32_t gpio_num) +{ + uint32_t value; + value = PADS_BANK0->GPIO[gpio_num]; + value |= (1U << PADS_BANK0_IE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + value |= (1U << PADS_BANK0_PUE_SHIFT); + value &= ~(1U << PADS_BANK0_PDE_SHIFT); + PADS_BANK0->GPIO[gpio_num] = value; +} + +/** + * @brief Enable the output driver for a GPIO pin via SIO. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_enable_output(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OE_SET_OFFSET] = (1U << gpio_num); +} + +void gpio_config(uint32_t gpio_num) +{ + gpio_config_pad(gpio_num); + gpio_config_funcsel(gpio_num); + gpio_enable_output(gpio_num); +} + +void gpio_set(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_SET_OFFSET] = (1U << gpio_num); +} + +void gpio_clear(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_CLR_OFFSET] = (1U << gpio_num); +} + +void gpio_toggle(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_XOR_OFFSET] = (1U << gpio_num); +} + +bool gpio_get(uint32_t gpio_num) +{ + return (SIO[SIO_GPIO_IN_OFFSET] & (1U << gpio_num)) != 0; +} + +void gpio_config_input_pullup(uint32_t gpio_num) +{ + gpio_config_pad_input_pullup(gpio_num); + gpio_config_funcsel(gpio_num); +} diff --git a/drivers/0x03_button_cbm/Src/rp2350_led.c b/drivers/0x03_button_cbm/Src/rp2350_led.c new file mode 100644 index 0000000..633a6ca --- /dev/null +++ b/drivers/0x03_button_cbm/Src/rp2350_led.c @@ -0,0 +1,58 @@ +/** + * @file rp2350_led.c + * @brief LED driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * High-level wrapper around the GPIO driver for LED control. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_led.h" +#include "rp2350_gpio.h" + +void led_init(uint32_t pin) +{ + gpio_config(pin); + gpio_clear(pin); +} + +void led_on(uint32_t pin) +{ + gpio_set(pin); +} + +void led_off(uint32_t pin) +{ + gpio_clear(pin); +} + +void led_toggle(uint32_t pin) +{ + gpio_toggle(pin); +} + +bool led_get_state(uint32_t pin) +{ + return gpio_get(pin); +} diff --git a/drivers/0x03_button_cbm/Src/rp2350_reset.c b/drivers/0x03_button_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x03_button_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x03_button_cbm/Src/rp2350_reset_handler.c b/drivers/0x03_button_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..62b58d2 --- /dev/null +++ b/drivers/0x03_button_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,80 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, then branches + * to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x03_button_cbm/Src/rp2350_stack.c b/drivers/0x03_button_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x03_button_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x03_button_cbm/Src/rp2350_uart.c b/drivers/0x03_button_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x03_button_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x03_button_cbm/Src/rp2350_xosc.c b/drivers/0x03_button_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..e4a58c2 --- /dev/null +++ b/drivers/0x03_button_cbm/Src/rp2350_xosc.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} diff --git a/drivers/0x03_button_cbm/Src/vector_table.c b/drivers/0x03_button_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x03_button_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x03_button_cbm/build.log b/drivers/0x03_button_cbm/build.log new file mode 100644 index 0000000..ddf0e65 Binary files /dev/null and b/drivers/0x03_button_cbm/build.log differ diff --git a/drivers/0x03_button_cbm/linker.ld b/drivers/0x03_button_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x03_button_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x03_button_rust/.cargo/config.toml b/drivers/0x03_button_rust/.cargo/config.toml new file mode 100644 index 0000000..1791d74 --- /dev/null +++ b/drivers/0x03_button_rust/.cargo/config.toml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2021–2024 The rp-rs Developers +# Copyright (c) 2021 rp-rs organization +# Copyright (c) 2025 Raspberry Pi Ltd. +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# You might want to make a similar file in your own repository if you are +# writing programs for Raspberry Silicon microcontrollers. +# + +[build] +target = "thumbv8m.main-none-eabihf" +# Set the default target to match the Cortex-M33 in the RP2350 +# target = "thumbv8m.main-none-eabihf" +# target = "thumbv6m-none-eabi" +# target = "riscv32imac-unknown-none-elf" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the hard-float ABI for Arm mode. +# +# The FPU is enabled by default, and float function arguments use FPU +# registers. +[target.thumbv8m.main-none-eabihf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as a linker script. +# This is usually provided by the cortex-m-rt crate, and by default the +# version in that crate will include a file called `memory.x` which describes +# the particular memory layout for your specific chip. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the soft-float ABI for RISC-V mode. +# +# Hazard 3 does not have an FPU and so float function arguments use integer +# registers. +[target.riscv32imac-unknown-none-elf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Trp235x_riscv.x also tells the linker to use +# `rp235x_riscv.x` as a linker script. This adds in RP2350 RISC-V specific +# things that the riscv-rt crate's `link.x` requires and then includes +# `link.x` automatically. This is the reverse of how we do it on Cortex-M. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x03_button_rust/.gitignore b/drivers/0x03_button_rust/.gitignore new file mode 100644 index 0000000..03381c1 --- /dev/null +++ b/drivers/0x03_button_rust/.gitignore @@ -0,0 +1,113 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,macos,windows,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux diff --git a/drivers/0x03_button_rust/.pico-rs b/drivers/0x03_button_rust/.pico-rs new file mode 100644 index 0000000..1b6702a --- /dev/null +++ b/drivers/0x03_button_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 \ No newline at end of file diff --git a/drivers/0x03_button_rust/.vscode/extensions.json b/drivers/0x03_button_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x03_button_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x03_button_rust/.vscode/launch.json b/drivers/0x03_button_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x03_button_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x03_button_rust/.vscode/settings.json b/drivers/0x03_button_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x03_button_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x03_button_rust/.vscode/tasks.json b/drivers/0x03_button_rust/.vscode/tasks.json new file mode 100644 index 0000000..4194af4 --- /dev/null +++ b/drivers/0x03_button_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/drivers/0x03_button_rust/Cargo.toml b/drivers/0x03_button_rust/Cargo.toml new file mode 100644 index 0000000..8784140 --- /dev/null +++ b/drivers/0x03_button_rust/Cargo.toml @@ -0,0 +1,41 @@ + +[package] +edition = "2024" +name = "button" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "button_lib" +path = "src/lib.rs" + +[[bin]] +name = "button" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +embedded-hal = "1.0.0" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x03_button_rust/LICENSE-APACHE b/drivers/0x03_button_rust/LICENSE-APACHE new file mode 100644 index 0000000..8d99cbc --- /dev/null +++ b/drivers/0x03_button_rust/LICENSE-APACHE @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2021–2024 The rp-rs Developers + Copyright (c) 2021 rp-rs organization + Copyright (c) 2025 Raspberry Pi Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/drivers/0x03_button_rust/LICENSE-MIT b/drivers/0x03_button_rust/LICENSE-MIT new file mode 100644 index 0000000..5369c70 --- /dev/null +++ b/drivers/0x03_button_rust/LICENSE-MIT @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2021–2024 The rp-rs Developers +Copyright (c) 2021 rp-rs organization +Copyright (c) 2025 Raspberry Pi Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + \ No newline at end of file diff --git a/drivers/0x03_button_rust/build.rs b/drivers/0x03_button_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x03_button_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x03_button_rust/rp2040.x b/drivers/0x03_button_rust/rp2040.x new file mode 100644 index 0000000..0cc665b --- /dev/null +++ b/drivers/0x03_button_rust/rp2040.x @@ -0,0 +1,91 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + /* + * Here we assume you have 2048 KiB of Flash. This is what the Pi Pico + * has, but your board may have more or less Flash and you should adjust + * this value to suit. + */ + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + /* + * RAM consists of 4 banks, SRAM0-SRAM3, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K + /* + * RAM banks 4 and 5 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k + + /* SRAM banks 0-3 can also be accessed directly. However, those ranges + alias with the RAM mapping, above. So don't use them at the same time! + SRAM0 : ORIGIN = 0x21000000, LENGTH = 64k + SRAM1 : ORIGIN = 0x21010000, LENGTH = 64k + SRAM2 : ORIGIN = 0x21020000, LENGTH = 64k + SRAM3 : ORIGIN = 0x21030000, LENGTH = 64k + */ +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader + * + * An executable block of code which sets up the QSPI interface for + * 'Execute-In-Place' (or XIP) mode. Also sends chip-specific commands to + * the external flash chip. + * + * Must go at the start of external flash, where the Boot ROM expects it. + */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 512 bytes of flash, + * where picotool can find it + */ + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x03_button_rust/rp2350.x b/drivers/0x03_button_rust/rp2350.x new file mode 100644 index 0000000..bda94d8 --- /dev/null +++ b/drivers/0x03_button_rust/rp2350.x @@ -0,0 +1,83 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 4K of flash + * where the Boot ROM (and picotool) can find it + */ + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + /* move .text to start /after/ the boot info */ + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + /* ### Boot ROM extra info + * + * Goes after everything in our program, so it can contain a signature. + */ + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + \ No newline at end of file diff --git a/drivers/0x03_button_rust/rp2350_riscv.x b/drivers/0x03_button_rust/rp2350_riscv.x new file mode 100644 index 0000000..84388aa --- /dev/null +++ b/drivers/0x03_button_rust/rp2350_riscv.x @@ -0,0 +1,259 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol is not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `32`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > FLASH + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + __start_block_addr = .; + KEEP(*(.start_block)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + . = ALIGN(4); + } > FLASH + + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > FLASH + + .data : ALIGN(32) + { + _sidata = LOADADDR(.data); + __sidata = LOADADDR(.data); + _sdata = .; + __sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(32); + _edata = .; + __edata = .; + } > RAM AT > FLASH + + .bss (NOLOAD) : ALIGN(32) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(32); + _ebss = .; + } > RAM + + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > RAM + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > RAM + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(start_to_end = __end_block_addr - __start_block_addr); +PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 32 == 0, " +ERROR(riscv-rt): the start of the RAM must be 32-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, " +BUG(riscv-rt): .data is not 32-byte aligned"); + +ASSERT(_sidata % 32 == 0, " +BUG(riscv-rt): the LMA of .data is not 32-byte aligned"); + +ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, " +BUG(riscv-rt): .bss is not 32-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(riscv-rt): The .text section must be placed inside the FLASH region. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/drivers/0x03_button_rust/src/board.rs b/drivers/0x03_button_rust/src/board.rs new file mode 100644 index 0000000..7bb2385 --- /dev/null +++ b/drivers/0x03_button_rust/src/board.rs @@ -0,0 +1,309 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Interior mutability for shared delay access +use core::cell::RefCell; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{ + FunctionNull, FunctionSioInput, FunctionSioOutput, FunctionUart, Pin, PullDown, PullNone, + PullUp, +}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Debounce settling time in milliseconds. +pub(crate) const DEBOUNCE_MS: u32 = 20; + +/// Main-loop polling interval in milliseconds. +pub(crate) const POLL_MS: u32 = 10; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Type alias for the button input pin (GPIO 15, pull-up). +type BtnPin = Pin; + +/// Type alias for the LED output pin (GPIO 25, push-pull). +type LedPin = Pin; + +/// Initialise all peripherals and run the button debounce demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + uart.write_full_blocking(b"Button driver initialized: button=GPIO15 led=GPIO25\r\n"); + button_demo( + &uart, + p.gpio15, + p.gpio25, + &RefCell::new(init_delay(&clocks)), + ) +} + +/// Create button and LED drivers, then run the polling loop. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `btn_pin` - Default GPIO 15 pin for the button input. +/// * `led_pin` - Default GPIO 25 pin for the LED output. +/// * `delay` - Shared reference to the delay provider. +fn button_demo( + uart: &EnabledUart, + btn_pin: Pin, + led_pin: Pin, + delay: &RefCell, +) -> ! { + let mut btn = + button_lib::button::ButtonDriver::init(btn_pin.into_pull_up_input(), DEBOUNCE_MS, |ms| { + delay.borrow_mut().delay_ms(ms) + }); + let mut led = button_lib::button::ButtonLed::init(led_pin.into_push_pull_output()); + let mut last = false; + loop { + poll_button(uart, &mut btn, &mut led, &mut last, delay); + } +} + +/// Poll button state, update LED, and report edge transitions. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `btn` - Mutable reference to the button driver. +/// * `led` - Mutable reference to the LED driver. +/// * `last` - Mutable reference to the previous button state. +/// * `delay` - Shared reference to the delay provider. +fn poll_button( + uart: &EnabledUart, + btn: &mut button_lib::button::ButtonDriver, + led: &mut button_lib::button::ButtonLed, + last: &mut bool, + delay: &RefCell, +) { + let pressed = btn.is_pressed(); + led.set(pressed); + if pressed != *last { + report_edge(uart, pressed); + *last = pressed; + } + delay.borrow_mut().delay_ms(POLL_MS); +} + +/// Print button press/release message over UART. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `pressed` - `true` if the button is pressed, `false` if released. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `pressed` - The `pressed` parameter. +fn report_edge(uart: &EnabledUart, pressed: bool) { + let msg = if pressed { + b"Button: PRESSED\r\n" as &[u8] + } else { + b"Button: RELEASED\r\n" + }; + uart.write_full_blocking(msg); +} + diff --git a/drivers/0x03_button_rust/src/button.rs b/drivers/0x03_button_rust/src/button.rs new file mode 100644 index 0000000..1b5b169 --- /dev/null +++ b/drivers/0x03_button_rust/src/button.rs @@ -0,0 +1,357 @@ +//! Implementation module +//! +//! **File:** `button.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Embedded HAL digital I/O traits for GPIO pin control +use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin}; + +/// Push-button GPIO input driver with debounce. +pub struct ButtonDriver { + btn_pin: B, + debounce_ms: u32, + delay_fn: D, +} + +impl ButtonDriver { + /// Initialize a GPIO pin as an active-low button input with pull-up. + /// + /// The pin reads logic high when the button is open and logic low when + /// the button connects the pin to GND. + /// + /// # Arguments + /// + /// * `btn_pin` - GPIO pin configured as input with pull-up. + /// * `debounce_ms` - Debounce settling time in milliseconds (e.g. 20). + /// * `delay_fn` - Closure that sleeps for the given number of milliseconds. + /// + /// # Returns + /// + /// A new ButtonDriver instance. + /// + /// # Arguments + /// + /// * `btn_pin` - The `btn_pin` parameter. + /// * `debounce_ms` - The `debounce_ms` parameter. + /// * `delay_fn` - The `delay_fn` parameter. + /// + /// # Returns + /// + /// A new instance of the struct. + pub fn init(btn_pin: B, debounce_ms: u32, delay_fn: D) -> Self { + Self { + btn_pin, + debounce_ms, + delay_fn, + } + } + + /// Confirm a raw active-low pin read by waiting for the debounce period. + /// + /// After an initial low reading on the pin, this helper re-samples the pin + /// after debounce_ms milliseconds to confirm the reading is stable. + /// + /// # Returns + /// + /// `true` if the pin is still low after the debounce delay. + /// + /// # Returns + /// + /// `true` if successful or set, `false` otherwise. + fn debounce_confirm(&mut self) -> bool { + (self.delay_fn)(self.debounce_ms); + self.btn_pin.is_low().unwrap() + } + + /// Read the debounced state of the button. + /// + /// Returns true only when the pin reads low continuously for the debounce + /// period configured in init(). This prevents mechanical contact bounce + /// from registering as multiple rapid presses. + /// + /// # Returns + /// + /// `true` if the button is firmly pressed, `false` if released. + /// + /// # Returns + /// + /// `true` if successful or set, `false` otherwise. + pub fn is_pressed(&mut self) -> bool { + if self.btn_pin.is_low().unwrap() { + return self.debounce_confirm(); + } + false + } +} + +/// Indicator LED driver that owns a single output pin. +pub struct ButtonLed { + led_pin: L, +} + +impl ButtonLed { + /// Initialize a GPIO pin as a push-pull digital output for an indicator LED. + /// + /// Configures the output and drives it low (LED off). + /// + /// # Arguments + /// + /// * `led_pin` - GPIO pin configured as a digital output. + /// + /// # Returns + /// + /// A new ButtonLed instance. + /// + /// # Arguments + /// + /// * `led_pin` - The `led_pin` parameter. + /// + /// # Returns + /// + /// A new instance of the struct. + pub fn init(mut led_pin: L) -> Self { + led_pin.set_low().unwrap(); + Self { led_pin } + } + + /// Set the indicator LED state. + /// + /// Drives the output pin high (LED on) or low (LED off). + /// + /// # Arguments + /// + /// * `on` - `true` to turn the LED on, `false` to turn it off. + /// + /// # Arguments + /// + /// * `on` - The `on` parameter. + pub fn set(&mut self, on: bool) { + if on { + self.led_pin.set_high().unwrap(); + } else { + self.led_pin.set_low().unwrap(); + } + } +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + // Infallible error type for mock pin implementations + use core::convert::Infallible; + // Error type trait for mock pin implementations + use embedded_hal::digital::ErrorType; + + struct MockInputPin { + low: bool, + } + + impl MockInputPin { + /// Creates a new hardware wrapper instance. + /// + /// # Arguments + /// + /// * `low` - The `low` parameter. + /// + /// # Returns + /// + /// A new instance of the struct. + fn new(low: bool) -> Self { + Self { low } + } + } + + impl ErrorType for MockInputPin { + type Error = Infallible; + } + + impl InputPin for MockInputPin { + /// Executes the is high operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn is_high(&mut self) -> Result { + Ok(!self.low) + } + + /// Executes the is low operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn is_low(&mut self) -> Result { + Ok(self.low) + } + } + + struct MockOutputPin { + state: bool, + } + + impl MockOutputPin { + /// Creates a new hardware wrapper instance. + /// + /// # Returns + /// + /// A new instance of the struct. + fn new() -> Self { + Self { state: false } + } + } + + impl ErrorType for MockOutputPin { + type Error = Infallible; + } + + impl OutputPin for MockOutputPin { + /// Executes the set low operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn set_low(&mut self) -> Result<(), Self::Error> { + self.state = false; + Ok(()) + } + + /// Executes the set high operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn set_high(&mut self) -> Result<(), Self::Error> { + self.state = true; + Ok(()) + } + } + + impl StatefulOutputPin for MockOutputPin { + /// Executes the is set high operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn is_set_high(&mut self) -> Result { + Ok(self.state) + } + + /// Executes the is set low operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn is_set_low(&mut self) -> Result { + Ok(!self.state) + } + + /// Executes the toggle operation. + /// + /// # Returns + /// + /// A Result indicating success or failure. + fn toggle(&mut self) -> Result<(), Self::Error> { + self.state = !self.state; + Ok(()) + } + } + + /// Executes the noop delay operation. + /// + /// # Arguments + /// + /// * `ms` - The `ms` parameter. + fn noop_delay(_ms: u32) {} + + /// Executes the is pressed when low and confirmed operation. + #[test] + fn is_pressed_when_low_and_confirmed() { + let pin = MockInputPin::new(true); + let mut drv = ButtonDriver::init(pin, 20, noop_delay); + assert!(drv.is_pressed()); + } + + /// Executes the is not pressed when high operation. + #[test] + fn is_not_pressed_when_high() { + let pin = MockInputPin::new(false); + let mut drv = ButtonDriver::init(pin, 20, noop_delay); + assert!(!drv.is_pressed()); + } + + /// Executes the debounce ms stored operation. + #[test] + fn debounce_ms_stored() { + let pin = MockInputPin::new(false); + let drv = ButtonDriver::init(pin, 50, noop_delay); + assert_eq!(drv.debounce_ms, 50); + } + + /// Executes the debounce calls delay operation. + #[test] + fn debounce_calls_delay() { + let mut called_with: u32 = 0; + let pin = MockInputPin::new(true); + let mut drv = ButtonDriver::init(pin, 25, |ms| called_with = ms); + drv.is_pressed(); + assert_eq!(called_with, 25); + } + + /// Executes the led init starts off operation. + #[test] + fn led_init_starts_off() { + let led = ButtonLed::init(MockOutputPin::new()); + assert!(!led.led_pin.state); + } + + /// Executes the led set on operation. + #[test] + fn led_set_on() { + let mut led = ButtonLed::init(MockOutputPin::new()); + led.set(true); + assert!(led.led_pin.state); + } + + /// Executes the led set off operation. + #[test] + fn led_set_off() { + let mut led = ButtonLed::init(MockOutputPin::new()); + led.set(true); + led.set(false); + assert!(!led.led_pin.state); + } + + /// Executes the led set on then off then on operation. + #[test] + fn led_set_on_then_off_then_on() { + let mut led = ButtonLed::init(MockOutputPin::new()); + led.set(true); + led.set(false); + led.set(true); + assert!(led.led_pin.state); + } +} diff --git a/drivers/0x03_button_rust/src/lib.rs b/drivers/0x03_button_rust/src/lib.rs new file mode 100644 index 0000000..5c41471 --- /dev/null +++ b/drivers/0x03_button_rust/src/lib.rs @@ -0,0 +1,37 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `lib.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![cfg_attr(not(test), no_std)] + +// Button driver module +pub mod button; diff --git a/drivers/0x03_button_rust/src/main.rs b/drivers/0x03_button_rust/src/main.rs new file mode 100644 index 0000000..a1fa27e --- /dev/null +++ b/drivers/0x03_button_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// Button driver module — suppress warnings for unused public API functions +#[allow(dead_code)] +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the button debounce demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"Button Debounce Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x04_pwm/.vscode/.vscode/c_cpp_properties.json b/drivers/0x04_pwm/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x04_pwm/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x04_pwm/.vscode/.vscode/cmake-kits.json b/drivers/0x04_pwm/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x04_pwm/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x04_pwm/.vscode/.vscode/extensions.json b/drivers/0x04_pwm/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x04_pwm/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x04_pwm/.vscode/.vscode/launch.json b/drivers/0x04_pwm/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x04_pwm/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x04_pwm/.vscode/.vscode/settings.json b/drivers/0x04_pwm/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x04_pwm/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x04_pwm/.vscode/.vscode/tasks.json b/drivers/0x04_pwm/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x04_pwm/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x04_pwm/.vscode/c_cpp_properties.json b/drivers/0x04_pwm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x04_pwm/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x04_pwm/.vscode/cmake-kits.json b/drivers/0x04_pwm/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x04_pwm/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x04_pwm/.vscode/extensions.json b/drivers/0x04_pwm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x04_pwm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x04_pwm/.vscode/launch.json b/drivers/0x04_pwm/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x04_pwm/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x04_pwm/.vscode/settings.json b/drivers/0x04_pwm/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x04_pwm/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x04_pwm/.vscode/tasks.json b/drivers/0x04_pwm/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x04_pwm/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x04_pwm/0x04_pwm.c b/drivers/0x04_pwm/0x04_pwm.c new file mode 100644 index 0000000..2f36f81 --- /dev/null +++ b/drivers/0x04_pwm/0x04_pwm.c @@ -0,0 +1,77 @@ +/** + * @file 0x04_pwm.c + * @brief PWM demonstration: LED breathing effect via duty-cycle sweep + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates PWM output using the pwm driver (pwm.h / pwm.c). A 1 kHz + * signal on GPIO 25 (onboard LED) sweeps its duty cycle from 0% to 100% + * and back to produce a smooth breathing effect. The current duty is + * reported over UART at 115200 baud. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * GPIO25 -> Onboard LED (no external wiring needed) + */ + +#include +#include "pico/stdlib.h" +#include "pwm.h" + +/** @brief GPIO pin for PWM output */ +#define PWM_PIN 25 +/** @brief Target PWM frequency in Hz */ +#define PWM_FREQ_HZ 1000 + +/** + * @brief Sweep the PWM duty cycle between start and end in given steps + * + * Iterates from start to end with the given step increment, updating + * the PWM duty cycle and printing each value with a 50 ms delay. + * + * @param start Starting duty percentage + * @param end Ending duty percentage + * @param step Increment per iteration (negative for descending) + */ +static void sweep_duty(int start, int end, int step) { + for (int duty = start; (step > 0) ? duty <= end : duty >= end; duty += step) { + pwm_driver_set_duty_percent((uint8_t)duty); + printf("Duty: %3d%%\r\n", duty); + sleep_ms(50); + } +} + +int main(void) { + stdio_init_all(); + pwm_driver_init(PWM_PIN, PWM_FREQ_HZ); + printf("PWM initialized: GPIO%d @ %d Hz\r\n", PWM_PIN, PWM_FREQ_HZ); + while (true) { + sweep_duty(0, 100, 5); + sweep_duty(100, 0, -5); + } +} diff --git a/drivers/0x04_pwm/CMakeLists.txt b/drivers/0x04_pwm/CMakeLists.txt new file mode 100644 index 0000000..f9fa8ef --- /dev/null +++ b/drivers/0x04_pwm/CMakeLists.txt @@ -0,0 +1,58 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x04_pwm C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x04_pwm 0x04_pwm.c pwm.c) + +pico_set_program_name(0x04_pwm "0x04_pwm") +pico_set_program_version(0x04_pwm "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x04_pwm 1) +pico_enable_stdio_usb(0x04_pwm 0) + +# Add the standard library to the build +target_link_libraries(0x04_pwm + pico_stdlib + hardware_pwm + hardware_clocks) + +# Add the standard include files to the build +target_include_directories(0x04_pwm PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x04_pwm) diff --git a/drivers/0x04_pwm/pico_sdk_import.cmake b/drivers/0x04_pwm/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x04_pwm/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x04_pwm/pwm.c b/drivers/0x04_pwm/pwm.c new file mode 100644 index 0000000..05849e6 --- /dev/null +++ b/drivers/0x04_pwm/pwm.c @@ -0,0 +1,88 @@ +/** + * @file pwm.c + * @brief Implementation of the generic PWM output driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "pwm.h" +#include "pico/stdlib.h" +#include "hardware/pwm.h" +#include "hardware/clocks.h" + +/** @brief PWM hardware slice index */ +static uint pwm_slice; +/** @brief PWM channel (A or B) within the slice */ +static uint pwm_chan; +/** @brief PWM counter wrap value */ +static uint32_t pwm_wrap; + +/** + * @brief Compute the PWM clock divider that yields the target frequency + * + * Derives a floating-point divider from the current system clock frequency, + * the desired output frequency, and the chosen wrap value so that the PWM + * counter overflows exactly freq_hz times per second. + * + * @param freq_hz Desired PWM output frequency in Hz + * @param wrap_val Chosen PWM counter wrap value (period - 1) + * @return float Clock divider to program into the PWM slice + */ +static float calc_clk_div(uint32_t freq_hz, uint32_t wrap_val) { + uint32_t sys_hz = clock_get_hz(clk_sys); + return (float)sys_hz / ((float)freq_hz * (float)(wrap_val + 1)); +} + +/** + * @brief Apply the PWM configuration to the active slice + * + * Builds a default config, sets the clock divider for the target frequency, + * programs the wrap value, starts the slice, and zeroes the channel level. + * + * @param freq_hz Desired PWM output frequency in Hz + */ +static void apply_pwm_config(uint32_t freq_hz) { + pwm_config cfg = pwm_get_default_config(); + pwm_config_set_clkdiv(&cfg, calc_clk_div(freq_hz, pwm_wrap)); + pwm_config_set_wrap(&cfg, pwm_wrap); + pwm_init(pwm_slice, &cfg, true); + pwm_set_chan_level(pwm_slice, pwm_chan, 0); +} + +void pwm_driver_init(uint32_t pin, uint32_t freq_hz) { + gpio_set_function(pin, GPIO_FUNC_PWM); + pwm_slice = pwm_gpio_to_slice_num(pin); + pwm_chan = pwm_gpio_to_channel(pin); + pwm_wrap = 10000 - 1; + apply_pwm_config(freq_hz); +} + +void pwm_driver_set_duty_percent(uint8_t percent) { + if (percent > 100) { + percent = 100; + } + uint32_t level = ((uint32_t)percent * (pwm_wrap + 1)) / 100; + pwm_set_chan_level(pwm_slice, pwm_chan, level); +} diff --git a/drivers/0x04_pwm/pwm.h b/drivers/0x04_pwm/pwm.h new file mode 100644 index 0000000..fc9e0a2 --- /dev/null +++ b/drivers/0x04_pwm/pwm.h @@ -0,0 +1,59 @@ +/** + * @file pwm.h + * @brief Header for generic PWM output driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef PWM_H +#define PWM_H + +#include + +/** + * @brief Initialize PWM output on the specified GPIO pin at a target frequency + * + * Assigns the GPIO pin to the PWM function, calculates the clock divider and + * wrap value from the system clock to achieve the requested frequency, then + * starts the PWM slice with a 0% duty cycle. Must be called once before using + * pwm_driver_set_duty_percent(). + * + * @param pin GPIO pin number to drive with PWM output + * @param freq_hz Desired PWM frequency in Hz (e.g. 1000 for 1 kHz) + */ +void pwm_driver_init(uint32_t pin, uint32_t freq_hz); + +/** + * @brief Set the PWM duty cycle as an integer percentage + * + * Maps the percentage value to the internal PWM counter range and writes + * the result to the channel level register. Values above 100 are clamped + * to 100. + * + * @param percent Duty cycle from 0 (always low) to 100 (always high) + */ +void pwm_driver_set_duty_percent(uint8_t percent); + +#endif // PWM_H diff --git a/drivers/0x04_pwm_cbm/.vscode/c_cpp_properties.json b/drivers/0x04_pwm_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x04_pwm_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x04_pwm_cbm/.vscode/extensions.json b/drivers/0x04_pwm_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x04_pwm_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x04_pwm_cbm/.vscode/launch.json b/drivers/0x04_pwm_cbm/.vscode/launch.json new file mode 100644 index 0000000..c5e08af --- /dev/null +++ b/drivers/0x04_pwm_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/pwm.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDir": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDir": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x04_pwm_cbm/.vscode/settings.json b/drivers/0x04_pwm_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x04_pwm_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x04_pwm_cbm/.vscode/tasks.json b/drivers/0x04_pwm_cbm/.vscode/tasks.json new file mode 100644 index 0000000..9f7f41c --- /dev/null +++ b/drivers/0x04_pwm_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/pwm.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/pwm.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x04_pwm_cbm/Inc/rp2350.h b/drivers/0x04_pwm_cbm/Inc/rp2350.h new file mode 100644 index 0000000..24af5bb --- /dev/null +++ b/drivers/0x04_pwm_cbm/Inc/rp2350.h @@ -0,0 +1,223 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define PWM_BASE 0x400A8000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[18]; // Other clock registers Address offset: 0x00-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_PWM_SHIFT 16U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_PWM 0x04U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief GPIO pin definitions + */ +#define LED_PIN 25U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_IN_OFFSET (0x004U / 4U) +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OUT_XOR_OFFSET (0x028U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) + +/** + * @brief PWM register offsets (per slice, stride 0x14) + */ +#define PWM_CH_CSR_OFFSET 0x00U +#define PWM_CH_DIV_OFFSET 0x04U +#define PWM_CH_CC_OFFSET 0x0CU +#define PWM_CH_TOP_OFFSET 0x10U +#define PWM_CH_STRIDE 0x14U + +/** + * @brief PWM bit definitions + */ +#define PWM_CSR_EN_SHIFT 0U +#define PWM_DIV_INT_SHIFT 4U +#define PWM_CC_B_SHIFT 16U +#define PWM_WRAP_DEFAULT 9999U + +#endif /* __RP2350_H */ diff --git a/drivers/0x04_pwm_cbm/Inc/rp2350_delay.h b/drivers/0x04_pwm_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..6860cf7 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Inc/rp2350_delay.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond busy-wait delay calibrated for a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x04_pwm_cbm/Inc/rp2350_gpio.h b/drivers/0x04_pwm_cbm/Inc/rp2350_gpio.h new file mode 100644 index 0000000..ca3595f --- /dev/null +++ b/drivers/0x04_pwm_cbm/Inc/rp2350_gpio.h @@ -0,0 +1,72 @@ +/** + * @file rp2350_gpio.h + * @brief GPIO driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * SIO-based GPIO configuration, set, clear, toggle, and read + * functions for the RP2350 GPIO pins 0-29. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_GPIO_H +#define __RP2350_GPIO_H + +#include "rp2350.h" + +/** + * @brief Configure a GPIO pin as SIO output. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_config(uint32_t gpio_num); + +/** + * @brief Drive a GPIO output high. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_set(uint32_t gpio_num); + +/** + * @brief Drive a GPIO output low. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_clear(uint32_t gpio_num); + +/** + * @brief Toggle a GPIO output. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_toggle(uint32_t gpio_num); + +/** + * @brief Read the current input level of a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval bool true if pin is high, false if low + */ +bool gpio_get(uint32_t gpio_num); + +#endif /* __RP2350_GPIO_H */ diff --git a/drivers/0x04_pwm_cbm/Inc/rp2350_led.h b/drivers/0x04_pwm_cbm/Inc/rp2350_led.h new file mode 100644 index 0000000..12b56c3 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Inc/rp2350_led.h @@ -0,0 +1,72 @@ +/** + * @file rp2350_led.h + * @brief LED driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * High-level GPIO output / LED driver wrapping the + * low-level GPIO functions. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_LED_H +#define __RP2350_LED_H + +#include "rp2350.h" + +/** + * @brief Initialize a GPIO pin as a push-pull digital output. + * @param pin GPIO pin number to configure + * @retval None + */ +void led_init(uint32_t pin); + +/** + * @brief Drive the output pin high (LED on). + * @param pin GPIO pin number + * @retval None + */ +void led_on(uint32_t pin); + +/** + * @brief Drive the output pin low (LED off). + * @param pin GPIO pin number + * @retval None + */ +void led_off(uint32_t pin); + +/** + * @brief Toggle the current state of the output pin. + * @param pin GPIO pin number + * @retval None + */ +void led_toggle(uint32_t pin); + +/** + * @brief Query the current drive state of the output pin. + * @param pin GPIO pin number + * @retval bool true if the pin is driven high, false if low + */ +bool led_get_state(uint32_t pin); + +#endif /* __RP2350_LED_H */ diff --git a/drivers/0x04_pwm_cbm/Inc/rp2350_pwm.h b/drivers/0x04_pwm_cbm/Inc/rp2350_pwm.h new file mode 100644 index 0000000..037d5f9 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Inc/rp2350_pwm.h @@ -0,0 +1,57 @@ +/** + * @file rp2350_pwm.h + * @brief PWM driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides PWM output on GPIO 25 (onboard LED) at approximately + * 1 kHz using PWM slice 4, channel B. Duty cycle is controllable + * from 0 to 100 percent. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_PWM_H +#define __RP2350_PWM_H + +#include "rp2350.h" + +/** + * @brief Release PWM from reset and wait until ready. + * @retval None + */ +void pwm_release_reset(void); + +/** + * @brief Initialize PWM on GPIO 25 at approximately 1 kHz. + * @retval None + */ +void pwm_init(void); + +/** + * @brief Set the PWM duty cycle as an integer percentage. + * @param percent duty cycle from 0 (off) to 100 (fully on) + * @retval None + */ +void pwm_set_duty(uint8_t percent); + +#endif /* __RP2350_PWM_H */ diff --git a/drivers/0x04_pwm_cbm/Inc/rp2350_reset.h b/drivers/0x04_pwm_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x04_pwm_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x04_pwm_cbm/Inc/rp2350_reset_handler.h b/drivers/0x04_pwm_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x04_pwm_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x04_pwm_cbm/Inc/rp2350_stack.h b/drivers/0x04_pwm_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x04_pwm_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x04_pwm_cbm/Inc/rp2350_uart.h b/drivers/0x04_pwm_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x04_pwm_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x04_pwm_cbm/Inc/rp2350_xosc.h b/drivers/0x04_pwm_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..41303ca --- /dev/null +++ b/drivers/0x04_pwm_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,49 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x04_pwm_cbm/Makefile b/drivers/0x04_pwm_cbm/Makefile new file mode 100644 index 0000000..2b5aad8 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Makefile @@ -0,0 +1,85 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C PWM driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = pwm + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_pwm.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x04_pwm_cbm/Src/image_def.c b/drivers/0x04_pwm_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x04_pwm_cbm/Src/main.c b/drivers/0x04_pwm_cbm/Src/main.c new file mode 100644 index 0000000..f541c71 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/main.c @@ -0,0 +1,115 @@ +/** + * @file main.c + * @brief PWM demonstration: LED breathing effect via duty-cycle sweep. + * @author Kevin Thomas + * @date 2026 + * + * Demonstrates PWM output using the PWM driver. A signal on + * GPIO 25 (onboard LED) sweeps its duty cycle from 0% to 100% + * and back to produce a smooth breathing effect. The current + * duty is reported over UART. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * GPIO25 -> Onboard LED (no external wiring needed) + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_pwm.h" +#include "rp2350_uart.h" +#include "rp2350_delay.h" + +/** @brief PWM duty cycle increment per step */ +#define SWEEP_STEP 5 +/** @brief Delay between PWM sweep steps in milliseconds */ +#define SWEEP_DELAY_MS 50 + +/** + * @brief Convert a uint8_t to a decimal string. + * @param value number to convert (0-255) + * @param buf output buffer (at least 4 bytes) + * @retval None + */ +static void uint8_to_str(uint8_t value, char *buf) +{ + uint8_t idx = 0; + if (value >= 100) + buf[idx++] = (char)('0' + value / 100); + if (value >= 10) + buf[idx++] = (char)('0' + (value / 10) % 10); + buf[idx++] = (char)('0' + value % 10); + buf[idx] = '\0'; +} + +/** + * @brief Print a duty percentage value as a decimal string over UART. + * @param duty duty cycle value (0-100) + * @retval None + */ +static void print_duty(uint8_t duty) +{ + char buf[4]; + uint8_to_str(duty, buf); + uart_puts("Duty: "); + uart_puts(buf); + uart_puts("%\r\n"); +} + +/** + * @brief Sweep duty cycle upward from 0 to 100 percent. + * @retval None + */ +static void sweep_up(void) +{ + for (uint8_t duty = 0; duty <= 100; duty += SWEEP_STEP) + { + pwm_set_duty(duty); + print_duty(duty); + delay_ms(SWEEP_DELAY_MS); + } +} + +/** + * @brief Sweep duty cycle downward from 100 to 0 percent. + * @retval None + */ +static void sweep_down(void) +{ + for (int8_t duty = 100; duty >= 0; duty -= SWEEP_STEP) + { + pwm_set_duty((uint8_t)duty); + print_duty((uint8_t)duty); + delay_ms(SWEEP_DELAY_MS); + } +} + +int main(void) +{ + uart_puts("PWM initialized: GPIO25\r\n"); + while (1) + { + sweep_up(); + sweep_down(); + } +} diff --git a/drivers/0x04_pwm_cbm/Src/rp2350_delay.c b/drivers/0x04_pwm_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..70d7f16 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/rp2350_delay.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond delay calibrated for a 12 MHz clock + * (3600 loop iterations per millisecond). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x04_pwm_cbm/Src/rp2350_gpio.c b/drivers/0x04_pwm_cbm/Src/rp2350_gpio.c new file mode 100644 index 0000000..f2fdb20 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/rp2350_gpio.c @@ -0,0 +1,99 @@ +/** + * @file rp2350_gpio.c + * @brief GPIO driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * SIO-based GPIO configuration using IO_BANK0 and PADS_BANK0 + * register structs defined in rp2350.h. All register offsets + * verified against the RP2350 datasheet (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_gpio.h" + +/** + * @brief Configure pad control for a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_config_pad(uint32_t gpio_num) +{ + uint32_t value; + value = PADS_BANK0->GPIO[gpio_num]; + value &= ~(1U << PADS_BANK0_OD_SHIFT); + value |= (1U << PADS_BANK0_IE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + PADS_BANK0->GPIO[gpio_num] = value; +} + +/** + * @brief Set IO_BANK0 FUNCSEL to SIO for a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_config_funcsel(uint32_t gpio_num) +{ + uint32_t value; + value = IO_BANK0->GPIO[gpio_num].CTRL; + value &= ~IO_BANK0_CTRL_FUNCSEL_MASK; + value |= IO_BANK0_CTRL_FUNCSEL_SIO; + IO_BANK0->GPIO[gpio_num].CTRL = value; +} + +/** + * @brief Enable the output driver for a GPIO pin via SIO. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_enable_output(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OE_SET_OFFSET] = (1U << gpio_num); +} + +void gpio_config(uint32_t gpio_num) +{ + gpio_config_pad(gpio_num); + gpio_config_funcsel(gpio_num); + gpio_enable_output(gpio_num); +} + +void gpio_set(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_SET_OFFSET] = (1U << gpio_num); +} + +void gpio_clear(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_CLR_OFFSET] = (1U << gpio_num); +} + +void gpio_toggle(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_XOR_OFFSET] = (1U << gpio_num); +} + +bool gpio_get(uint32_t gpio_num) +{ + return (SIO[SIO_GPIO_IN_OFFSET] & (1U << gpio_num)) != 0; +} diff --git a/drivers/0x04_pwm_cbm/Src/rp2350_led.c b/drivers/0x04_pwm_cbm/Src/rp2350_led.c new file mode 100644 index 0000000..633a6ca --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/rp2350_led.c @@ -0,0 +1,58 @@ +/** + * @file rp2350_led.c + * @brief LED driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * High-level wrapper around the GPIO driver for LED control. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_led.h" +#include "rp2350_gpio.h" + +void led_init(uint32_t pin) +{ + gpio_config(pin); + gpio_clear(pin); +} + +void led_on(uint32_t pin) +{ + gpio_set(pin); +} + +void led_off(uint32_t pin) +{ + gpio_clear(pin); +} + +void led_toggle(uint32_t pin) +{ + gpio_toggle(pin); +} + +bool led_get_state(uint32_t pin) +{ + return gpio_get(pin); +} diff --git a/drivers/0x04_pwm_cbm/Src/rp2350_pwm.c b/drivers/0x04_pwm_cbm/Src/rp2350_pwm.c new file mode 100644 index 0000000..55c1c3e --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/rp2350_pwm.c @@ -0,0 +1,135 @@ +/** + * @file rp2350_pwm.c + * @brief PWM driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures PWM slice 4, channel B on GPIO 25 (onboard LED). + * Uses a wrap value of 9999 with no clock division, yielding + * approximately 650 Hz from the ROSC system clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_pwm.h" + +/** @brief Base address pointer for PWM peripheral registers */ +#define PWM ((volatile uint32_t *) PWM_BASE) + +/** + * @brief PWM slice number for GPIO 25. + */ +#define PWM_SLICE 4U + +/** + * @brief Compute the byte offset for a register within the slice. + * @param reg per-slice register offset (e.g. PWM_CH_CSR_OFFSET) + * @retval uint32_t word index from PWM_BASE + */ +#define PWM_REG(reg) (((PWM_SLICE * PWM_CH_STRIDE) + (reg)) / 4U) + +/** + * @brief Clear the PWM reset bit in the reset controller. + * @retval None + */ +static void pwm_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_PWM_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the PWM block is out of reset. + * @retval None + */ +static void pwm_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_PWM_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO 25 pad and funcsel for PWM output. + * @retval None + */ +static void pwm_configure_pin(void) +{ + uint32_t value; + value = PADS_BANK0->GPIO[LED_PIN]; + value &= ~(1U << PADS_BANK0_OD_SHIFT); + value |= (1U << PADS_BANK0_IE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + PADS_BANK0->GPIO[LED_PIN] = value; + IO_BANK0->GPIO[LED_PIN].CTRL = IO_BANK0_CTRL_FUNCSEL_PWM; +} + +/** + * @brief Set the clock divider to 1 (no division). + * @retval None + */ +static void pwm_set_divider(void) +{ + PWM[PWM_REG(PWM_CH_DIV_OFFSET)] = (1U << PWM_DIV_INT_SHIFT); +} + +/** + * @brief Set the wrap (TOP) value and zero the compare register. + * @retval None + */ +static void pwm_set_wrap(void) +{ + PWM[PWM_REG(PWM_CH_TOP_OFFSET)] = PWM_WRAP_DEFAULT; + PWM[PWM_REG(PWM_CH_CC_OFFSET)] = 0; +} + +/** + * @brief Enable the PWM slice counter. + * @retval None + */ +static void pwm_enable(void) +{ + PWM[PWM_REG(PWM_CH_CSR_OFFSET)] = (1U << PWM_CSR_EN_SHIFT); +} + +void pwm_release_reset(void) +{ + pwm_clear_reset_bit(); + pwm_wait_reset_done(); +} + +void pwm_init(void) +{ + pwm_configure_pin(); + pwm_set_divider(); + pwm_set_wrap(); + pwm_enable(); +} + +void pwm_set_duty(uint8_t percent) +{ + uint32_t level; + if (percent > 100) + percent = 100; + level = ((uint32_t)percent * (PWM_WRAP_DEFAULT + 1)) / 100; + PWM[PWM_REG(PWM_CH_CC_OFFSET)] = (level << PWM_CC_B_SHIFT); +} diff --git a/drivers/0x04_pwm_cbm/Src/rp2350_reset.c b/drivers/0x04_pwm_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x04_pwm_cbm/Src/rp2350_reset_handler.c b/drivers/0x04_pwm_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..4688437 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,88 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, PWM, then + * branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" +#include "rp2350_pwm.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void _late_init(void) +{ + pwm_release_reset(); + pwm_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "bl _late_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x04_pwm_cbm/Src/rp2350_stack.c b/drivers/0x04_pwm_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x04_pwm_cbm/Src/rp2350_uart.c b/drivers/0x04_pwm_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x04_pwm_cbm/Src/rp2350_xosc.c b/drivers/0x04_pwm_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..e4a58c2 --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/rp2350_xosc.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} diff --git a/drivers/0x04_pwm_cbm/Src/vector_table.c b/drivers/0x04_pwm_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x04_pwm_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x04_pwm_cbm/build.log b/drivers/0x04_pwm_cbm/build.log new file mode 100644 index 0000000..fd7204c Binary files /dev/null and b/drivers/0x04_pwm_cbm/build.log differ diff --git a/drivers/0x04_pwm_cbm/linker.ld b/drivers/0x04_pwm_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x04_pwm_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x04_pwm_rust/.cargo/config.toml b/drivers/0x04_pwm_rust/.cargo/config.toml new file mode 100644 index 0000000..1791d74 --- /dev/null +++ b/drivers/0x04_pwm_rust/.cargo/config.toml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2021–2024 The rp-rs Developers +# Copyright (c) 2021 rp-rs organization +# Copyright (c) 2025 Raspberry Pi Ltd. +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# You might want to make a similar file in your own repository if you are +# writing programs for Raspberry Silicon microcontrollers. +# + +[build] +target = "thumbv8m.main-none-eabihf" +# Set the default target to match the Cortex-M33 in the RP2350 +# target = "thumbv8m.main-none-eabihf" +# target = "thumbv6m-none-eabi" +# target = "riscv32imac-unknown-none-elf" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the hard-float ABI for Arm mode. +# +# The FPU is enabled by default, and float function arguments use FPU +# registers. +[target.thumbv8m.main-none-eabihf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as a linker script. +# This is usually provided by the cortex-m-rt crate, and by default the +# version in that crate will include a file called `memory.x` which describes +# the particular memory layout for your specific chip. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the soft-float ABI for RISC-V mode. +# +# Hazard 3 does not have an FPU and so float function arguments use integer +# registers. +[target.riscv32imac-unknown-none-elf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Trp235x_riscv.x also tells the linker to use +# `rp235x_riscv.x` as a linker script. This adds in RP2350 RISC-V specific +# things that the riscv-rt crate's `link.x` requires and then includes +# `link.x` automatically. This is the reverse of how we do it on Cortex-M. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x04_pwm_rust/.gitignore b/drivers/0x04_pwm_rust/.gitignore new file mode 100644 index 0000000..03381c1 --- /dev/null +++ b/drivers/0x04_pwm_rust/.gitignore @@ -0,0 +1,113 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,macos,windows,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux diff --git a/drivers/0x04_pwm_rust/.pico-rs b/drivers/0x04_pwm_rust/.pico-rs new file mode 100644 index 0000000..1b6702a --- /dev/null +++ b/drivers/0x04_pwm_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 \ No newline at end of file diff --git a/drivers/0x04_pwm_rust/.vscode/extensions.json b/drivers/0x04_pwm_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x04_pwm_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x04_pwm_rust/.vscode/launch.json b/drivers/0x04_pwm_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x04_pwm_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x04_pwm_rust/.vscode/settings.json b/drivers/0x04_pwm_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x04_pwm_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x04_pwm_rust/.vscode/tasks.json b/drivers/0x04_pwm_rust/.vscode/tasks.json new file mode 100644 index 0000000..4194af4 --- /dev/null +++ b/drivers/0x04_pwm_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/drivers/0x04_pwm_rust/Cargo.toml b/drivers/0x04_pwm_rust/Cargo.toml new file mode 100644 index 0000000..f1d8282 --- /dev/null +++ b/drivers/0x04_pwm_rust/Cargo.toml @@ -0,0 +1,41 @@ + +[package] +edition = "2024" +name = "pwm" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "pwm_lib" +path = "src/lib.rs" + +[[bin]] +name = "pwm" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +embedded-hal = "1.0.0" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x04_pwm_rust/LICENSE-APACHE b/drivers/0x04_pwm_rust/LICENSE-APACHE new file mode 100644 index 0000000..8d99cbc --- /dev/null +++ b/drivers/0x04_pwm_rust/LICENSE-APACHE @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2021–2024 The rp-rs Developers + Copyright (c) 2021 rp-rs organization + Copyright (c) 2025 Raspberry Pi Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/drivers/0x04_pwm_rust/LICENSE-MIT b/drivers/0x04_pwm_rust/LICENSE-MIT new file mode 100644 index 0000000..5369c70 --- /dev/null +++ b/drivers/0x04_pwm_rust/LICENSE-MIT @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2021–2024 The rp-rs Developers +Copyright (c) 2021 rp-rs organization +Copyright (c) 2025 Raspberry Pi Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + \ No newline at end of file diff --git a/drivers/0x04_pwm_rust/build.rs b/drivers/0x04_pwm_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x04_pwm_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x04_pwm_rust/rp2040.x b/drivers/0x04_pwm_rust/rp2040.x new file mode 100644 index 0000000..0cc665b --- /dev/null +++ b/drivers/0x04_pwm_rust/rp2040.x @@ -0,0 +1,91 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + /* + * Here we assume you have 2048 KiB of Flash. This is what the Pi Pico + * has, but your board may have more or less Flash and you should adjust + * this value to suit. + */ + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + /* + * RAM consists of 4 banks, SRAM0-SRAM3, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K + /* + * RAM banks 4 and 5 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k + + /* SRAM banks 0-3 can also be accessed directly. However, those ranges + alias with the RAM mapping, above. So don't use them at the same time! + SRAM0 : ORIGIN = 0x21000000, LENGTH = 64k + SRAM1 : ORIGIN = 0x21010000, LENGTH = 64k + SRAM2 : ORIGIN = 0x21020000, LENGTH = 64k + SRAM3 : ORIGIN = 0x21030000, LENGTH = 64k + */ +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader + * + * An executable block of code which sets up the QSPI interface for + * 'Execute-In-Place' (or XIP) mode. Also sends chip-specific commands to + * the external flash chip. + * + * Must go at the start of external flash, where the Boot ROM expects it. + */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 512 bytes of flash, + * where picotool can find it + */ + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x04_pwm_rust/rp2350.x b/drivers/0x04_pwm_rust/rp2350.x new file mode 100644 index 0000000..bda94d8 --- /dev/null +++ b/drivers/0x04_pwm_rust/rp2350.x @@ -0,0 +1,83 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 4K of flash + * where the Boot ROM (and picotool) can find it + */ + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + /* move .text to start /after/ the boot info */ + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + /* ### Boot ROM extra info + * + * Goes after everything in our program, so it can contain a signature. + */ + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + \ No newline at end of file diff --git a/drivers/0x04_pwm_rust/rp2350_riscv.x b/drivers/0x04_pwm_rust/rp2350_riscv.x new file mode 100644 index 0000000..84388aa --- /dev/null +++ b/drivers/0x04_pwm_rust/rp2350_riscv.x @@ -0,0 +1,259 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol is not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `32`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > FLASH + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + __start_block_addr = .; + KEEP(*(.start_block)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + . = ALIGN(4); + } > FLASH + + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > FLASH + + .data : ALIGN(32) + { + _sidata = LOADADDR(.data); + __sidata = LOADADDR(.data); + _sdata = .; + __sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(32); + _edata = .; + __edata = .; + } > RAM AT > FLASH + + .bss (NOLOAD) : ALIGN(32) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(32); + _ebss = .; + } > RAM + + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > RAM + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > RAM + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(start_to_end = __end_block_addr - __start_block_addr); +PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 32 == 0, " +ERROR(riscv-rt): the start of the RAM must be 32-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, " +BUG(riscv-rt): .data is not 32-byte aligned"); + +ASSERT(_sidata % 32 == 0, " +BUG(riscv-rt): the LMA of .data is not 32-byte aligned"); + +ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, " +BUG(riscv-rt): .bss is not 32-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(riscv-rt): The .text section must be placed inside the FLASH region. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/drivers/0x04_pwm_rust/src/board.rs b/drivers/0x04_pwm_rust/src/board.rs new file mode 100644 index 0000000..a835ff2 --- /dev/null +++ b/drivers/0x04_pwm_rust/src/board.rs @@ -0,0 +1,418 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// PWM duty-cycle trait for .set_duty_cycle() +use embedded_hal::pwm::SetDutyCycle; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Desired PWM output frequency in Hz. +pub(crate) const PWM_FREQ_HZ: u32 = 1000; + +/// PWM counter wrap value (period - 1). +pub(crate) const PWM_WRAP: u32 = 10000 - 1; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Format a duty percentage into a fixed byte buffer as "Duty: NNN%\r\n". +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 16 bytes). +/// * `duty` - Duty cycle percentage to format. +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `duty` - The `duty` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub(crate) fn format_duty(buf: &mut [u8], duty: u8) -> usize { + buf[..6].copy_from_slice(b"Duty: "); + let pos = 6 + write_duty_digits(&mut buf[6..], duty); + buf[pos..pos + 3].copy_from_slice(b"%\r\n"); + pos + 3 +} + +/// Write the 3-character right-justified duty digits into `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `duty` - The `duty` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `duty` - The `duty` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn write_duty_digits(buf: &mut [u8], duty: u8) -> usize { + if duty >= 100 { + buf[..3].copy_from_slice(b"100"); + } else if duty >= 10 { + buf[..3].copy_from_slice(&[b' ', b'0' + duty / 10, b'0' + duty % 10]); + } else { + buf[..3].copy_from_slice(&[b' ', b' ', b'0' + duty]); + } + 3 +} + +/// Sweep the PWM duty cycle from 0% to 100% in steps of 5. +/// +/// # Arguments +/// +/// * `uart` - UART peripheral for serial output. +/// * `channel` - PWM channel to set duty on. +/// * `delay` - Delay provider for 50 ms pauses. +/// * `buf` - Scratch buffer for formatting output. +pub(crate) fn sweep_up( + uart: &EnabledUart, + channel: &mut impl SetDutyCycle, + delay: &mut cortex_m::delay::Delay, + buf: &mut [u8; 16], +) { + let mut duty: u8 = 0; + while duty <= 100 { + apply_duty(uart, channel, delay, buf, duty); + duty += 5; + } +} + +/// Sweep the PWM duty cycle from 100% to 0% in steps of 5. +/// +/// # Arguments +/// +/// * `uart` - UART peripheral for serial output. +/// * `channel` - PWM channel to set duty on. +/// * `delay` - Delay provider for 50 ms pauses. +/// * `buf` - Scratch buffer for formatting output. +pub(crate) fn sweep_down( + uart: &EnabledUart, + channel: &mut impl SetDutyCycle, + delay: &mut cortex_m::delay::Delay, + buf: &mut [u8; 16], +) { + let mut duty: i8 = 100; + while duty >= 0 { + apply_duty(uart, channel, delay, buf, duty as u8); + duty -= 5; + } +} + +/// Apply a single duty step: set PWM level, format, print, and delay. +fn apply_duty( + uart: &EnabledUart, + channel: &mut impl SetDutyCycle, + delay: &mut cortex_m::delay::Delay, + buf: &mut [u8; 16], + duty: u8, +) { + let level = pwm_lib::pwm::duty_to_level(duty, PWM_WRAP) as u16; + channel.set_duty_cycle(level).ok(); + let n = format_duty(buf, duty); + uart.write_full_blocking(&buf[..n]); + delay.delay_ms(50u32); +} + +/// Type alias for PWM slice 4 (onboard LED, GPIO 25). +type PwmSlice4 = hal::pwm::Slice; + +/// Initialise all peripherals and run the PWM breathing demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + let mut pwm = init_pwm(pac.PWM, &mut pac.RESETS, &clocks, p.gpio25); + uart.write_full_blocking(b"PWM initialized: GPIO25 @ 1000 Hz\r\n"); + pwm_loop(&uart, &mut pwm, &mut delay) +} + +/// Configure PWM slice 4 for 1 kHz output on channel B (GPIO 25). +/// +/// # Arguments +/// +/// * `pwm_pac` - PAC PWM peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// * `led_pin` - Default GPIO 25 pin to bind to PWM channel B. +/// +/// # Returns +/// +/// Configured PWM slice 4 in free-running mode. +fn init_pwm( + pwm_pac: hal::pac::PWM, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, + led_pin: Pin, +) -> PwmSlice4 { + let slices = hal::pwm::Slices::new(pwm_pac, resets); + let mut slice = slices.pwm4; + configure_pwm_div(&mut slice, clocks); + slice.set_top(PWM_WRAP as u16); + slice.enable(); + slice.channel_b.output_to(led_pin); + slice +} + +/// Set the clock divider for a PWM slice based on the system clock. +/// +/// # Arguments +/// +/// * `slice` - Mutable reference to the PWM slice to configure. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Arguments +/// +/// * `slice` - The `slice` parameter. +/// * `clocks` - The `clocks` parameter. +fn configure_pwm_div(slice: &mut PwmSlice4, clocks: &hal::clocks::ClocksManager) { + let sys_hz = clocks.system_clock.freq().to_Hz(); + let div = pwm_lib::pwm::calc_clk_div(sys_hz, PWM_FREQ_HZ, PWM_WRAP); + let div_int = div as u8; + slice.set_div_int(div_int); + slice.set_div_frac((((div - div_int as f32) * 16.0) as u8).min(15)); +} + +/// Run the PWM duty-cycle sweep loop forever. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `pwm` - Mutable reference to the configured PWM slice. +/// * `delay` - Mutable reference to the blocking delay provider. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `pwm` - The `pwm` parameter. +/// * `delay` - Delay value. +/// +/// # Returns +/// +/// A value of type `!`. +fn pwm_loop(uart: &EnabledUart, pwm: &mut PwmSlice4, delay: &mut cortex_m::delay::Delay) -> ! { + let mut buf = [0u8; 16]; + loop { + sweep_up(uart, &mut pwm.channel_b, delay, &mut buf); + sweep_down(uart, &mut pwm.channel_b, delay, &mut buf); + } +} + diff --git a/drivers/0x04_pwm_rust/src/lib.rs b/drivers/0x04_pwm_rust/src/lib.rs new file mode 100644 index 0000000..a3f36ec --- /dev/null +++ b/drivers/0x04_pwm_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod pwm; diff --git a/drivers/0x04_pwm_rust/src/main.rs b/drivers/0x04_pwm_rust/src/main.rs new file mode 100644 index 0000000..f5b7651 --- /dev/null +++ b/drivers/0x04_pwm_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// PWM driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the PWM LED breathing demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"PWM LED Breathing Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x04_pwm_rust/src/pwm.rs b/drivers/0x04_pwm_rust/src/pwm.rs new file mode 100644 index 0000000..12fa6f6 --- /dev/null +++ b/drivers/0x04_pwm_rust/src/pwm.rs @@ -0,0 +1,180 @@ +//! Implementation module +//! +//! **File:** `pwm.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// Compute the PWM clock divider that yields the target frequency. +/// +/// Derives a floating-point divider from the system clock frequency, +/// the desired output frequency, and the chosen wrap value so that the PWM +/// counter overflows exactly freq_hz times per second. +/// +/// # Arguments +/// +/// * `sys_hz` - System clock frequency in Hz. +/// * `freq_hz` - Desired PWM output frequency in Hz. +/// * `wrap_val` - Chosen PWM counter wrap value (period - 1). +/// +/// # Returns +/// +/// Clock divider to program into the PWM slice. +/// +/// # Arguments +/// +/// * `sys_hz` - The `sys_hz` parameter. +/// * `freq_hz` - The `freq_hz` parameter. +/// * `wrap_val` - The `wrap_val` parameter. +/// +/// # Returns +/// +/// A value of type `f32`. +pub fn calc_clk_div(sys_hz: u32, freq_hz: u32, wrap_val: u32) -> f32 { + sys_hz as f32 / (freq_hz as f32 * (wrap_val + 1) as f32) +} + +/// Clamp a duty percentage to the valid 0–100 range. +/// +/// Values above 100 are clamped to 100. +/// +/// # Arguments +/// +/// * `percent` - Raw duty cycle percentage. +/// +/// # Returns +/// +/// Clamped percentage in the range 0..=100. +/// +/// # Arguments +/// +/// * `percent` - The `percent` parameter. +/// +/// # Returns +/// +/// An 8-bit unsigned integer value. +pub fn clamp_percent(percent: u8) -> u8 { + if percent > 100 { 100 } else { percent } +} + +/// Map a duty percentage to a PWM channel level. +/// +/// Converts a 0–100 percentage to the internal PWM counter range based +/// on the configured wrap value. +/// +/// # Arguments +/// +/// * `percent` - Duty cycle from 0 (always low) to 100 (always high). +/// * `wrap` - PWM counter wrap value (period - 1). +/// +/// # Returns +/// +/// Channel level value to write to the PWM slice. +/// +/// # Arguments +/// +/// * `percent` - The `percent` parameter. +/// * `wrap` - The `wrap` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +pub fn duty_to_level(percent: u8, wrap: u32) -> u32 { + let p = clamp_percent(percent); + (p as u32 * (wrap + 1)) / 100 +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the calc clk div 1khz operation. + #[test] + fn calc_clk_div_1khz() { + let div = calc_clk_div(150_000_000, 1000, 9999); + assert!((div - 15.0).abs() < 0.01); + } + + /// Executes the calc clk div 10khz operation. + #[test] + fn calc_clk_div_10khz() { + let div = calc_clk_div(150_000_000, 10000, 9999); + assert!((div - 1.5).abs() < 0.01); + } + + /// Executes the clamp percent within range operation. + #[test] + fn clamp_percent_within_range() { + assert_eq!(clamp_percent(50), 50); + } + + /// Executes the clamp percent at 100 operation. + #[test] + fn clamp_percent_at_100() { + assert_eq!(clamp_percent(100), 100); + } + + /// Executes the clamp percent above 100 operation. + #[test] + fn clamp_percent_above_100() { + assert_eq!(clamp_percent(255), 100); + } + + /// Executes the clamp percent zero operation. + #[test] + fn clamp_percent_zero() { + assert_eq!(clamp_percent(0), 0); + } + + /// Executes the duty to level zero operation. + #[test] + fn duty_to_level_zero() { + assert_eq!(duty_to_level(0, 9999), 0); + } + + /// Executes the duty to level 100 operation. + #[test] + fn duty_to_level_100() { + assert_eq!(duty_to_level(100, 9999), 10000); + } + + /// Executes the duty to level 50 operation. + #[test] + fn duty_to_level_50() { + assert_eq!(duty_to_level(50, 9999), 5000); + } + + /// Executes the duty to level clamped operation. + #[test] + fn duty_to_level_clamped() { + assert_eq!(duty_to_level(200, 9999), 10000); + } + + /// Executes the duty to level 5 operation. + #[test] + fn duty_to_level_5() { + assert_eq!(duty_to_level(5, 9999), 500); + } +} diff --git a/drivers/0x05_servo/.vscode/.vscode/c_cpp_properties.json b/drivers/0x05_servo/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x05_servo/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x05_servo/.vscode/.vscode/cmake-kits.json b/drivers/0x05_servo/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x05_servo/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x05_servo/.vscode/.vscode/extensions.json b/drivers/0x05_servo/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x05_servo/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x05_servo/.vscode/.vscode/launch.json b/drivers/0x05_servo/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x05_servo/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x05_servo/.vscode/.vscode/settings.json b/drivers/0x05_servo/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x05_servo/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x05_servo/.vscode/.vscode/tasks.json b/drivers/0x05_servo/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x05_servo/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x05_servo/.vscode/c_cpp_properties.json b/drivers/0x05_servo/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x05_servo/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x05_servo/.vscode/cmake-kits.json b/drivers/0x05_servo/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x05_servo/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x05_servo/.vscode/extensions.json b/drivers/0x05_servo/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x05_servo/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x05_servo/.vscode/launch.json b/drivers/0x05_servo/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x05_servo/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x05_servo/.vscode/settings.json b/drivers/0x05_servo/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x05_servo/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x05_servo/.vscode/tasks.json b/drivers/0x05_servo/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x05_servo/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x05_servo/0x05_servo.c b/drivers/0x05_servo/0x05_servo.c new file mode 100644 index 0000000..d2c02f0 --- /dev/null +++ b/drivers/0x05_servo/0x05_servo.c @@ -0,0 +1,80 @@ +/** + * @file 0x05_servo.c + * @brief SG90 servo motor driver for the Raspberry Pi Pico 2 + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * This driver demonstrates SG90 servo control using the servo.c/servo.h + * driver (PWM at 50 Hz on GPIO 6). The servo sweeps from 0 degrees to + * 180 degrees and back in 10-degree increments, printing each angle over + * UART. + * + * Wiring: + * GPIO6 -> Servo signal wire (orange or yellow) + * 5V -> Servo power wire (red) -- use external 5 V supply for load + * GND -> Servo ground wire (brown or black) + */ + +#include +#include "pico/stdlib.h" +#include "servo.h" + +/** @brief GPIO pin for the servo PWM output */ +#define SERVO_GPIO 6 +/** @brief Angle increment per sweep step in degrees */ +#define STEP_DEGREES 10 +/** @brief Delay between sweep steps in milliseconds */ +#define STEP_DELAY_MS 150 + +/** + * @brief Sweep the servo between start and end angles in given steps + * + * Iterates from start to end with the given step increment, setting + * each angle and printing the current position with a delay between steps. + * + * @param start Starting angle in degrees + * @param end Ending angle in degrees + * @param step Increment per iteration (negative for descending) + */ +static void sweep_angle(int start, int end, int step) { + for (int angle = start; (step > 0) ? angle <= end : angle >= end; angle += step) { + servo_set_angle((float)angle); + printf("Angle: %3d deg\r\n", angle); + sleep_ms(STEP_DELAY_MS); + } +} + +int main(void) { + stdio_init_all(); + servo_init(SERVO_GPIO); + printf("Servo driver initialized on GPIO %d\r\n", SERVO_GPIO); + printf("Sweeping 0 -> 180 -> 0 degrees in %d-degree steps\r\n", STEP_DEGREES); + while (true) { + sweep_angle(0, 180, STEP_DEGREES); + sweep_angle(180, 0, -STEP_DEGREES); + } +} diff --git a/drivers/0x05_servo/CMakeLists.txt b/drivers/0x05_servo/CMakeLists.txt new file mode 100644 index 0000000..55266bd --- /dev/null +++ b/drivers/0x05_servo/CMakeLists.txt @@ -0,0 +1,58 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x05_servo C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x05_servo 0x05_servo.c servo.c) + +pico_set_program_name(0x05_servo "0x05_servo") +pico_set_program_version(0x05_servo "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x05_servo 1) +pico_enable_stdio_usb(0x05_servo 0) + +# Add the standard library to the build +target_link_libraries(0x05_servo + pico_stdlib + hardware_pwm + hardware_clocks) + +# Add the standard include files to the build +target_include_directories(0x05_servo PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x05_servo) diff --git a/drivers/0x05_servo/pico_sdk_import.cmake b/drivers/0x05_servo/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x05_servo/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x05_servo/servo.c b/drivers/0x05_servo/servo.c new file mode 100644 index 0000000..d0fefbd --- /dev/null +++ b/drivers/0x05_servo/servo.c @@ -0,0 +1,107 @@ +/** + * @file servo.c + * @brief Implementation of a simple SG90 servo driver using PWM (50Hz) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "servo.h" +#include "pico/stdlib.h" +#include "hardware/pwm.h" +#include "hardware/clocks.h" + +/** @brief Default minimum pulse width in microseconds */ +static const uint16_t SERVO_DEFAULT_MIN_US = 1000; +/** @brief Default maximum pulse width in microseconds */ +static const uint16_t SERVO_DEFAULT_MAX_US = 2000; + +/** @brief GPIO pin assigned to the servo */ +static uint8_t servo_pin = 0; +/** @brief PWM hardware slice for the servo pin */ +static uint servo_slice = 0; +/** @brief PWM channel within the servo slice */ +static uint servo_chan = 0; +/** @brief PWM counter wrap value for 50 Hz servo */ +static uint32_t servo_wrap = 20000 - 1; +/** @brief Servo PWM frequency in Hz */ +static float servo_hz = 50.0f; +/** @brief Flag indicating servo has been initialized */ +static bool servo_initialized = false; + +/** + * @brief Convert a pulse width in microseconds to a PWM counter level + * + * Uses the configured PWM wrap and servo frequency to map pulse time + * into the channel compare value expected by the PWM hardware. + * + * @param pulse_us Pulse width in microseconds + * @return uint32_t PWM level suitable for pwm_set_chan_level() + */ +static uint32_t pulse_us_to_level(uint32_t pulse_us) { + const float period_us = 1000000.0f / servo_hz; + float counts_per_us = (servo_wrap + 1) / period_us; + return (uint32_t)(pulse_us * counts_per_us + 0.5f); +} + +/** + * @brief Build and apply the PWM slice configuration for 50 Hz servo + * + * Computes the clock divider from the system clock to achieve the + * target servo frequency with the chosen wrap value, then starts + * the PWM slice. + */ +static void apply_servo_config(void) { + pwm_config config = pwm_get_default_config(); + const uint32_t sys_clock_hz = clock_get_hz(clk_sys); + float clock_div = (float)sys_clock_hz / (servo_hz * (servo_wrap + 1)); + pwm_config_set_clkdiv(&config, clock_div); + pwm_config_set_wrap(&config, servo_wrap); + pwm_init(servo_slice, &config, true); +} + +void servo_init(uint8_t pin) { + servo_pin = pin; + gpio_set_function(servo_pin, GPIO_FUNC_PWM); + servo_slice = pwm_gpio_to_slice_num(servo_pin); + servo_chan = pwm_gpio_to_channel(servo_pin); + apply_servo_config(); + servo_initialized = true; +} + +void servo_set_pulse_us(uint16_t pulse_us) { + if (!servo_initialized) return; + if (pulse_us < SERVO_DEFAULT_MIN_US) pulse_us = SERVO_DEFAULT_MIN_US; + if (pulse_us > SERVO_DEFAULT_MAX_US) pulse_us = SERVO_DEFAULT_MAX_US; + uint32_t level = pulse_us_to_level(pulse_us); + pwm_set_chan_level(servo_slice, servo_chan, level); +} + +void servo_set_angle(float degrees) { + if (degrees < 0.0f) degrees = 0.0f; + if (degrees > 180.0f) degrees = 180.0f; + float ratio = degrees / 180.0f; + uint16_t pulse = (uint16_t)(SERVO_DEFAULT_MIN_US + ratio * (SERVO_DEFAULT_MAX_US - SERVO_DEFAULT_MIN_US) + 0.5f); + servo_set_pulse_us(pulse); +} \ No newline at end of file diff --git a/drivers/0x05_servo/servo.h b/drivers/0x05_servo/servo.h new file mode 100644 index 0000000..878d029 --- /dev/null +++ b/drivers/0x05_servo/servo.h @@ -0,0 +1,62 @@ +/** + * @file servo.h + * @brief Header for SG90 servo driver (PWM) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SERVO_H +#define SERVO_H + +#include +#include + +/** + * @brief Initialize servo driver on a given GPIO pin + * + * Configures PWM for a 50 Hz servo (SG90). Call once before using other API. + * + * @param pin GPIO pin number to use for servo PWM + */ +void servo_init(uint8_t pin); + +/** + * @brief Set servo pulse width in microseconds (typical 1000-2000) + * + * @param pulse_us Pulse width in microseconds + */ +void servo_set_pulse_us(uint16_t pulse_us); + +/** + * @brief Set servo angle in degrees (0 to 180) + * + * Maps 0..180 degrees to the configured pulse range (default 1000..2000 us). + * Values outside [0,180] will be clamped. + * + * @param degrees Angle in degrees + */ +void servo_set_angle(float degrees); + +#endif // SERVO_H diff --git a/drivers/0x05_servo_cbm/.vscode/c_cpp_properties.json b/drivers/0x05_servo_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x05_servo_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x05_servo_cbm/.vscode/extensions.json b/drivers/0x05_servo_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x05_servo_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x05_servo_cbm/.vscode/launch.json b/drivers/0x05_servo_cbm/.vscode/launch.json new file mode 100644 index 0000000..f599abf --- /dev/null +++ b/drivers/0x05_servo_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/servo.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDir": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDir": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x05_servo_cbm/.vscode/settings.json b/drivers/0x05_servo_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x05_servo_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x05_servo_cbm/.vscode/tasks.json b/drivers/0x05_servo_cbm/.vscode/tasks.json new file mode 100644 index 0000000..b40fc37 --- /dev/null +++ b/drivers/0x05_servo_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/servo.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/servo.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x05_servo_cbm/Inc/rp2350.h b/drivers/0x05_servo_cbm/Inc/rp2350.h new file mode 100644 index 0000000..e484a5a --- /dev/null +++ b/drivers/0x05_servo_cbm/Inc/rp2350.h @@ -0,0 +1,224 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define PWM_BASE 0x400A8000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[18]; // Other clock registers Address offset: 0x00-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_PWM_SHIFT 16U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_PWM 0x04U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief GPIO pin definitions + */ +#define LED_PIN 25U +#define SERVO_PIN 6U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_IN_OFFSET (0x004U / 4U) +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OUT_XOR_OFFSET (0x028U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) + +/** + * @brief PWM register offsets (per slice, stride 0x14) + */ +#define PWM_CH_CSR_OFFSET 0x00U +#define PWM_CH_DIV_OFFSET 0x04U +#define PWM_CH_CC_OFFSET 0x0CU +#define PWM_CH_TOP_OFFSET 0x10U +#define PWM_CH_STRIDE 0x14U + +/** + * @brief PWM bit definitions + */ +#define PWM_CSR_EN_SHIFT 0U +#define PWM_DIV_INT_SHIFT 4U +#define PWM_CC_B_SHIFT 16U +#define PWM_WRAP_DEFAULT 9999U + +#endif /* __RP2350_H */ diff --git a/drivers/0x05_servo_cbm/Inc/rp2350_delay.h b/drivers/0x05_servo_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..6860cf7 --- /dev/null +++ b/drivers/0x05_servo_cbm/Inc/rp2350_delay.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond busy-wait delay calibrated for a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x05_servo_cbm/Inc/rp2350_gpio.h b/drivers/0x05_servo_cbm/Inc/rp2350_gpio.h new file mode 100644 index 0000000..ca3595f --- /dev/null +++ b/drivers/0x05_servo_cbm/Inc/rp2350_gpio.h @@ -0,0 +1,72 @@ +/** + * @file rp2350_gpio.h + * @brief GPIO driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * SIO-based GPIO configuration, set, clear, toggle, and read + * functions for the RP2350 GPIO pins 0-29. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_GPIO_H +#define __RP2350_GPIO_H + +#include "rp2350.h" + +/** + * @brief Configure a GPIO pin as SIO output. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_config(uint32_t gpio_num); + +/** + * @brief Drive a GPIO output high. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_set(uint32_t gpio_num); + +/** + * @brief Drive a GPIO output low. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_clear(uint32_t gpio_num); + +/** + * @brief Toggle a GPIO output. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +void gpio_toggle(uint32_t gpio_num); + +/** + * @brief Read the current input level of a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval bool true if pin is high, false if low + */ +bool gpio_get(uint32_t gpio_num); + +#endif /* __RP2350_GPIO_H */ diff --git a/drivers/0x05_servo_cbm/Inc/rp2350_led.h b/drivers/0x05_servo_cbm/Inc/rp2350_led.h new file mode 100644 index 0000000..12b56c3 --- /dev/null +++ b/drivers/0x05_servo_cbm/Inc/rp2350_led.h @@ -0,0 +1,72 @@ +/** + * @file rp2350_led.h + * @brief LED driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * High-level GPIO output / LED driver wrapping the + * low-level GPIO functions. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_LED_H +#define __RP2350_LED_H + +#include "rp2350.h" + +/** + * @brief Initialize a GPIO pin as a push-pull digital output. + * @param pin GPIO pin number to configure + * @retval None + */ +void led_init(uint32_t pin); + +/** + * @brief Drive the output pin high (LED on). + * @param pin GPIO pin number + * @retval None + */ +void led_on(uint32_t pin); + +/** + * @brief Drive the output pin low (LED off). + * @param pin GPIO pin number + * @retval None + */ +void led_off(uint32_t pin); + +/** + * @brief Toggle the current state of the output pin. + * @param pin GPIO pin number + * @retval None + */ +void led_toggle(uint32_t pin); + +/** + * @brief Query the current drive state of the output pin. + * @param pin GPIO pin number + * @retval bool true if the pin is driven high, false if low + */ +bool led_get_state(uint32_t pin); + +#endif /* __RP2350_LED_H */ diff --git a/drivers/0x05_servo_cbm/Inc/rp2350_reset.h b/drivers/0x05_servo_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x05_servo_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x05_servo_cbm/Inc/rp2350_reset_handler.h b/drivers/0x05_servo_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x05_servo_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x05_servo_cbm/Inc/rp2350_servo.h b/drivers/0x05_servo_cbm/Inc/rp2350_servo.h new file mode 100644 index 0000000..15c41ac --- /dev/null +++ b/drivers/0x05_servo_cbm/Inc/rp2350_servo.h @@ -0,0 +1,63 @@ +/** + * @file rp2350_servo.h + * @brief SG90 servo driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * PWM-based servo driver on GPIO 6 at 50 Hz. Supports pulse + * width control in microseconds and angle control in degrees. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_SERVO_H +#define __RP2350_SERVO_H + +#include "rp2350.h" + +/** + * @brief Release PWM from reset and wait until ready. + * @retval None + */ +void servo_release_reset(void); + +/** + * @brief Initialize servo PWM on GPIO 6 at 50 Hz. + * @retval None + */ +void servo_init(void); + +/** + * @brief Set the servo pulse width in microseconds (clamped 1000-2000). + * @param pulse_us pulse width in microseconds + * @retval None + */ +void servo_set_pulse_us(uint16_t pulse_us); + +/** + * @brief Set the servo angle in degrees (clamped 0-180). + * @param degrees angle from 0 to 180 + * @retval None + */ +void servo_set_angle(uint8_t degrees); + +#endif /* __RP2350_SERVO_H */ diff --git a/drivers/0x05_servo_cbm/Inc/rp2350_stack.h b/drivers/0x05_servo_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x05_servo_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x05_servo_cbm/Inc/rp2350_uart.h b/drivers/0x05_servo_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x05_servo_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x05_servo_cbm/Inc/rp2350_xosc.h b/drivers/0x05_servo_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..41303ca --- /dev/null +++ b/drivers/0x05_servo_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,49 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x05_servo_cbm/Makefile b/drivers/0x05_servo_cbm/Makefile new file mode 100644 index 0000000..4d9d67c --- /dev/null +++ b/drivers/0x05_servo_cbm/Makefile @@ -0,0 +1,85 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C servo driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = servo + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_servo.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x05_servo_cbm/Src/image_def.c b/drivers/0x05_servo_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x05_servo_cbm/Src/main.c b/drivers/0x05_servo_cbm/Src/main.c new file mode 100644 index 0000000..4da0034 --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/main.c @@ -0,0 +1,117 @@ +/** + * @file main.c + * @brief SG90 servo motor sweep demonstration. + * @author Kevin Thomas + * @date 2026 + * + * Demonstrates servo control using the servo driver. The servo + * sweeps from 0 to 180 degrees and back in 10-degree steps, + * reporting each angle over UART. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * GPIO6 -> Servo signal (orange/yellow) + * 5V -> Servo power (red) + * GND -> Servo ground (brown/black) + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_servo.h" +#include "rp2350_uart.h" +#include "rp2350_delay.h" + +/** @brief Servo angle increment per step in degrees */ +#define STEP_DEGREES 10 +/** @brief Delay between servo sweep steps in milliseconds */ +#define STEP_DELAY_MS 150 + +/** + * @brief Convert a uint8_t to a decimal string. + * @param value number to convert (0-255) + * @param buf output buffer (at least 4 bytes) + * @retval None + */ +static void uint8_to_str(uint8_t value, char *buf) +{ + uint8_t idx = 0; + if (value >= 100) + buf[idx++] = (char)('0' + value / 100); + if (value >= 10) + buf[idx++] = (char)('0' + (value / 10) % 10); + buf[idx++] = (char)('0' + value % 10); + buf[idx] = '\0'; +} + +/** + * @brief Print an angle value as a decimal string over UART. + * @param angle angle in degrees (0-180) + * @retval None + */ +static void print_angle(uint8_t angle) +{ + char buf[4]; + uint8_to_str(angle, buf); + uart_puts("Angle: "); + uart_puts(buf); + uart_puts(" deg\r\n"); +} + +/** + * @brief Sweep servo angle upward from 0 to 180 degrees. + * @retval None + */ +static void sweep_up(void) +{ + for (uint8_t angle = 0; angle <= 180; angle += STEP_DEGREES) + { + servo_set_angle(angle); + print_angle(angle); + delay_ms(STEP_DELAY_MS); + } +} + +/** + * @brief Sweep servo angle downward from 180 to 0 degrees. + * @retval None + */ +static void sweep_down(void) +{ + for (int16_t angle = 180; angle >= 0; angle -= STEP_DEGREES) + { + servo_set_angle((uint8_t)angle); + print_angle((uint8_t)angle); + delay_ms(STEP_DELAY_MS); + } +} + +int main(void) +{ + uart_puts("Servo driver initialized on GPIO6\r\n"); + uart_puts("Sweeping 0 -> 180 -> 0 degrees\r\n"); + while (1) + { + sweep_up(); + sweep_down(); + } +} diff --git a/drivers/0x05_servo_cbm/Src/rp2350_delay.c b/drivers/0x05_servo_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..70d7f16 --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/rp2350_delay.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond delay calibrated for a 12 MHz clock + * (3600 loop iterations per millisecond). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x05_servo_cbm/Src/rp2350_gpio.c b/drivers/0x05_servo_cbm/Src/rp2350_gpio.c new file mode 100644 index 0000000..f2fdb20 --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/rp2350_gpio.c @@ -0,0 +1,99 @@ +/** + * @file rp2350_gpio.c + * @brief GPIO driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * SIO-based GPIO configuration using IO_BANK0 and PADS_BANK0 + * register structs defined in rp2350.h. All register offsets + * verified against the RP2350 datasheet (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_gpio.h" + +/** + * @brief Configure pad control for a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_config_pad(uint32_t gpio_num) +{ + uint32_t value; + value = PADS_BANK0->GPIO[gpio_num]; + value &= ~(1U << PADS_BANK0_OD_SHIFT); + value |= (1U << PADS_BANK0_IE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + PADS_BANK0->GPIO[gpio_num] = value; +} + +/** + * @brief Set IO_BANK0 FUNCSEL to SIO for a GPIO pin. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_config_funcsel(uint32_t gpio_num) +{ + uint32_t value; + value = IO_BANK0->GPIO[gpio_num].CTRL; + value &= ~IO_BANK0_CTRL_FUNCSEL_MASK; + value |= IO_BANK0_CTRL_FUNCSEL_SIO; + IO_BANK0->GPIO[gpio_num].CTRL = value; +} + +/** + * @brief Enable the output driver for a GPIO pin via SIO. + * @param gpio_num GPIO pin number (0-29) + * @retval None + */ +static void gpio_enable_output(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OE_SET_OFFSET] = (1U << gpio_num); +} + +void gpio_config(uint32_t gpio_num) +{ + gpio_config_pad(gpio_num); + gpio_config_funcsel(gpio_num); + gpio_enable_output(gpio_num); +} + +void gpio_set(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_SET_OFFSET] = (1U << gpio_num); +} + +void gpio_clear(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_CLR_OFFSET] = (1U << gpio_num); +} + +void gpio_toggle(uint32_t gpio_num) +{ + SIO[SIO_GPIO_OUT_XOR_OFFSET] = (1U << gpio_num); +} + +bool gpio_get(uint32_t gpio_num) +{ + return (SIO[SIO_GPIO_IN_OFFSET] & (1U << gpio_num)) != 0; +} diff --git a/drivers/0x05_servo_cbm/Src/rp2350_led.c b/drivers/0x05_servo_cbm/Src/rp2350_led.c new file mode 100644 index 0000000..633a6ca --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/rp2350_led.c @@ -0,0 +1,58 @@ +/** + * @file rp2350_led.c + * @brief LED driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * High-level wrapper around the GPIO driver for LED control. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_led.h" +#include "rp2350_gpio.h" + +void led_init(uint32_t pin) +{ + gpio_config(pin); + gpio_clear(pin); +} + +void led_on(uint32_t pin) +{ + gpio_set(pin); +} + +void led_off(uint32_t pin) +{ + gpio_clear(pin); +} + +void led_toggle(uint32_t pin) +{ + gpio_toggle(pin); +} + +bool led_get_state(uint32_t pin) +{ + return gpio_get(pin); +} diff --git a/drivers/0x05_servo_cbm/Src/rp2350_reset.c b/drivers/0x05_servo_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x05_servo_cbm/Src/rp2350_reset_handler.c b/drivers/0x05_servo_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..d3ac7a2 --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,88 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, servo, then + * branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" +#include "rp2350_servo.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void _late_init(void) +{ + servo_release_reset(); + servo_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "bl _late_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x05_servo_cbm/Src/rp2350_servo.c b/drivers/0x05_servo_cbm/Src/rp2350_servo.c new file mode 100644 index 0000000..8841017 --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/rp2350_servo.c @@ -0,0 +1,176 @@ +/** + * @file rp2350_servo.c + * @brief SG90 servo driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures PWM slice 3, channel A on GPIO 6 at 50 Hz for + * standard hobby servo control. Uses a wrap of 19999 and a + * clock divider of 12 (12 MHz XOSC / 50 Hz / 20000 = 12). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_servo.h" + +/** @brief Base address pointer for PWM peripheral registers */ +#define PWM ((volatile uint32_t *) PWM_BASE) + +/** + * @brief PWM slice number for GPIO 6. + */ +#define SERVO_SLICE 3U + +/** + * @brief Wrap value for 50 Hz at 12 MHz with divider 12. + */ +#define SERVO_WRAP 19999U + +/** + * @brief Clock divider register value for 12 (INT=12, FRAC=0). + */ +#define SERVO_DIV_VAL ((12U << PWM_DIV_INT_SHIFT) | 0U) + +/** + * @brief Minimum pulse width in microseconds (0 degrees). + */ +#define SERVO_MIN_US 1000U + +/** + * @brief Maximum pulse width in microseconds (180 degrees). + */ +#define SERVO_MAX_US 2000U + +/** + * @brief Compute the byte offset for a register within the slice. + * @param reg per-slice register offset (e.g. PWM_CH_CSR_OFFSET) + * @retval uint32_t word index from PWM_BASE + */ +#define SERVO_REG(reg) (((SERVO_SLICE * PWM_CH_STRIDE) + (reg)) / 4U) + +/** + * @brief Clear the PWM reset bit in the reset controller. + * @retval None + */ +static void servo_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_PWM_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the PWM block is out of reset. + * @retval None + */ +static void servo_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_PWM_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO 6 pad and funcsel for PWM output. + * @retval None + */ +static void servo_configure_pin(void) +{ + uint32_t value; + value = PADS_BANK0->GPIO[SERVO_PIN]; + value &= ~(1U << PADS_BANK0_OD_SHIFT); + value |= (1U << PADS_BANK0_IE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + PADS_BANK0->GPIO[SERVO_PIN] = value; + IO_BANK0->GPIO[SERVO_PIN].CTRL = IO_BANK0_CTRL_FUNCSEL_PWM; +} + +/** + * @brief Set the fractional clock divider for 50 Hz servo frequency. + * @retval None + */ +static void servo_set_divider(void) +{ + PWM[SERVO_REG(PWM_CH_DIV_OFFSET)] = SERVO_DIV_VAL; +} + +/** + * @brief Set the wrap value and zero the compare register. + * @retval None + */ +static void servo_set_wrap(void) +{ + PWM[SERVO_REG(PWM_CH_TOP_OFFSET)] = SERVO_WRAP; + PWM[SERVO_REG(PWM_CH_CC_OFFSET)] = 0; +} + +/** + * @brief Enable the PWM slice counter. + * @retval None + */ +static void servo_enable(void) +{ + PWM[SERVO_REG(PWM_CH_CSR_OFFSET)] = (1U << PWM_CSR_EN_SHIFT); +} + +/** + * @brief Convert a pulse width in microseconds to a PWM counter level. + * @param pulse_us pulse width in microseconds + * @retval uint32_t PWM level for the CC register + */ +static uint32_t pulse_us_to_level(uint16_t pulse_us) +{ + return ((uint32_t)pulse_us * (SERVO_WRAP + 1)) / 20000U; +} + +void servo_release_reset(void) +{ + servo_clear_reset_bit(); + servo_wait_reset_done(); +} + +void servo_init(void) +{ + servo_configure_pin(); + servo_set_divider(); + servo_set_wrap(); + servo_enable(); +} + +void servo_set_pulse_us(uint16_t pulse_us) +{ + uint32_t level; + if (pulse_us < SERVO_MIN_US) + pulse_us = SERVO_MIN_US; + if (pulse_us > SERVO_MAX_US) + pulse_us = SERVO_MAX_US; + level = pulse_us_to_level(pulse_us); + PWM[SERVO_REG(PWM_CH_CC_OFFSET)] = level; +} + +void servo_set_angle(uint8_t degrees) +{ + uint16_t pulse; + if (degrees > 180) + degrees = 180; + pulse = (uint16_t)(SERVO_MIN_US + ((uint32_t)degrees * (SERVO_MAX_US - SERVO_MIN_US)) / 180U); + servo_set_pulse_us(pulse); +} diff --git a/drivers/0x05_servo_cbm/Src/rp2350_stack.c b/drivers/0x05_servo_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x05_servo_cbm/Src/rp2350_uart.c b/drivers/0x05_servo_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x05_servo_cbm/Src/rp2350_xosc.c b/drivers/0x05_servo_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..e4a58c2 --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/rp2350_xosc.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} diff --git a/drivers/0x05_servo_cbm/Src/vector_table.c b/drivers/0x05_servo_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x05_servo_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x05_servo_cbm/build.log b/drivers/0x05_servo_cbm/build.log new file mode 100644 index 0000000..e76adc0 Binary files /dev/null and b/drivers/0x05_servo_cbm/build.log differ diff --git a/drivers/0x05_servo_cbm/linker.ld b/drivers/0x05_servo_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x05_servo_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x05_servo_rust/.cargo/config.toml b/drivers/0x05_servo_rust/.cargo/config.toml new file mode 100644 index 0000000..1791d74 --- /dev/null +++ b/drivers/0x05_servo_rust/.cargo/config.toml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2021–2024 The rp-rs Developers +# Copyright (c) 2021 rp-rs organization +# Copyright (c) 2025 Raspberry Pi Ltd. +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# You might want to make a similar file in your own repository if you are +# writing programs for Raspberry Silicon microcontrollers. +# + +[build] +target = "thumbv8m.main-none-eabihf" +# Set the default target to match the Cortex-M33 in the RP2350 +# target = "thumbv8m.main-none-eabihf" +# target = "thumbv6m-none-eabi" +# target = "riscv32imac-unknown-none-elf" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the hard-float ABI for Arm mode. +# +# The FPU is enabled by default, and float function arguments use FPU +# registers. +[target.thumbv8m.main-none-eabihf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as a linker script. +# This is usually provided by the cortex-m-rt crate, and by default the +# version in that crate will include a file called `memory.x` which describes +# the particular memory layout for your specific chip. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the soft-float ABI for RISC-V mode. +# +# Hazard 3 does not have an FPU and so float function arguments use integer +# registers. +[target.riscv32imac-unknown-none-elf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Trp235x_riscv.x also tells the linker to use +# `rp235x_riscv.x` as a linker script. This adds in RP2350 RISC-V specific +# things that the riscv-rt crate's `link.x` requires and then includes +# `link.x` automatically. This is the reverse of how we do it on Cortex-M. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x05_servo_rust/.gitignore b/drivers/0x05_servo_rust/.gitignore new file mode 100644 index 0000000..03381c1 --- /dev/null +++ b/drivers/0x05_servo_rust/.gitignore @@ -0,0 +1,113 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,macos,windows,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux diff --git a/drivers/0x05_servo_rust/.pico-rs b/drivers/0x05_servo_rust/.pico-rs new file mode 100644 index 0000000..1b6702a --- /dev/null +++ b/drivers/0x05_servo_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 \ No newline at end of file diff --git a/drivers/0x05_servo_rust/.vscode/extensions.json b/drivers/0x05_servo_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x05_servo_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x05_servo_rust/.vscode/launch.json b/drivers/0x05_servo_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x05_servo_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x05_servo_rust/.vscode/settings.json b/drivers/0x05_servo_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x05_servo_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x05_servo_rust/.vscode/tasks.json b/drivers/0x05_servo_rust/.vscode/tasks.json new file mode 100644 index 0000000..4194af4 --- /dev/null +++ b/drivers/0x05_servo_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/drivers/0x05_servo_rust/Cargo.toml b/drivers/0x05_servo_rust/Cargo.toml new file mode 100644 index 0000000..8504505 --- /dev/null +++ b/drivers/0x05_servo_rust/Cargo.toml @@ -0,0 +1,40 @@ +[package] +edition = "2024" +name = "servo" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "servo_lib" +path = "src/lib.rs" + +[[bin]] +name = "servo" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +embedded-hal = "1.0.0" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x05_servo_rust/LICENSE-APACHE b/drivers/0x05_servo_rust/LICENSE-APACHE new file mode 100644 index 0000000..8d99cbc --- /dev/null +++ b/drivers/0x05_servo_rust/LICENSE-APACHE @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2021–2024 The rp-rs Developers + Copyright (c) 2021 rp-rs organization + Copyright (c) 2025 Raspberry Pi Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/drivers/0x05_servo_rust/LICENSE-MIT b/drivers/0x05_servo_rust/LICENSE-MIT new file mode 100644 index 0000000..5369c70 --- /dev/null +++ b/drivers/0x05_servo_rust/LICENSE-MIT @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2021–2024 The rp-rs Developers +Copyright (c) 2021 rp-rs organization +Copyright (c) 2025 Raspberry Pi Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + \ No newline at end of file diff --git a/drivers/0x05_servo_rust/build.rs b/drivers/0x05_servo_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x05_servo_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x05_servo_rust/rp2040.x b/drivers/0x05_servo_rust/rp2040.x new file mode 100644 index 0000000..0cc665b --- /dev/null +++ b/drivers/0x05_servo_rust/rp2040.x @@ -0,0 +1,91 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + /* + * Here we assume you have 2048 KiB of Flash. This is what the Pi Pico + * has, but your board may have more or less Flash and you should adjust + * this value to suit. + */ + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + /* + * RAM consists of 4 banks, SRAM0-SRAM3, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K + /* + * RAM banks 4 and 5 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k + + /* SRAM banks 0-3 can also be accessed directly. However, those ranges + alias with the RAM mapping, above. So don't use them at the same time! + SRAM0 : ORIGIN = 0x21000000, LENGTH = 64k + SRAM1 : ORIGIN = 0x21010000, LENGTH = 64k + SRAM2 : ORIGIN = 0x21020000, LENGTH = 64k + SRAM3 : ORIGIN = 0x21030000, LENGTH = 64k + */ +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader + * + * An executable block of code which sets up the QSPI interface for + * 'Execute-In-Place' (or XIP) mode. Also sends chip-specific commands to + * the external flash chip. + * + * Must go at the start of external flash, where the Boot ROM expects it. + */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 512 bytes of flash, + * where picotool can find it + */ + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x05_servo_rust/rp2350.x b/drivers/0x05_servo_rust/rp2350.x new file mode 100644 index 0000000..bda94d8 --- /dev/null +++ b/drivers/0x05_servo_rust/rp2350.x @@ -0,0 +1,83 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 4K of flash + * where the Boot ROM (and picotool) can find it + */ + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + /* move .text to start /after/ the boot info */ + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + /* ### Boot ROM extra info + * + * Goes after everything in our program, so it can contain a signature. + */ + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + \ No newline at end of file diff --git a/drivers/0x05_servo_rust/rp2350_riscv.x b/drivers/0x05_servo_rust/rp2350_riscv.x new file mode 100644 index 0000000..84388aa --- /dev/null +++ b/drivers/0x05_servo_rust/rp2350_riscv.x @@ -0,0 +1,259 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol is not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `32`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > FLASH + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + __start_block_addr = .; + KEEP(*(.start_block)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + . = ALIGN(4); + } > FLASH + + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > FLASH + + .data : ALIGN(32) + { + _sidata = LOADADDR(.data); + __sidata = LOADADDR(.data); + _sdata = .; + __sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(32); + _edata = .; + __edata = .; + } > RAM AT > FLASH + + .bss (NOLOAD) : ALIGN(32) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(32); + _ebss = .; + } > RAM + + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > RAM + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > RAM + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(start_to_end = __end_block_addr - __start_block_addr); +PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 32 == 0, " +ERROR(riscv-rt): the start of the RAM must be 32-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, " +BUG(riscv-rt): .data is not 32-byte aligned"); + +ASSERT(_sidata % 32 == 0, " +BUG(riscv-rt): the LMA of .data is not 32-byte aligned"); + +ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, " +BUG(riscv-rt): .bss is not 32-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(riscv-rt): The .text section must be placed inside the FLASH region. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/drivers/0x05_servo_rust/src/board.rs b/drivers/0x05_servo_rust/src/board.rs new file mode 100644 index 0000000..d964e6a --- /dev/null +++ b/drivers/0x05_servo_rust/src/board.rs @@ -0,0 +1,539 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// PWM duty-cycle trait for .set_duty_cycle() +use embedded_hal::pwm::SetDutyCycle; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Angle increment per sweep step in degrees. +pub(crate) const STEP_DEGREES: i32 = 10; + +/// Delay between sweep steps in milliseconds. +pub(crate) const STEP_DELAY_MS: u32 = 150; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Write 3-character right-justified angle digits into `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `a` - The `a` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `a` - The `a` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn write_angle_digits(buf: &mut [u8], a: u32) -> usize { + if a >= 100 { + write_angle_hundreds(buf, a); + } else if a >= 10 { + write_angle_tens(buf, a); + } else { + write_angle_ones(buf, a); + } + 3 +} + +/// Write digits for angles >= 100. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `a` - The `a` parameter. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `a` - The `a` parameter. +fn write_angle_hundreds(buf: &mut [u8], a: u32) { + buf[0] = b'0' + (a / 100) as u8; + buf[1] = b'0' + ((a / 10) % 10) as u8; + buf[2] = b'0' + (a % 10) as u8; +} + +/// Write digits for angles 10..99 with leading space. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `a` - The `a` parameter. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `a` - The `a` parameter. +fn write_angle_tens(buf: &mut [u8], a: u32) { + buf[0] = b' '; + buf[1] = b'0' + (a / 10) as u8; + buf[2] = b'0' + (a % 10) as u8; +} + +/// Write digit for angles 0..9 with leading spaces. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `a` - The `a` parameter. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `a` - The `a` parameter. +fn write_angle_ones(buf: &mut [u8], a: u32) { + buf[0] = b' '; + buf[1] = b' '; + buf[2] = b'0' + a as u8; +} + +/// Format an angle into "Angle: NNN deg\r\n". +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 20 bytes). +/// * `angle` - Angle in degrees (0..180). +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `angle` - The `angle` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub(crate) fn format_angle(buf: &mut [u8], angle: i32) -> usize { + buf[..7].copy_from_slice(b"Angle: "); + let mut pos = 7; + let a = if angle < 0 { 0 } else { angle as u32 }; + pos += write_angle_digits(&mut buf[pos..], a); + buf[pos..pos + 6].copy_from_slice(b" deg\r\n"); + pos + 6 +} + +/// Sweep the servo angle upward from 0 to 180 in STEP_DEGREES increments. +/// +/// # Arguments +/// +/// * `uart` - UART peripheral for serial output. +/// * `channel` - PWM channel implementing SetDutyCycle. +/// * `delay` - Delay provider for pause between steps. +/// * `buf` - Scratch buffer for formatting output. +pub(crate) fn sweep_angle_up( + uart: &EnabledUart, + channel: &mut impl SetDutyCycle, + delay: &mut cortex_m::delay::Delay, + buf: &mut [u8; 20], +) { + let mut angle: i32 = 0; + while angle <= 180 { + apply_angle(uart, channel, delay, buf, angle); + angle += STEP_DEGREES; + } +} + +/// Sweep the servo angle downward from 180 to 0 in STEP_DEGREES decrements. +/// +/// # Arguments +/// +/// * `uart` - UART peripheral for serial output. +/// * `channel` - PWM channel implementing SetDutyCycle. +/// * `delay` - Delay provider for pause between steps. +/// * `buf` - Scratch buffer for formatting output. +pub(crate) fn sweep_angle_down( + uart: &EnabledUart, + channel: &mut impl SetDutyCycle, + delay: &mut cortex_m::delay::Delay, + buf: &mut [u8; 20], +) { + let mut angle: i32 = 180; + while angle >= 0 { + apply_angle(uart, channel, delay, buf, angle); + angle -= STEP_DEGREES; + } +} + +/// Apply a single angle step: compute pulse, set PWM, format, print, delay. +fn apply_angle( + uart: &EnabledUart, + channel: &mut impl SetDutyCycle, + delay: &mut cortex_m::delay::Delay, + buf: &mut [u8; 20], + angle: i32, +) { + let level = compute_servo_level(angle) as u16; + channel.set_duty_cycle(level).ok(); + let n = format_angle(buf, angle); + uart.write_full_blocking(&buf[..n]); + delay.delay_ms(STEP_DELAY_MS); +} + +/// Compute the pulse width in microseconds for the given angle. +/// +/// # Arguments +/// +/// * `angle` - The `angle` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +/// +/// # Arguments +/// +/// * `angle` - The `angle` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +fn compute_pulse_us(angle: i32) -> u32 { + servo_lib::servo::angle_to_pulse_us( + angle as f32, + servo_lib::servo::SERVO_DEFAULT_MIN_US, + servo_lib::servo::SERVO_DEFAULT_MAX_US, + ) as u32 +} + +/// Compute the PWM level for a given angle using servo constants. +/// +/// # Arguments +/// +/// * `angle` - The `angle` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +/// +/// # Arguments +/// +/// * `angle` - The `angle` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +fn compute_servo_level(angle: i32) -> u32 { + servo_lib::servo::pulse_us_to_level( + compute_pulse_us(angle), + servo_lib::servo::SERVO_WRAP, + servo_lib::servo::SERVO_HZ, + ) +} + +/// Type alias for PWM slice 3 (servo on GPIO 6, channel A). +type PwmSlice3 = hal::pwm::Slice; + +/// Initialise all peripherals and run the servo sweep demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + let mut pwm = init_servo_pwm(pac.PWM, &mut pac.RESETS, &clocks, p.gpio6); + announce_servo(&uart); + servo_loop(&uart, &mut pwm, &mut delay) +} + +/// Configure PWM slice 3 for 50 Hz servo output on channel A (GPIO 6). +/// +/// # Arguments +/// +/// * `pwm_pac` - PAC PWM peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// * `servo_pin` - Default GPIO 6 pin to bind to PWM channel A. +/// +/// # Returns +/// +/// Configured PWM slice 3 in free-running mode. +fn init_servo_pwm( + pwm_pac: hal::pac::PWM, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, + servo_pin: Pin, +) -> PwmSlice3 { + let slices = hal::pwm::Slices::new(pwm_pac, resets); + let mut slice = slices.pwm3; + configure_servo_div(&mut slice, clocks); + slice.enable(); + slice.channel_a.output_to(servo_pin); + slice +} + +/// Set the clock divider and wrap for a servo PWM slice. +/// +/// # Arguments +/// +/// * `slice` - Mutable reference to the PWM slice to configure. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Arguments +/// +/// * `slice` - The `slice` parameter. +/// * `clocks` - The `clocks` parameter. +fn configure_servo_div(slice: &mut PwmSlice3, clocks: &hal::clocks::ClocksManager) { + let sys_hz = clocks.system_clock.freq().to_Hz(); + let div = servo_lib::servo::calc_clk_div( + sys_hz, + servo_lib::servo::SERVO_HZ, + servo_lib::servo::SERVO_WRAP, + ); + let div_int = div as u8; + slice.set_div_int(div_int); + slice.set_div_frac((((div - div_int as f32) * 16.0) as u8).min(15)); + slice.set_top(servo_lib::servo::SERVO_WRAP as u16); +} + +/// Print the servo initialisation banner over UART. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +fn announce_servo(uart: &EnabledUart) { + uart.write_full_blocking(b"Servo driver initialized on GPIO 6\r\n"); + uart.write_full_blocking(b"Sweeping 0 -> 180 -> 0 degrees in 10-degree steps\r\n"); +} + +/// Run the servo angle sweep loop forever. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `pwm` - Mutable reference to the configured PWM slice. +/// * `delay` - Mutable reference to the blocking delay provider. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `pwm` - The `pwm` parameter. +/// * `delay` - Delay value. +/// +/// # Returns +/// +/// A value of type `!`. +fn servo_loop(uart: &EnabledUart, pwm: &mut PwmSlice3, delay: &mut cortex_m::delay::Delay) -> ! { + let mut buf = [0u8; 20]; + loop { + sweep_angle_up(uart, &mut pwm.channel_a, delay, &mut buf); + sweep_angle_down(uart, &mut pwm.channel_a, delay, &mut buf); + } +} + diff --git a/drivers/0x05_servo_rust/src/lib.rs b/drivers/0x05_servo_rust/src/lib.rs new file mode 100644 index 0000000..75de91f --- /dev/null +++ b/drivers/0x05_servo_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod servo; diff --git a/drivers/0x05_servo_rust/src/main.rs b/drivers/0x05_servo_rust/src/main.rs new file mode 100644 index 0000000..0416852 --- /dev/null +++ b/drivers/0x05_servo_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// Servo driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the servo sweep demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"SG90 Servo Sweep Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x05_servo_rust/src/servo.rs b/drivers/0x05_servo_rust/src/servo.rs new file mode 100644 index 0000000..116e337 --- /dev/null +++ b/drivers/0x05_servo_rust/src/servo.rs @@ -0,0 +1,288 @@ +//! Implementation module +//! +//! **File:** `servo.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// Default minimum pulse width in microseconds (0 degrees). +pub const SERVO_DEFAULT_MIN_US: u16 = 1000; + +/// Default maximum pulse width in microseconds (180 degrees). +pub const SERVO_DEFAULT_MAX_US: u16 = 2000; + +/// Default PWM wrap value for 50 Hz servo (20 000 - 1). +pub const SERVO_WRAP: u32 = 20000 - 1; + +/// Default servo frequency in Hz. +pub const SERVO_HZ: f32 = 50.0; + +/// Convert a pulse width in microseconds to a PWM counter level. +/// +/// Uses the configured PWM wrap and servo frequency to map pulse time +/// into the channel compare value expected by the PWM hardware. +/// +/// # Arguments +/// +/// * `pulse_us` - Pulse width in microseconds. +/// * `wrap` - PWM counter wrap value. +/// * `hz` - PWM frequency in Hz. +/// +/// # Returns +/// +/// PWM level suitable for the channel compare register. +/// +/// # Arguments +/// +/// * `pulse_us` - The `pulse_us` parameter. +/// * `wrap` - The `wrap` parameter. +/// * `hz` - The `hz` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +pub fn pulse_us_to_level(pulse_us: u32, wrap: u32, hz: f32) -> u32 { + let period_us = 1_000_000.0f32 / hz; + let counts_per_us = (wrap + 1) as f32 / period_us; + (pulse_us as f32 * counts_per_us + 0.5f32) as u32 +} + +/// Clamp a pulse width to the valid servo range. +/// +/// Values below min_us are raised to min_us; values above max_us are +/// lowered to max_us. +/// +/// # Arguments +/// +/// * `pulse_us` - Raw pulse width in microseconds. +/// * `min_us` - Minimum allowed pulse width. +/// * `max_us` - Maximum allowed pulse width. +/// +/// # Returns +/// +/// Clamped pulse width. +/// +/// # Arguments +/// +/// * `pulse_us` - The `pulse_us` parameter. +/// * `min_us` - The `min_us` parameter. +/// * `max_us` - The `max_us` parameter. +/// +/// # Returns +/// +/// A 16-bit unsigned integer value. +pub fn clamp_pulse_us(pulse_us: u16, min_us: u16, max_us: u16) -> u16 { + if pulse_us < min_us { + min_us + } else if pulse_us > max_us { + max_us + } else { + pulse_us + } +} + +/// Clamp a floating-point angle to the valid servo range [0.0, 180.0]. +/// +/// # Arguments +/// +/// * `degrees` - The `degrees` parameter. +/// +/// # Returns +/// +/// A value of type `f32`. +/// +/// # Arguments +/// +/// * `degrees` - The `degrees` parameter. +/// +/// # Returns +/// +/// A value of type `f32`. +fn clamp_degrees(degrees: f32) -> f32 { + if degrees < 0.0f32 { + 0.0f32 + } else if degrees > 180.0f32 { + 180.0f32 + } else { + degrees + } +} + +/// Map a servo angle in degrees to a pulse width in microseconds. +/// +/// Clamps degrees to [0, 180], then linearly maps to the pulse range. +/// +/// # Arguments +/// +/// * `degrees` - Angle in degrees (0.0 to 180.0). +/// * `min_us` - Pulse width at 0 degrees. +/// * `max_us` - Pulse width at 180 degrees. +/// +/// # Returns +/// +/// Pulse width in microseconds corresponding to the given angle. +/// +/// # Arguments +/// +/// * `degrees` - The `degrees` parameter. +/// * `min_us` - The `min_us` parameter. +/// * `max_us` - The `max_us` parameter. +/// +/// # Returns +/// +/// A 16-bit unsigned integer value. +pub fn angle_to_pulse_us(degrees: f32, min_us: u16, max_us: u16) -> u16 { + let d = clamp_degrees(degrees); + let ratio = d / 180.0f32; + let span = (max_us - min_us) as f32; + (min_us as f32 + ratio * span + 0.5f32) as u16 +} + +/// Compute the PWM clock divider for the servo frequency. +/// +/// # Arguments +/// +/// * `sys_hz` - System clock frequency in Hz. +/// * `servo_hz` - Desired servo PWM frequency in Hz. +/// * `wrap` - PWM counter wrap value. +/// +/// # Returns +/// +/// Clock divider value. +/// +/// # Arguments +/// +/// * `sys_hz` - The `sys_hz` parameter. +/// * `servo_hz` - The `servo_hz` parameter. +/// * `wrap` - The `wrap` parameter. +/// +/// # Returns +/// +/// A value of type `f32`. +pub fn calc_clk_div(sys_hz: u32, servo_hz: f32, wrap: u32) -> f32 { + sys_hz as f32 / (servo_hz * (wrap + 1) as f32) +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the pulse us to level 1000us operation. + #[test] + fn pulse_us_to_level_1000us() { + let level = pulse_us_to_level(1000, SERVO_WRAP, SERVO_HZ); + assert_eq!(level, 1000); + } + + /// Executes the pulse us to level 2000us operation. + #[test] + fn pulse_us_to_level_2000us() { + let level = pulse_us_to_level(2000, SERVO_WRAP, SERVO_HZ); + assert_eq!(level, 2000); + } + + /// Executes the pulse us to level 1500us operation. + #[test] + fn pulse_us_to_level_1500us() { + let level = pulse_us_to_level(1500, SERVO_WRAP, SERVO_HZ); + assert_eq!(level, 1500); + } + + /// Executes the pulse us to level zero operation. + #[test] + fn pulse_us_to_level_zero() { + let level = pulse_us_to_level(0, SERVO_WRAP, SERVO_HZ); + assert_eq!(level, 0); + } + + /// Executes the clamp pulse us below min operation. + #[test] + fn clamp_pulse_us_below_min() { + assert_eq!( + clamp_pulse_us(500, SERVO_DEFAULT_MIN_US, SERVO_DEFAULT_MAX_US), + 1000 + ); + } + + /// Executes the clamp pulse us above max operation. + #[test] + fn clamp_pulse_us_above_max() { + assert_eq!( + clamp_pulse_us(3000, SERVO_DEFAULT_MIN_US, SERVO_DEFAULT_MAX_US), + 2000 + ); + } + + /// Executes the clamp pulse us within range operation. + #[test] + fn clamp_pulse_us_within_range() { + assert_eq!( + clamp_pulse_us(1500, SERVO_DEFAULT_MIN_US, SERVO_DEFAULT_MAX_US), + 1500 + ); + } + + /// Executes the angle to pulse us zero operation. + #[test] + fn angle_to_pulse_us_zero() { + let pulse = angle_to_pulse_us(0.0, SERVO_DEFAULT_MIN_US, SERVO_DEFAULT_MAX_US); + assert_eq!(pulse, 1000); + } + + /// Executes the angle to pulse us 180 operation. + #[test] + fn angle_to_pulse_us_180() { + let pulse = angle_to_pulse_us(180.0, SERVO_DEFAULT_MIN_US, SERVO_DEFAULT_MAX_US); + assert_eq!(pulse, 2000); + } + + /// Executes the angle to pulse us 90 operation. + #[test] + fn angle_to_pulse_us_90() { + let pulse = angle_to_pulse_us(90.0, SERVO_DEFAULT_MIN_US, SERVO_DEFAULT_MAX_US); + assert_eq!(pulse, 1500); + } + + /// Executes the angle to pulse us clamped negative operation. + #[test] + fn angle_to_pulse_us_clamped_negative() { + let pulse = angle_to_pulse_us(-10.0, SERVO_DEFAULT_MIN_US, SERVO_DEFAULT_MAX_US); + assert_eq!(pulse, 1000); + } + + /// Executes the angle to pulse us clamped above operation. + #[test] + fn angle_to_pulse_us_clamped_above() { + let pulse = angle_to_pulse_us(200.0, SERVO_DEFAULT_MIN_US, SERVO_DEFAULT_MAX_US); + assert_eq!(pulse, 2000); + } + + /// Executes the calc clk div 150mhz operation. + #[test] + fn calc_clk_div_150mhz() { + let div = calc_clk_div(150_000_000, SERVO_HZ, SERVO_WRAP); + assert!((div - 150.0).abs() < 0.01); + } +} diff --git a/drivers/0x06_adc/.vscode/.vscode/c_cpp_properties.json b/drivers/0x06_adc/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x06_adc/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x06_adc/.vscode/.vscode/cmake-kits.json b/drivers/0x06_adc/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x06_adc/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x06_adc/.vscode/.vscode/extensions.json b/drivers/0x06_adc/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x06_adc/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x06_adc/.vscode/.vscode/launch.json b/drivers/0x06_adc/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x06_adc/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x06_adc/.vscode/.vscode/settings.json b/drivers/0x06_adc/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x06_adc/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x06_adc/.vscode/.vscode/tasks.json b/drivers/0x06_adc/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x06_adc/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x06_adc/.vscode/c_cpp_properties.json b/drivers/0x06_adc/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x06_adc/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x06_adc/.vscode/cmake-kits.json b/drivers/0x06_adc/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x06_adc/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x06_adc/.vscode/extensions.json b/drivers/0x06_adc/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x06_adc/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x06_adc/.vscode/launch.json b/drivers/0x06_adc/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x06_adc/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x06_adc/.vscode/settings.json b/drivers/0x06_adc/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x06_adc/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x06_adc/.vscode/tasks.json b/drivers/0x06_adc/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x06_adc/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x06_adc/0x06_adc.c b/drivers/0x06_adc/0x06_adc.c new file mode 100644 index 0000000..7dc2bb8 --- /dev/null +++ b/drivers/0x06_adc/0x06_adc.c @@ -0,0 +1,70 @@ +/** + * @file 0x06_adc.c + * @brief ADC demonstration: potentiometer voltage + on-chip temperature + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates 12-bit ADC using the adc driver (adc.h / adc.c). Reads + * ADC channel 0 (GPIO 26) and reports the voltage in millivolts alongside + * the on-chip temperature sensor reading every 500 ms over UART. + * + * Wiring: + * GPIO26 -> Wiper of a 10 kohm potentiometer + * 3.3V -> One end of the potentiometer + * GND -> Other end of the potentiometer + */ + +#include +#include "pico/stdlib.h" +#include "adc.h" + +/** @brief GPIO pin for the analog input */ +#define ADC_GPIO 26 +/** @brief ADC channel corresponding to ADC_GPIO */ +#define ADC_CHANNEL 0 + +/** + * @brief Read and print ADC voltage and chip temperature over UART + * + * Samples the ADC channel for voltage in millivolts and reads the + * on-chip temperature sensor, then prints both values. + */ +static void print_adc_readings(void) { + uint32_t voltage_mv = adc_driver_read_mv(); + float temp_c = adc_driver_read_temp_celsius(); + printf("ADC0: %4lu mV | Chip temp: %.1f C\r\n", voltage_mv, temp_c); +} + +int main(void) { + stdio_init_all(); + adc_driver_init(ADC_GPIO, ADC_CHANNEL); + printf("ADC driver initialized: GPIO%d (channel %d)\r\n", ADC_GPIO, ADC_CHANNEL); + while (true) { + print_adc_readings(); + sleep_ms(500); + } +} diff --git a/drivers/0x06_adc/CMakeLists.txt b/drivers/0x06_adc/CMakeLists.txt new file mode 100644 index 0000000..72fe0a2 --- /dev/null +++ b/drivers/0x06_adc/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x06_adc C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x06_adc 0x06_adc.c adc.c) + +pico_set_program_name(0x06_adc "0x06_adc") +pico_set_program_version(0x06_adc "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x06_adc 1) +pico_enable_stdio_usb(0x06_adc 0) + +# Add the standard library to the build +target_link_libraries(0x06_adc + pico_stdlib + hardware_adc) + +# Add the standard include files to the build +target_include_directories(0x06_adc PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x06_adc) diff --git a/drivers/0x06_adc/adc.c b/drivers/0x06_adc/adc.c new file mode 100644 index 0000000..d6f9d9a --- /dev/null +++ b/drivers/0x06_adc/adc.c @@ -0,0 +1,85 @@ +/** + * @file adc.c + * @brief Implementation of the 12-bit ADC driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "adc.h" +#include "pico/stdlib.h" +#include "hardware/adc.h" + +/** @brief ADC reference voltage in millivolts */ +#define ADC_VREF_MV 3300 +/** @brief Maximum 12-bit ADC raw value */ +#define ADC_FULL_SCALE 4095 + +/** @brief Currently selected ADC input channel */ +static uint8_t active_channel = 0; + +/** + * @brief Convert a raw 12-bit ADC value to millivolts + * + * Scales the raw value linearly against the 3.3 V reference. + * + * @param raw 12-bit ADC conversion result (0 - 4095) + * @return uint32_t Equivalent voltage in millivolts (0 - 3300) + */ +static uint32_t raw_to_mv(uint16_t raw) { + return (uint32_t)raw * ADC_VREF_MV / ADC_FULL_SCALE; +} + +/** + * @brief Convert a raw temperature-sensor ADC value to degrees Celsius + * + * Applies the RP2350 datasheet formula: + * T = 27 - (V - 0.706) / 0.001721 + * + * @param raw 12-bit ADC result from the internal temperature sensor (channel 4) + * @return float Die temperature in degrees Celsius + */ +static float raw_to_celsius(uint16_t raw) { + float voltage = (float)raw * 3.3f / (float)ADC_FULL_SCALE; + return 27.0f - (voltage - 0.706f) / 0.001721f; +} + +void adc_driver_init(uint32_t gpio, uint8_t channel) { + active_channel = channel; + adc_init(); + adc_gpio_init(gpio); + adc_set_temp_sensor_enabled(true); + adc_select_input(channel); +} + +uint32_t adc_driver_read_mv(void) { + return raw_to_mv(adc_read()); +} + +float adc_driver_read_temp_celsius(void) { + adc_select_input(4); + float result = raw_to_celsius(adc_read()); + adc_select_input(active_channel); + return result; +} diff --git a/drivers/0x06_adc/adc.h b/drivers/0x06_adc/adc.h new file mode 100644 index 0000000..b607551 --- /dev/null +++ b/drivers/0x06_adc/adc.h @@ -0,0 +1,69 @@ +/** + * @file adc.h + * @brief Header for 12-bit ADC driver (analog pin + internal temperature sensor) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ADC_H +#define ADC_H + +#include + +/** + * @brief Initialize the ADC peripheral and configure an analog GPIO pin + * + * Powers on the ADC block, configures the specified GPIO as an analog input, + * enables the internal temperature sensor on channel 4, and selects the + * given channel as the active input. Must be called once before using any + * other adc_driver_* functions. + * + * @param gpio GPIO pin number for the analog input (26 = ch0, 27 = ch1, 28 = ch2) + * @param channel ADC channel number corresponding to the GPIO (0, 1, or 2) + */ +void adc_driver_init(uint32_t gpio, uint8_t channel); + +/** + * @brief Perform a single ADC conversion on the active channel and return millivolts + * + * Reads the 12-bit raw value from the currently selected channel and scales + * it to millivolts using the 3.3 V (3300 mV) full-scale reference. + * + * @return uint32_t Measured voltage in millivolts (0 to 3300) + */ +uint32_t adc_driver_read_mv(void); + +/** + * @brief Read the on-chip temperature sensor and return degrees Celsius + * + * Temporarily switches to ADC channel 4 (internal temperature sensor), + * performs a conversion, applies the formula from the RP2350 datasheet, + * then restores the channel that was active before the call. + * + * @return float Die temperature in degrees Celsius + */ +float adc_driver_read_temp_celsius(void); + +#endif // ADC_H diff --git a/drivers/0x06_adc/pico_sdk_import.cmake b/drivers/0x06_adc/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x06_adc/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x06_adc_cbm/.vscode/c_cpp_properties.json b/drivers/0x06_adc_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x06_adc_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x06_adc_cbm/.vscode/extensions.json b/drivers/0x06_adc_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x06_adc_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x06_adc_cbm/.vscode/launch.json b/drivers/0x06_adc_cbm/.vscode/launch.json new file mode 100644 index 0000000..0c300c1 --- /dev/null +++ b/drivers/0x06_adc_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/adc.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDir": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDir": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x06_adc_cbm/.vscode/settings.json b/drivers/0x06_adc_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x06_adc_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x06_adc_cbm/.vscode/tasks.json b/drivers/0x06_adc_cbm/.vscode/tasks.json new file mode 100644 index 0000000..0b2c8ce --- /dev/null +++ b/drivers/0x06_adc_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/adc.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/adc.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x06_adc_cbm/Inc/rp2350.h b/drivers/0x06_adc_cbm/Inc/rp2350.h new file mode 100644 index 0000000..bb5fdc2 --- /dev/null +++ b/drivers/0x06_adc_cbm/Inc/rp2350.h @@ -0,0 +1,242 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define ADC_BASE 0x400A0000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[18]; // Other clock registers Address offset: 0x00-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 + __IO uint32_t RESERVED1[8]; // CLK_PERI_DIV..CLK_USB_SELECTED Address offset: 0x4C-0x68 + __IO uint32_t CLK_ADC_CTRL; // ADC clock control Address offset: 0x6C +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief ADC (Analog-to-Digital Converter) + */ +typedef struct +{ + __IO uint32_t CS; // Control and status Address offset: 0x00 + __IO uint32_t RESULT; // Conversion result Address offset: 0x04 +} ADC_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define ADC ((ADC_TypeDef *) ADC_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_MASK (0x07U << CLK_PERI_CTRL_AUXSRC_SHIFT) +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief CLK_ADC bit definitions + */ +#define CLK_ADC_CTRL_ENABLE_SHIFT 11U +#define CLK_ADC_CTRL_AUXSRC_SHIFT 5U +#define CLK_ADC_CTRL_AUXSRC_MASK (0x07U << CLK_ADC_CTRL_AUXSRC_SHIFT) +#define CLK_ADC_CTRL_AUXSRC_XOSC 3U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_ADC_SHIFT 0U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U +#define IO_BANK0_CTRL_FUNCSEL_NULL 0x1FU + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief GPIO pin definitions + */ +#define ADC_PIN 26U +#define ADC_CHANNEL 0U +#define ADC_TEMP_CHANNEL 4U + +/** + * @brief PADS_BANK0 drive strength value + */ +#define PADS_BANK0_DRIVE_4MA (1U << 4) + +/** + * @brief ADC CS register bit definitions + */ +#define ADC_CS_EN_SHIFT 0U +#define ADC_CS_TS_EN_SHIFT 1U +#define ADC_CS_START_ONCE_SHIFT 2U +#define ADC_CS_READY_SHIFT 8U +#define ADC_CS_AINSEL_SHIFT 12U +#define ADC_CS_AINSEL_MASK (0x0FU << ADC_CS_AINSEL_SHIFT) + +/** + * @brief ADC conversion constants + */ +#define ADC_VREF_MV 3300U +#define ADC_FULL_SCALE 4095U +#define ADC_READY_TIMEOUT 1000000U + +#endif /* __RP2350_H */ diff --git a/drivers/0x06_adc_cbm/Inc/rp2350_adc.h b/drivers/0x06_adc_cbm/Inc/rp2350_adc.h new file mode 100644 index 0000000..4752831 --- /dev/null +++ b/drivers/0x06_adc_cbm/Inc/rp2350_adc.h @@ -0,0 +1,84 @@ +/** + * @file rp2350_adc.h + * @brief Header for RP2350 12-bit ADC driver. + * @author Kevin Thomas + * @date 2026 + * + * Provides functions to initialise the ADC peripheral, read an + * analog voltage in millivolts from GPIO26 (channel 0), and read + * the on-chip temperature sensor (channel 4) in tenths of degrees + * Celsius. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_ADC_H +#define __RP2350_ADC_H + +#include "rp2350.h" + +/** + * @brief Release the ADC subsystem from reset. + * + * Clears the ADC bit in the RESETS register and waits until + * RESET_DONE confirms the subsystem is running. + * + * @retval None + */ +void adc_release_reset(void); + +/** + * @brief Initialise the ADC peripheral for GPIO26 (channel 0). + * + * Configures GPIO26 pad for analog input (disables digital I/O, + * pulls, and pad isolation), sets the IO mux function to NULL, + * powers on the ADC, enables the temperature sensor, and selects + * channel 0 as the default input. + * + * @retval None + */ +void adc_init(void); + +/** + * @brief Perform a single ADC conversion and return millivolts. + * + * Triggers a one-shot conversion on the currently selected channel, + * waits for the result, and scales the 12-bit value against the + * 3.3 V reference. + * + * @retval uint32_t measured voltage in millivolts (0-3300) + */ +uint32_t adc_read_mv(void); + +/** + * @brief Read the on-chip temperature sensor in tenths of degrees Celsius. + * + * Temporarily switches to ADC channel 4 (temperature sensor), + * performs a conversion, applies the RP2350 datasheet formula + * T = 27 - (V - 0.706) / 0.001721 using integer arithmetic, + * and restores the previously active channel. + * + * @retval int32_t die temperature in tenths of degrees (e.g. 270 = 27.0 C) + */ +int32_t adc_read_temp_tenths(void); + +#endif /* __RP2350_ADC_H */ diff --git a/drivers/0x06_adc_cbm/Inc/rp2350_delay.h b/drivers/0x06_adc_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..6860cf7 --- /dev/null +++ b/drivers/0x06_adc_cbm/Inc/rp2350_delay.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond busy-wait delay calibrated for a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x06_adc_cbm/Inc/rp2350_reset.h b/drivers/0x06_adc_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x06_adc_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x06_adc_cbm/Inc/rp2350_reset_handler.h b/drivers/0x06_adc_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x06_adc_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x06_adc_cbm/Inc/rp2350_stack.h b/drivers/0x06_adc_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x06_adc_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x06_adc_cbm/Inc/rp2350_uart.h b/drivers/0x06_adc_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x06_adc_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x06_adc_cbm/Inc/rp2350_xosc.h b/drivers/0x06_adc_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..7dc643e --- /dev/null +++ b/drivers/0x06_adc_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,55 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +/** + * @brief Enable the XOSC ADC clock via CLK_ADC_CTRL. + * @retval None + */ +void xosc_enable_adc_clk(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x06_adc_cbm/Makefile b/drivers/0x06_adc_cbm/Makefile new file mode 100644 index 0000000..bd61056 --- /dev/null +++ b/drivers/0x06_adc_cbm/Makefile @@ -0,0 +1,85 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C ADC driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = adc + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_adc.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x06_adc_cbm/Src/image_def.c b/drivers/0x06_adc_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x06_adc_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x06_adc_cbm/Src/main.c b/drivers/0x06_adc_cbm/Src/main.c new file mode 100644 index 0000000..32ad0de --- /dev/null +++ b/drivers/0x06_adc_cbm/Src/main.c @@ -0,0 +1,119 @@ +/** + * @file main.c + * @brief ADC demonstration: potentiometer voltage and chip temperature. + * @author Kevin Thomas + * @date 2026 + * + * Demonstrates 12-bit ADC using the bare-metal ADC driver. Reads + * ADC channel 0 (GPIO26) and reports the voltage in millivolts + * alongside the on-chip temperature sensor reading every 500 ms + * over UART. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * GPIO26 -> Wiper of a 10 kohm potentiometer + * 3.3V -> One end of the potentiometer + * GND -> Other end of the potentiometer + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_adc.h" +#include "rp2350_uart.h" +#include "rp2350_xosc.h" +#include "rp2350_delay.h" + +/** @brief Delay between ADC samples in milliseconds */ +#define SAMPLE_DELAY_MS 500 + +/** + * @brief Reverse a character buffer in place. + * @param buf pointer to the buffer + * @param len number of characters to reverse + * @retval None + */ +static void reverse(char *buf, uint8_t len) +{ + for (uint8_t i = 0; i < len / 2; i++) + { + char tmp = buf[i]; + buf[i] = buf[len - 1 - i]; + buf[len - 1 - i] = tmp; + } +} + +/** + * @brief Print an unsigned 32-bit integer as a decimal string over UART. + * @param val value to print + * @retval None + */ +static void print_uint32(uint32_t val) +{ + char buf[11]; + uint8_t len = 0; + do { buf[len++] = (char)('0' + val % 10); val /= 10; } while (val > 0); + reverse(buf, len); + buf[len] = '\0'; + uart_puts(buf); +} + +/** + * @brief Print a signed temperature in tenths of degrees as XX.X over UART. + * @param tenths temperature in tenths of degrees Celsius + * @retval None + */ +static void print_temp(int32_t tenths) +{ + if (tenths < 0) { uart_puts("-"); tenths = -tenths; } + print_uint32((uint32_t)(tenths / 10)); + char frac[2] = { (char)('0' + tenths % 10), '\0' }; + uart_puts("."); + uart_puts(frac); +} + +/** + * @brief Print ADC voltage and chip temperature readings over UART. + * @retval None + */ +static void print_readings(void) +{ + uint32_t mv = adc_read_mv(); + int32_t temp = adc_read_temp_tenths(); + uart_puts("ADC0: "); + print_uint32(mv); + uart_puts(" mV | Chip temp: "); + print_temp(temp); + uart_puts(" C\r\n"); +} + +int main(void) +{ + xosc_enable_adc_clk(); + adc_release_reset(); + adc_init(); + while (1) + { + print_readings(); + delay_ms(SAMPLE_DELAY_MS); + } +} diff --git a/drivers/0x06_adc_cbm/Src/rp2350_adc.c b/drivers/0x06_adc_cbm/Src/rp2350_adc.c new file mode 100644 index 0000000..94087a1 --- /dev/null +++ b/drivers/0x06_adc_cbm/Src/rp2350_adc.c @@ -0,0 +1,179 @@ +/** + * @file rp2350_adc.c + * @brief RP2350 12-bit ADC driver implementation. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal driver for the RP2350 ADC peripheral. Reads analog + * voltage on GPIO26 (channel 0) and the on-chip temperature sensor + * (channel 4). All register accesses verified against the RP2350 + * datasheet (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_adc.h" + +static uint8_t active_channel = 0; + +/** + * @brief Configure the GPIO26 pad for analog input. + * + * Sets drive strength to 4 mA and clears pad isolation, output + * disable, input enable, pull-up, pull-down, and schmitt trigger + * bits so the pad is fully analog. + * + * @retval None + */ +static void adc_config_pad(void) +{ + PADS_BANK0->GPIO[ADC_PIN] = PADS_BANK0_DRIVE_4MA; +} + +/** + * @brief Set GPIO26 IO mux function to NULL (disconnected). + * + * Writes FUNCSEL = 0x1F to the IO_BANK0 control register for + * GPIO26, ensuring no digital peripheral drives the pin. + * + * @retval None + */ +static void adc_config_gpio(void) +{ + IO_BANK0->GPIO[ADC_PIN].CTRL = IO_BANK0_CTRL_FUNCSEL_NULL; +} + +/** + * @brief Select an ADC input channel. + * + * Writes the channel number into the AINSEL field of the CS + * register while preserving all other bits. + * + * @param ch channel number (0-4) + * @retval None + */ +static void adc_select_input(uint8_t ch) +{ + uint32_t cs = ADC->CS; + cs &= ~ADC_CS_AINSEL_MASK; + cs |= ((uint32_t)ch << ADC_CS_AINSEL_SHIFT); + ADC->CS = cs; +} + +/** + * @brief Trigger a single conversion and return the raw 12-bit result. + * + * Sets START_ONCE in the CS register, spins until READY is + * asserted, then reads the 12-bit value from the RESULT register. + * + * @retval uint16_t raw ADC conversion result (0-4095) + */ +static uint16_t adc_read_raw(void) +{ + uint32_t timeout = ADC_READY_TIMEOUT; + ADC->CS |= (1U << ADC_CS_START_ONCE_SHIFT); + while (!(ADC->CS & (1U << ADC_CS_READY_SHIFT)) && timeout > 0U) + { + timeout--; + } + if (timeout == 0U) + { + return 0U; + } + return (uint16_t)(ADC->RESULT & 0xFFFU); +} + +/** + * @brief Convert a raw 12-bit ADC value to millivolts. + * + * Scales the raw value linearly against the 3.3 V (3300 mV) + * full-scale reference. + * + * @param raw 12-bit ADC conversion result (0-4095) + * @retval uint32_t equivalent voltage in millivolts (0-3300) + */ +static uint32_t raw_to_mv(uint16_t raw) +{ + return (uint32_t)raw * ADC_VREF_MV / ADC_FULL_SCALE; +} + +/** + * @brief Convert a raw temperature-sensor ADC value to tenths of degrees. + * + * Applies the RP2350 datasheet formula T = 27 - (V - 0.706) / 0.001721 + * using integer arithmetic with millivolt-times-ten precision. + * V is computed as raw * 33000 / 4095 (voltage in 0.1 mV units). + * + * @param raw 12-bit ADC result from the internal temperature sensor + * @retval int32_t die temperature in tenths of degrees Celsius + */ +static int32_t raw_to_temp_tenths(uint16_t raw) +{ + int32_t v_mv10 = (int32_t)((uint32_t)raw * 33000U / ADC_FULL_SCALE); + return 270 - (v_mv10 - 7060) * 1000 / 1721; +} + +void adc_release_reset(void) +{ + RESETS->RESET |= (1U << RESETS_RESET_ADC_SHIFT); + uint32_t timeout = ADC_READY_TIMEOUT; + RESETS->RESET &= ~(1U << RESETS_RESET_ADC_SHIFT); + while (!(RESETS->RESET_DONE & (1U << RESETS_RESET_ADC_SHIFT)) && timeout > 0U) + { + timeout--; + } +} + +/** + * @brief Enable the ADC block and wait until it is ready. + * @retval None + */ +static void adc_enable(void) +{ + ADC->CS = (1U << ADC_CS_EN_SHIFT); + uint32_t timeout = ADC_READY_TIMEOUT; + while (!(ADC->CS & (1U << ADC_CS_READY_SHIFT)) && timeout > 0U) + timeout--; + ADC->CS |= (1U << ADC_CS_TS_EN_SHIFT); +} + +void adc_init(void) +{ + adc_config_pad(); + adc_config_gpio(); + adc_enable(); + active_channel = ADC_CHANNEL; + adc_select_input(active_channel); +} + +uint32_t adc_read_mv(void) +{ + return raw_to_mv(adc_read_raw()); +} + +int32_t adc_read_temp_tenths(void) +{ + adc_select_input(ADC_TEMP_CHANNEL); + int32_t result = raw_to_temp_tenths(adc_read_raw()); + adc_select_input(active_channel); + return result; +} diff --git a/drivers/0x06_adc_cbm/Src/rp2350_delay.c b/drivers/0x06_adc_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..70d7f16 --- /dev/null +++ b/drivers/0x06_adc_cbm/Src/rp2350_delay.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond delay calibrated for a 12 MHz clock + * (3600 loop iterations per millisecond). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x06_adc_cbm/Src/rp2350_reset.c b/drivers/0x06_adc_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x06_adc_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x06_adc_cbm/Src/rp2350_reset_handler.c b/drivers/0x06_adc_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..f831947 --- /dev/null +++ b/drivers/0x06_adc_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,81 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, ADC, then + * branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" +#include "rp2350_adc.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x06_adc_cbm/Src/rp2350_stack.c b/drivers/0x06_adc_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x06_adc_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x06_adc_cbm/Src/rp2350_uart.c b/drivers/0x06_adc_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x06_adc_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x06_adc_cbm/Src/rp2350_xosc.c b/drivers/0x06_adc_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..b100a45 --- /dev/null +++ b/drivers/0x06_adc_cbm/Src/rp2350_xosc.c @@ -0,0 +1,59 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value &= ~CLK_PERI_CTRL_AUXSRC_MASK; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} + +void xosc_enable_adc_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_ADC_CTRL; + value &= ~CLK_ADC_CTRL_AUXSRC_MASK; + value |= (1U << CLK_ADC_CTRL_ENABLE_SHIFT); + value |= (CLK_ADC_CTRL_AUXSRC_XOSC << CLK_ADC_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_ADC_CTRL = value; +} diff --git a/drivers/0x06_adc_cbm/Src/vector_table.c b/drivers/0x06_adc_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x06_adc_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x06_adc_cbm/build.log b/drivers/0x06_adc_cbm/build.log new file mode 100644 index 0000000..9b93096 Binary files /dev/null and b/drivers/0x06_adc_cbm/build.log differ diff --git a/drivers/0x06_adc_cbm/linker.ld b/drivers/0x06_adc_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x06_adc_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x06_adc_rust/.cargo/config.toml b/drivers/0x06_adc_rust/.cargo/config.toml new file mode 100644 index 0000000..1791d74 --- /dev/null +++ b/drivers/0x06_adc_rust/.cargo/config.toml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2021–2024 The rp-rs Developers +# Copyright (c) 2021 rp-rs organization +# Copyright (c) 2025 Raspberry Pi Ltd. +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# You might want to make a similar file in your own repository if you are +# writing programs for Raspberry Silicon microcontrollers. +# + +[build] +target = "thumbv8m.main-none-eabihf" +# Set the default target to match the Cortex-M33 in the RP2350 +# target = "thumbv8m.main-none-eabihf" +# target = "thumbv6m-none-eabi" +# target = "riscv32imac-unknown-none-elf" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the hard-float ABI for Arm mode. +# +# The FPU is enabled by default, and float function arguments use FPU +# registers. +[target.thumbv8m.main-none-eabihf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as a linker script. +# This is usually provided by the cortex-m-rt crate, and by default the +# version in that crate will include a file called `memory.x` which describes +# the particular memory layout for your specific chip. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the soft-float ABI for RISC-V mode. +# +# Hazard 3 does not have an FPU and so float function arguments use integer +# registers. +[target.riscv32imac-unknown-none-elf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Trp235x_riscv.x also tells the linker to use +# `rp235x_riscv.x` as a linker script. This adds in RP2350 RISC-V specific +# things that the riscv-rt crate's `link.x` requires and then includes +# `link.x` automatically. This is the reverse of how we do it on Cortex-M. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x06_adc_rust/.gitignore b/drivers/0x06_adc_rust/.gitignore new file mode 100644 index 0000000..03381c1 --- /dev/null +++ b/drivers/0x06_adc_rust/.gitignore @@ -0,0 +1,113 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,macos,windows,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux diff --git a/drivers/0x06_adc_rust/.pico-rs b/drivers/0x06_adc_rust/.pico-rs new file mode 100644 index 0000000..1b6702a --- /dev/null +++ b/drivers/0x06_adc_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 \ No newline at end of file diff --git a/drivers/0x06_adc_rust/.vscode/extensions.json b/drivers/0x06_adc_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x06_adc_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x06_adc_rust/.vscode/launch.json b/drivers/0x06_adc_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x06_adc_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x06_adc_rust/.vscode/settings.json b/drivers/0x06_adc_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x06_adc_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x06_adc_rust/.vscode/tasks.json b/drivers/0x06_adc_rust/.vscode/tasks.json new file mode 100644 index 0000000..4194af4 --- /dev/null +++ b/drivers/0x06_adc_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/drivers/0x06_adc_rust/Cargo.toml b/drivers/0x06_adc_rust/Cargo.toml new file mode 100644 index 0000000..e408b26 --- /dev/null +++ b/drivers/0x06_adc_rust/Cargo.toml @@ -0,0 +1,40 @@ +[package] +edition = "2024" +name = "adc" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "adc_lib" +path = "src/lib.rs" + +[[bin]] +name = "adc" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +embedded-hal = "1.0.0" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x06_adc_rust/LICENSE-APACHE b/drivers/0x06_adc_rust/LICENSE-APACHE new file mode 100644 index 0000000..8d99cbc --- /dev/null +++ b/drivers/0x06_adc_rust/LICENSE-APACHE @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2021–2024 The rp-rs Developers + Copyright (c) 2021 rp-rs organization + Copyright (c) 2025 Raspberry Pi Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/drivers/0x06_adc_rust/LICENSE-MIT b/drivers/0x06_adc_rust/LICENSE-MIT new file mode 100644 index 0000000..5369c70 --- /dev/null +++ b/drivers/0x06_adc_rust/LICENSE-MIT @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2021–2024 The rp-rs Developers +Copyright (c) 2021 rp-rs organization +Copyright (c) 2025 Raspberry Pi Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + \ No newline at end of file diff --git a/drivers/0x06_adc_rust/build.rs b/drivers/0x06_adc_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x06_adc_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x06_adc_rust/rp2040.x b/drivers/0x06_adc_rust/rp2040.x new file mode 100644 index 0000000..0cc665b --- /dev/null +++ b/drivers/0x06_adc_rust/rp2040.x @@ -0,0 +1,91 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + /* + * Here we assume you have 2048 KiB of Flash. This is what the Pi Pico + * has, but your board may have more or less Flash and you should adjust + * this value to suit. + */ + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + /* + * RAM consists of 4 banks, SRAM0-SRAM3, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K + /* + * RAM banks 4 and 5 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k + + /* SRAM banks 0-3 can also be accessed directly. However, those ranges + alias with the RAM mapping, above. So don't use them at the same time! + SRAM0 : ORIGIN = 0x21000000, LENGTH = 64k + SRAM1 : ORIGIN = 0x21010000, LENGTH = 64k + SRAM2 : ORIGIN = 0x21020000, LENGTH = 64k + SRAM3 : ORIGIN = 0x21030000, LENGTH = 64k + */ +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader + * + * An executable block of code which sets up the QSPI interface for + * 'Execute-In-Place' (or XIP) mode. Also sends chip-specific commands to + * the external flash chip. + * + * Must go at the start of external flash, where the Boot ROM expects it. + */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 512 bytes of flash, + * where picotool can find it + */ + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x06_adc_rust/rp2350.x b/drivers/0x06_adc_rust/rp2350.x new file mode 100644 index 0000000..bda94d8 --- /dev/null +++ b/drivers/0x06_adc_rust/rp2350.x @@ -0,0 +1,83 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 4K of flash + * where the Boot ROM (and picotool) can find it + */ + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + /* move .text to start /after/ the boot info */ + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + /* ### Boot ROM extra info + * + * Goes after everything in our program, so it can contain a signature. + */ + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + \ No newline at end of file diff --git a/drivers/0x06_adc_rust/rp2350_riscv.x b/drivers/0x06_adc_rust/rp2350_riscv.x new file mode 100644 index 0000000..84388aa --- /dev/null +++ b/drivers/0x06_adc_rust/rp2350_riscv.x @@ -0,0 +1,259 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol is not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `32`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > FLASH + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + __start_block_addr = .; + KEEP(*(.start_block)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + . = ALIGN(4); + } > FLASH + + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > FLASH + + .data : ALIGN(32) + { + _sidata = LOADADDR(.data); + __sidata = LOADADDR(.data); + _sdata = .; + __sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(32); + _edata = .; + __edata = .; + } > RAM AT > FLASH + + .bss (NOLOAD) : ALIGN(32) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(32); + _ebss = .; + } > RAM + + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > RAM + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > RAM + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(start_to_end = __end_block_addr - __start_block_addr); +PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 32 == 0, " +ERROR(riscv-rt): the start of the RAM must be 32-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, " +BUG(riscv-rt): .data is not 32-byte aligned"); + +ASSERT(_sidata % 32 == 0, " +BUG(riscv-rt): the LMA of .data is not 32-byte aligned"); + +ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, " +BUG(riscv-rt): .bss is not 32-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(riscv-rt): The .text section must be placed inside the FLASH region. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/drivers/0x06_adc_rust/src/adc.rs b/drivers/0x06_adc_rust/src/adc.rs new file mode 100644 index 0000000..86bc0c6 --- /dev/null +++ b/drivers/0x06_adc_rust/src/adc.rs @@ -0,0 +1,148 @@ +//! Implementation module +//! +//! **File:** `adc.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// ADC reference voltage in millivolts. +pub const ADC_VREF_MV: u32 = 3300; + +/// ADC full-scale value for 12-bit resolution. +pub const ADC_FULL_SCALE: u32 = 4095; + +/// Convert a raw 12-bit ADC value to millivolts. +/// +/// Scales the raw value linearly against the 3.3 V reference. +/// +/// # Arguments +/// +/// * `raw` - 12-bit ADC conversion result (0–4095). +/// +/// # Returns +/// +/// Equivalent voltage in millivolts (0–3300). +/// +/// # Arguments +/// +/// * `raw` - The `raw` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +pub fn raw_to_mv(raw: u16) -> u32 { + raw as u32 * ADC_VREF_MV / ADC_FULL_SCALE +} + +/// Convert a raw temperature-sensor ADC value to degrees Celsius. +/// +/// Applies the RP2350 datasheet formula: +/// T = 27 - (V - 0.706) / 0.001721 +/// +/// # Arguments +/// +/// * `raw` - 12-bit ADC result from the internal temperature sensor (channel 4). +/// +/// # Returns +/// +/// Die temperature in degrees Celsius. +/// +/// # Arguments +/// +/// * `raw` - The `raw` parameter. +/// +/// # Returns +/// +/// A value of type `f32`. +pub fn raw_to_celsius(raw: u16) -> f32 { + let voltage = raw as f32 * 3.3f32 / ADC_FULL_SCALE as f32; + 27.0f32 - (voltage - 0.706f32) / 0.001721f32 +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the raw to mv zero operation. + #[test] + fn raw_to_mv_zero() { + assert_eq!(raw_to_mv(0), 0); + } + + /// Executes the raw to mv full scale operation. + #[test] + fn raw_to_mv_full_scale() { + assert_eq!(raw_to_mv(4095), 3300); + } + + /// Executes the raw to mv half operation. + #[test] + fn raw_to_mv_half() { + let mv = raw_to_mv(2048); + assert!(mv >= 1649 && mv <= 1651); + } + + /// Executes the raw to mv quarter operation. + #[test] + fn raw_to_mv_quarter() { + let mv = raw_to_mv(1024); + assert!(mv >= 824 && mv <= 826); + } + + /// Executes the raw to celsius room temp operation. + #[test] + fn raw_to_celsius_room_temp() { + let temp = raw_to_celsius(876); + assert!(temp > 20.0 && temp < 35.0); + } + + /// Executes the raw to celsius known voltage operation. + #[test] + fn raw_to_celsius_known_voltage() { + let raw = (0.706f32 / 3.3f32 * ADC_FULL_SCALE as f32 + 0.5f32) as u16; + let temp = raw_to_celsius(raw); + assert!((temp - 27.0).abs() < 1.0); + } + + /// Executes the raw to celsius higher voltage operation. + #[test] + fn raw_to_celsius_higher_voltage() { + let temp_low = raw_to_celsius(1000); + let temp_high = raw_to_celsius(800); + assert!(temp_high > temp_low); + } + + /// Executes the raw to mv one count operation. + #[test] + fn raw_to_mv_one_count() { + assert_eq!(raw_to_mv(1), 0); + } + + /// Executes the raw to mv ten counts operation. + #[test] + fn raw_to_mv_ten_counts() { + assert_eq!(raw_to_mv(10), 8); + } +} diff --git a/drivers/0x06_adc_rust/src/board.rs b/drivers/0x06_adc_rust/src/board.rs new file mode 100644 index 0000000..3908892 --- /dev/null +++ b/drivers/0x06_adc_rust/src/board.rs @@ -0,0 +1,459 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// ADC one-shot trait for .read() +use cortex_m::prelude::_embedded_hal_adc_OneShot; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Main-loop polling interval in milliseconds. +pub(crate) const POLL_MS: u32 = 500; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Write a conditional digit into `buf` if `val` meets the threshold. +fn write_conditional_digit( + buf: &mut [u8], + pos: &mut usize, + val: u32, + threshold: u32, + divisor: u32, +) { + if val >= threshold { + buf[*pos] = b'0' + ((val / divisor) % 10) as u8; + *pos += 1; + } +} + +/// Write a u32 with minimum digits (no leading zeros). +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `val` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `val` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn write_min_digits(buf: &mut [u8], val: u32) -> usize { + let mut pos = 0; + write_conditional_digit(buf, &mut pos, val, 100, 100); + write_conditional_digit(buf, &mut pos, val, 10, 10); + buf[pos] = b'0' + (val % 10) as u8; + pos + 1 +} + +/// Write 4-digit millivolt value into `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `mv` - The `mv` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `mv` - The `mv` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn write_mv_digits(buf: &mut [u8], mv: u32) -> usize { + buf[0] = b'0' + ((mv / 1000) % 10) as u8; + buf[1] = b'0' + ((mv / 100) % 10) as u8; + buf[2] = b'0' + ((mv / 10) % 10) as u8; + buf[3] = b'0' + (mv % 10) as u8; + 4 +} + +/// Write a negative sign if needed and return the absolute temperature value. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `temp_int` - The `temp_int` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `temp_int` - The `temp_int` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +fn write_sign(buf: &mut [u8], pos: &mut usize, temp_int: i32) -> u32 { + if temp_int >= 0 { + return temp_int as u32; + } + buf[*pos] = b'-'; + *pos += 1; + (-temp_int) as u32 +} + +/// Write temperature as "[-]NN.F" into `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `temp_int` - The `temp_int` parameter. +/// * `temp_frac` - The `temp_frac` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `temp_int` - The `temp_int` parameter. +/// * `temp_frac` - The `temp_frac` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn write_temp(buf: &mut [u8], temp_int: i32, temp_frac: u8) -> usize { + let mut pos = 0; + let abs_temp = write_sign(buf, &mut pos, temp_int); + pos += write_min_digits(&mut buf[pos..], abs_temp); + buf[pos] = b'.'; + buf[pos + 1] = b'0' + temp_frac; + pos + 2 +} + +/// Format a millivolt value into "ADC0: NNNN mV | Chip temp: NN.N C\r\n". +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 48 bytes). +/// * `mv` - Voltage in millivolts. +/// * `temp_int` - Integer part of temperature. +/// * `temp_frac` - Single decimal digit of temperature fraction. +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `mv` - The `mv` parameter. +/// * `temp_int` - The `temp_int` parameter. +/// * `temp_frac` - The `temp_frac` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub(crate) fn format_adc_line(buf: &mut [u8], mv: u32, temp_int: i32, temp_frac: u8) -> usize { + buf[..6].copy_from_slice(b"ADC0: "); + let p1 = 6 + write_mv_digits(&mut buf[6..], mv); + buf[p1..p1 + 19].copy_from_slice(b" mV | Chip temp: "); + let p2 = p1 + 19 + write_temp(&mut buf[p1 + 19..], temp_int, temp_frac); + buf[p2..p2 + 4].copy_from_slice(b" C\r\n"); + p2 + 4 +} + +/// Type alias for the ADC input pin on GPIO 26. +type Gpio26Adc = hal::adc::AdcPin>; + +/// Initialise all peripherals and run the ADC demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + let (mut adc, mut adc_pin, mut temp) = init_adc(pac.ADC, p.gpio26, &mut pac.RESETS); + uart.write_full_blocking(b"ADC driver initialized: GPIO26 (channel 0)\r\n"); + adc_loop(&uart, &mut adc, &mut adc_pin, &mut temp, &mut delay) +} + +/// Create the ADC peripheral, GPIO 26 input channel, and temperature sensor. +/// +/// # Arguments +/// +/// * `adc_pac` - PAC ADC peripheral singleton. +/// * `gpio26` - Default GPIO 26 pin to use as ADC input. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// Tuple of (ADC driver, ADC pin channel, temperature sensor channel). +fn init_adc( + adc_pac: hal::pac::ADC, + gpio26: Pin, + resets: &mut hal::pac::RESETS, +) -> (hal::Adc, Gpio26Adc, hal::adc::TempSense) { + let mut adc = hal::Adc::new(adc_pac, resets); + let pin = hal::adc::AdcPin::new(gpio26).unwrap(); + let temp = adc.take_temp_sensor().unwrap(); + (adc, pin, temp) +} + +/// Sample voltage and temperature, format, and print in a loop. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `adc` - Mutable reference to the ADC driver. +/// * `adc_pin` - Mutable reference to the GPIO 26 ADC channel. +/// * `temp` - Mutable reference to the temperature sensor channel. +/// * `delay` - Mutable reference to the blocking delay provider. +fn adc_loop( + uart: &EnabledUart, + adc: &mut hal::Adc, + adc_pin: &mut Gpio26Adc, + temp: &mut hal::adc::TempSense, + delay: &mut cortex_m::delay::Delay, +) -> ! { + let mut buf = [0u8; 48]; + loop { + let (mv, temp_int, temp_frac) = read_adc(adc, adc_pin, temp); + let n = format_adc_line(&mut buf, mv, temp_int, temp_frac); + uart.write_full_blocking(&buf[..n]); + delay.delay_ms(POLL_MS); + } +} + +/// Read voltage and temperature from the ADC. +/// +/// # Arguments +/// +/// * `adc` - Mutable reference to the ADC driver. +/// * `adc_pin` - Mutable reference to the GPIO 26 ADC channel. +/// * `temp` - Mutable reference to the temperature sensor channel. +/// +/// # Returns +/// +/// Tuple of (millivolts, integer temperature, fractional temperature digit). +fn read_adc( + adc: &mut hal::Adc, + adc_pin: &mut Gpio26Adc, + temp: &mut hal::adc::TempSense, +) -> (u32, i32, u8) { + let raw_v: u16 = adc.read(adc_pin).unwrap(); + let mv = adc_lib::adc::raw_to_mv(raw_v); + let raw_t: u16 = adc.read(temp).unwrap(); + let celsius = adc_lib::adc::raw_to_celsius(raw_t); + let temp_int = celsius as i32; + let temp_frac = (((celsius - temp_int as f32) * 10.0) as u8).min(9); + (mv, temp_int, temp_frac) +} + diff --git a/drivers/0x06_adc_rust/src/lib.rs b/drivers/0x06_adc_rust/src/lib.rs new file mode 100644 index 0000000..114b04e --- /dev/null +++ b/drivers/0x06_adc_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod adc; diff --git a/drivers/0x06_adc_rust/src/main.rs b/drivers/0x06_adc_rust/src/main.rs new file mode 100644 index 0000000..4f0c9d0 --- /dev/null +++ b/drivers/0x06_adc_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// ADC driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the ADC voltage and temperature demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"ADC Voltage and Temperature Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x07_i2c/.vscode/.vscode/c_cpp_properties.json b/drivers/0x07_i2c/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x07_i2c/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x07_i2c/.vscode/.vscode/cmake-kits.json b/drivers/0x07_i2c/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x07_i2c/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x07_i2c/.vscode/.vscode/extensions.json b/drivers/0x07_i2c/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x07_i2c/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x07_i2c/.vscode/.vscode/launch.json b/drivers/0x07_i2c/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x07_i2c/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x07_i2c/.vscode/.vscode/settings.json b/drivers/0x07_i2c/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x07_i2c/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x07_i2c/.vscode/.vscode/tasks.json b/drivers/0x07_i2c/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x07_i2c/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x07_i2c/.vscode/c_cpp_properties.json b/drivers/0x07_i2c/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x07_i2c/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x07_i2c/.vscode/cmake-kits.json b/drivers/0x07_i2c/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x07_i2c/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x07_i2c/.vscode/extensions.json b/drivers/0x07_i2c/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x07_i2c/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x07_i2c/.vscode/launch.json b/drivers/0x07_i2c/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x07_i2c/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x07_i2c/.vscode/settings.json b/drivers/0x07_i2c/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x07_i2c/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x07_i2c/.vscode/tasks.json b/drivers/0x07_i2c/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x07_i2c/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x07_i2c/0x07_i2c.c b/drivers/0x07_i2c/0x07_i2c.c new file mode 100644 index 0000000..2f7c527 --- /dev/null +++ b/drivers/0x07_i2c/0x07_i2c.c @@ -0,0 +1,65 @@ +/** + * @file 0x07_i2c.c + * @brief I2C demonstration: scan all 7-bit addresses and report devices + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates I2C bus scanning using the i2c driver (i2c.h / i2c.c). I2C1 + * is configured at 100 kHz on SDA=GPIO2 / SCL=GPIO3. A formatted hex table + * of all responding device addresses is printed over UART and repeated + * every 5 seconds. + * + * Wiring: + * GPIO2 (SDA) -> I2C device SDA (4.7 kohm pull-up to 3.3 V recommended) + * GPIO3 (SCL) -> I2C device SCL (4.7 kohm pull-up to 3.3 V recommended) + * 3.3V -> I2C device VCC + * GND -> I2C device GND + */ + +#include +#include "pico/stdlib.h" +#include "i2c.h" + +/** @brief I2C port number (0 or 1) */ +#define I2C_PORT 1 +/** @brief GPIO pin for I2C SDA */ +#define I2C_SDA_PIN 2 +/** @brief GPIO pin for I2C SCL */ +#define I2C_SCL_PIN 3 +/** @brief I2C bus clock rate in Hz */ +#define I2C_BAUD 100000 + +int main(void) { + stdio_init_all(); + i2c_driver_init(I2C_PORT, I2C_SDA_PIN, I2C_SCL_PIN, I2C_BAUD); + printf("I2C driver initialized: I2C%d @ %d Hz SDA=GPIO%d SCL=GPIO%d\r\n", + I2C_PORT, I2C_BAUD, I2C_SDA_PIN, I2C_SCL_PIN); + while (true) { + i2c_driver_scan(I2C_PORT); + sleep_ms(5000); + } +} diff --git a/drivers/0x07_i2c/CMakeLists.txt b/drivers/0x07_i2c/CMakeLists.txt new file mode 100644 index 0000000..30a6ff6 --- /dev/null +++ b/drivers/0x07_i2c/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x07_i2c C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x07_i2c 0x07_i2c.c i2c.c) + +pico_set_program_name(0x07_i2c "0x07_i2c") +pico_set_program_version(0x07_i2c "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x07_i2c 1) +pico_enable_stdio_usb(0x07_i2c 0) + +# Add the standard library to the build +target_link_libraries(0x07_i2c + pico_stdlib + hardware_i2c) + +# Add the standard include files to the build +target_include_directories(0x07_i2c PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x07_i2c) diff --git a/drivers/0x07_i2c/i2c.c b/drivers/0x07_i2c/i2c.c new file mode 100644 index 0000000..b1925a7 --- /dev/null +++ b/drivers/0x07_i2c/i2c.c @@ -0,0 +1,92 @@ +/** + * @file i2c.c + * @brief Implementation of the I2C bus driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "i2c.h" +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "hardware/gpio.h" + +/** + * @brief Map an I2C port number to its hardware instance pointer + * + * @param port I2C port number (0 for i2c0, 1 for i2c1) + * @return i2c_inst_t* Pointer to the corresponding I2C hardware instance + */ +static i2c_inst_t *get_i2c_inst(uint8_t port) { + return port == 0 ? i2c0 : i2c1; +} + +void i2c_driver_init(uint8_t port, uint32_t sda_pin, uint32_t scl_pin, + uint32_t baud_hz) { + i2c_inst_t *inst = get_i2c_inst(port); + i2c_init(inst, baud_hz); + gpio_set_function(sda_pin, GPIO_FUNC_I2C); + gpio_set_function(scl_pin, GPIO_FUNC_I2C); + gpio_pull_up(sda_pin); + gpio_pull_up(scl_pin); +} + +bool i2c_driver_probe(uint8_t port, uint8_t addr) { + i2c_inst_t *inst = get_i2c_inst(port); + uint8_t dummy; + return i2c_read_blocking(inst, addr, &dummy, 1, false) >= 0; +} + +/** + * @brief Print the I2C scan table header over UART + */ +static void print_scan_header(void) { + printf("\r\nI2C bus scan:\r\n"); + printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n"); +} + +/** + * @brief Print one cell of the scan table for a given address + * + * Prints the row label when the address is at a 16-byte boundary, then + * prints the address if a device responds, dashes if not, or blank if + * the address is in the reserved range. Ends the row at every 16th address. + * + * @param port I2C port number (0 for i2c0, 1 for i2c1) + * @param addr 7-bit I2C address being probed + */ +static void print_scan_entry(uint8_t port, uint8_t addr) { + if (addr % 16 == 0) printf("%02X: ", addr); + if (addr < 0x08 || addr > 0x77) printf(" "); + else if (i2c_driver_probe(port, addr)) printf("%02X ", addr); + else printf("-- "); + if (addr % 16 == 15) printf("\r\n"); +} + +void i2c_driver_scan(uint8_t port) { + print_scan_header(); + for (uint8_t addr = 0; addr < 128; addr++) + print_scan_entry(port, addr); +} diff --git a/drivers/0x07_i2c/i2c.h b/drivers/0x07_i2c/i2c.h new file mode 100644 index 0000000..c5add58 --- /dev/null +++ b/drivers/0x07_i2c/i2c.h @@ -0,0 +1,75 @@ +/** + * @file i2c.h + * @brief Header for I2C bus driver (init + 7-bit address scanner) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef I2C_H +#define I2C_H + +#include +#include + +/** + * @brief Initialize an I2C peripheral at the requested baud rate + * + * Calls i2c_init() for the given instance, assigns the GPIO alternate + * functions for SDA and SCL, and enables the internal pull-ups on both + * pins so that short buses operate without external resistors. + * + * @param port I2C port number (0 for i2c0, 1 for i2c1) + * @param sda_pin GPIO pin number for SDA + * @param scl_pin GPIO pin number for SCL + * @param baud_hz Bus clock frequency in Hz (e.g. 100000 for standard mode) + */ +void i2c_driver_init(uint8_t port, uint32_t sda_pin, uint32_t scl_pin, + uint32_t baud_hz); + +/** + * @brief Probe a 7-bit I2C address and return whether a device responds + * + * Attempts a 1-byte read to the target address. A non-negative return from + * i2c_read_blocking() means the device sent an ACK. A negative return means + * the address is unoccupied. + * + * @param port I2C port number (0 for i2c0, 1 for i2c1) + * @param addr 7-bit I2C address to probe (0x00 - 0x7F) + * @return bool true if a device acknowledged, false otherwise + */ +bool i2c_driver_probe(uint8_t port, uint8_t addr); + +/** + * @brief Scan all valid 7-bit addresses and print a formatted table over UART + * + * Iterates addresses 0x08 through 0x77, probes each one via i2c_driver_probe(), + * and prints a 16-column hex grid showing discovered device addresses. + * The reserved ranges (0x00-0x07 and 0x78-0x7F) are shown as blank. + * + * @param port I2C port number (0 for i2c0, 1 for i2c1) + */ +void i2c_driver_scan(uint8_t port); + +#endif // I2C_H diff --git a/drivers/0x07_i2c/pico_sdk_import.cmake b/drivers/0x07_i2c/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x07_i2c/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x07_i2c_cbm/.vscode/c_cpp_properties.json b/drivers/0x07_i2c_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x07_i2c_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x07_i2c_cbm/.vscode/extensions.json b/drivers/0x07_i2c_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x07_i2c_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x07_i2c_cbm/.vscode/launch.json b/drivers/0x07_i2c_cbm/.vscode/launch.json new file mode 100644 index 0000000..6fa0704 --- /dev/null +++ b/drivers/0x07_i2c_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/i2c.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDir": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDir": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x07_i2c_cbm/.vscode/settings.json b/drivers/0x07_i2c_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x07_i2c_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x07_i2c_cbm/.vscode/tasks.json b/drivers/0x07_i2c_cbm/.vscode/tasks.json new file mode 100644 index 0000000..985bcbe --- /dev/null +++ b/drivers/0x07_i2c_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/i2c.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/i2c.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x07_i2c_cbm/Inc/rp2350.h b/drivers/0x07_i2c_cbm/Inc/rp2350.h new file mode 100644 index 0000000..49b8459 --- /dev/null +++ b/drivers/0x07_i2c_cbm/Inc/rp2350.h @@ -0,0 +1,306 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define I2C1_BASE 0x40098000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[12]; // GPOUT0..GPOUT3 registers Address offset: 0x00-0x2C + __IO uint32_t CLK_REF_CTRL; // Reference clock control Address offset: 0x30 + __IO uint32_t RESERVED1[5]; // CLK_REF_DIV..CLK_SYS_SELECTED Address offset: 0x34-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief I2C (Synopsys DW APB I2C Controller) + */ +typedef struct +{ + __IO uint32_t CON; // Control register Address offset: 0x00 + __IO uint32_t TAR; // Target address Address offset: 0x04 + __IO uint32_t SAR; // Slave address Address offset: 0x08 + __IO uint32_t RESERVED0; // Padding Address offset: 0x0C + __IO uint32_t DATA_CMD; // Rx/Tx data buffer and command Address offset: 0x10 + __IO uint32_t SS_SCL_HCNT; // Standard speed SCL high count Address offset: 0x14 + __IO uint32_t SS_SCL_LCNT; // Standard speed SCL low count Address offset: 0x18 + __IO uint32_t FS_SCL_HCNT; // Fast mode SCL high count Address offset: 0x1C + __IO uint32_t FS_SCL_LCNT; // Fast mode SCL low count Address offset: 0x20 + __IO uint32_t RESERVED1[2]; // Padding Address offset: 0x24-0x28 + __IO uint32_t INTR_STAT; // Interrupt status (RO) Address offset: 0x2C + __IO uint32_t INTR_MASK; // Interrupt mask Address offset: 0x30 + __IO uint32_t RAW_INTR_STAT; // Raw interrupt status (RO) Address offset: 0x34 + __IO uint32_t RX_TL; // Receive FIFO threshold level Address offset: 0x38 + __IO uint32_t TX_TL; // Transmit FIFO threshold level Address offset: 0x3C + __IO uint32_t CLR_INTR; // Clear combined interrupt (RO) Address offset: 0x40 + __IO uint32_t CLR_RX_UNDER; // Clear RX_UNDER interrupt (RO) Address offset: 0x44 + __IO uint32_t CLR_RX_OVER; // Clear RX_OVER interrupt (RO) Address offset: 0x48 + __IO uint32_t CLR_TX_OVER; // Clear TX_OVER interrupt (RO) Address offset: 0x4C + __IO uint32_t CLR_RD_REQ; // Clear RD_REQ interrupt (RO) Address offset: 0x50 + __IO uint32_t CLR_TX_ABRT; // Clear TX_ABRT interrupt (RO) Address offset: 0x54 + __IO uint32_t CLR_RX_DONE; // Clear RX_DONE interrupt (RO) Address offset: 0x58 + __IO uint32_t CLR_ACTIVITY; // Clear ACTIVITY interrupt (RO) Address offset: 0x5C + __IO uint32_t CLR_STOP_DET; // Clear STOP_DET interrupt (RO) Address offset: 0x60 + __IO uint32_t CLR_START_DET; // Clear START_DET interrupt (RO) Address offset: 0x64 + __IO uint32_t CLR_GEN_CALL; // Clear GEN_CALL interrupt (RO) Address offset: 0x68 + __IO uint32_t ENABLE; // I2C enable Address offset: 0x6C + __IO uint32_t STATUS; // I2C status (RO) Address offset: 0x70 + __IO uint32_t TXFLR; // Transmit FIFO level (RO) Address offset: 0x74 + __IO uint32_t RXFLR; // Receive FIFO level (RO) Address offset: 0x78 + __IO uint32_t SDA_HOLD; // SDA hold time Address offset: 0x7C + __IO uint32_t TX_ABRT_SRC; // Transmit abort source (RO) Address offset: 0x80 + __IO uint32_t RESERVED2; // SLV_DATA_NACK_ONLY Address offset: 0x84 + __IO uint32_t DMA_CR; // DMA control Address offset: 0x88 + __IO uint32_t DMA_TDLR; // DMA transmit data level Address offset: 0x8C + __IO uint32_t DMA_RDLR; // DMA receive data level Address offset: 0x90 + __IO uint32_t SDA_SETUP; // SDA setup time Address offset: 0x94 + __IO uint32_t ACK_GEN_CALL; // ACK general call Address offset: 0x98 + __IO uint32_t ENABLE_STATUS; // I2C enable status (RO) Address offset: 0x9C + __IO uint32_t FS_SPKLEN; // Fast mode spike suppression limit Address offset: 0xA0 +} I2C_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLK_REF bit definitions + */ +#define CLK_REF_CTRL_SRC_XOSC 2U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_MASK (0x07U << CLK_PERI_CTRL_AUXSRC_SHIFT) +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_I2C1_SHIFT 5U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_I2C 0x03U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U +#define IO_BANK0_CTRL_FUNCSEL_NULL 0x1FU + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U +#define PADS_BANK0_PUE_SHIFT 3U + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief GPIO pin definitions + */ +#define I2C_SDA_PIN 2U +#define I2C_SCL_PIN 3U + +/** + * @brief I2C CON register bit definitions + */ +#define I2C_CON_MASTER_MODE_SHIFT 0U +#define I2C_CON_SPEED_SHIFT 1U +#define I2C_CON_SPEED_FAST 2U +#define I2C_CON_IC_RESTART_EN_SHIFT 5U +#define I2C_CON_IC_SLAVE_DISABLE_SHIFT 6U +#define I2C_CON_TX_EMPTY_CTRL_SHIFT 8U + +/** + * @brief I2C DATA_CMD register bit definitions + */ +#define I2C_DATA_CMD_CMD_SHIFT 8U +#define I2C_DATA_CMD_STOP_SHIFT 9U +#define I2C_DATA_CMD_RESTART_SHIFT 10U + +/** + * @brief I2C RAW_INTR_STAT register bit masks + */ +#define I2C_RAW_INTR_TX_EMPTY (1U << 4) +#define I2C_RAW_INTR_TX_ABRT (1U << 6) +#define I2C_RAW_INTR_STOP_DET (1U << 9) + +/** + * @brief I2C TX_ABRT_SOURCE bit definitions + */ +#define I2C_TX_ABRT_7B_ADDR_NOACK (1U << 0) + +/** + * @brief I2C STATUS register bit definitions + */ +#define I2C_STATUS_TFNF_SHIFT 1U +#define I2C_STATUS_RFNE_SHIFT 3U + +/** + * @brief I2C clock timing for 100 kHz at 12 MHz clk_sys + */ +#define I2C_SYS_CLK_HZ 12000000U +#define I2C_BAUD_HZ 100000U +#define I2C_FS_SCL_HCNT_VAL 48U +#define I2C_FS_SCL_LCNT_VAL 72U +#define I2C_SDA_TX_HOLD_VAL 4U +#define I2C_FS_SPKLEN_VAL 4U + +/** + * @brief I2C timeout + */ +#define I2C_TIMEOUT 1000000U + +#endif /* __RP2350_H */ diff --git a/drivers/0x07_i2c_cbm/Inc/rp2350_delay.h b/drivers/0x07_i2c_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..6860cf7 --- /dev/null +++ b/drivers/0x07_i2c_cbm/Inc/rp2350_delay.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond busy-wait delay calibrated for a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x07_i2c_cbm/Inc/rp2350_i2c.h b/drivers/0x07_i2c_cbm/Inc/rp2350_i2c.h new file mode 100644 index 0000000..1b8c7f8 --- /dev/null +++ b/drivers/0x07_i2c_cbm/Inc/rp2350_i2c.h @@ -0,0 +1,76 @@ +/** + * @file rp2350_i2c.h + * @brief I2C driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * I2C1 master-mode driver at 100 kHz on SDA=GPIO2 / SCL=GPIO3. + * Provides init, probe, read, write, and bus scan functions. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_I2C_H +#define __RP2350_I2C_H + +#include "rp2350.h" + +/** + * @brief Release I2C1 from reset and wait for completion. + * @retval None + */ +void i2c_release_reset(void); + +/** + * @brief Initialize I2C1 as a 100 kHz master on SDA=GPIO2 / SCL=GPIO3. + * + * Configures GPIO pads with pull-ups, sets FUNCSEL to I2C, + * programs SCL timing for 100 kHz at 12 MHz clk_sys, and + * enables the controller in master mode with 7-bit addressing. + * + * @retval None + */ +void i2c_init(void); + +/** + * @brief Probe a 7-bit I2C address and return whether a device responds. + * + * Sends a 1-byte read to the target address. Returns true if + * the device acknowledges, false otherwise. + * + * @param addr 7-bit I2C address (0x08-0x77) + * @retval bool true if a device acknowledged, false otherwise + */ +bool i2c_probe(uint8_t addr); + +/** + * @brief Scan all valid 7-bit addresses and print a formatted table. + * + * Iterates addresses 0x00 through 0x7F, probes each valid one + * via i2c_probe(), and prints a 16-column hex grid showing + * discovered device addresses over UART. + * + * @retval None + */ +void i2c_scan(void); + +#endif /* __RP2350_I2C_H */ diff --git a/drivers/0x07_i2c_cbm/Inc/rp2350_reset.h b/drivers/0x07_i2c_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x07_i2c_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x07_i2c_cbm/Inc/rp2350_reset_handler.h b/drivers/0x07_i2c_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x07_i2c_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x07_i2c_cbm/Inc/rp2350_stack.h b/drivers/0x07_i2c_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x07_i2c_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x07_i2c_cbm/Inc/rp2350_uart.h b/drivers/0x07_i2c_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x07_i2c_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x07_i2c_cbm/Inc/rp2350_xosc.h b/drivers/0x07_i2c_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..80fd75d --- /dev/null +++ b/drivers/0x07_i2c_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,55 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +/** + * @brief Switch CLK_REF source to XOSC for a stable 12 MHz clk_sys. + * @retval None + */ +void xosc_set_clk_ref(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x07_i2c_cbm/Makefile b/drivers/0x07_i2c_cbm/Makefile new file mode 100644 index 0000000..f37fda1 --- /dev/null +++ b/drivers/0x07_i2c_cbm/Makefile @@ -0,0 +1,85 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C I2C driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = i2c + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_i2c.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x07_i2c_cbm/Src/image_def.c b/drivers/0x07_i2c_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x07_i2c_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x07_i2c_cbm/Src/main.c b/drivers/0x07_i2c_cbm/Src/main.c new file mode 100644 index 0000000..7821d8d --- /dev/null +++ b/drivers/0x07_i2c_cbm/Src/main.c @@ -0,0 +1,61 @@ +/** + * @file main.c + * @brief I2C demonstration: scan all 7-bit addresses and report devices. + * @author Kevin Thomas + * @date 2026 + * + * Demonstrates I2C bus scanning using the bare-metal I2C driver. + * I2C1 is configured at 100 kHz on SDA=GPIO2 / SCL=GPIO3. A + * formatted hex table of all responding device addresses is + * printed over UART and repeated every 5 seconds. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * GPIO2 -> I2C device SDA (4.7 kohm pull-up to 3.3 V) + * GPIO3 -> I2C device SCL (4.7 kohm pull-up to 3.3 V) + * 3.3V -> I2C device VCC + * GND -> I2C device GND + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_i2c.h" +#include "rp2350_uart.h" +#include "rp2350_xosc.h" +#include "rp2350_delay.h" + +/** @brief Delay between I2C bus scans in milliseconds */ +#define SCAN_DELAY_MS 5000 + +int main(void) +{ + xosc_set_clk_ref(); + i2c_release_reset(); + i2c_init(); + uart_puts("I2C driver initialized: I2C1 @ 100 kHz SDA=GPIO2 SCL=GPIO3\r\n"); + while (1) + { + i2c_scan(); + delay_ms(SCAN_DELAY_MS); + } +} diff --git a/drivers/0x07_i2c_cbm/Src/rp2350_delay.c b/drivers/0x07_i2c_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..70d7f16 --- /dev/null +++ b/drivers/0x07_i2c_cbm/Src/rp2350_delay.c @@ -0,0 +1,48 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond delay calibrated for a 12 MHz clock + * (3600 loop iterations per millisecond). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x07_i2c_cbm/Src/rp2350_i2c.c b/drivers/0x07_i2c_cbm/Src/rp2350_i2c.c new file mode 100644 index 0000000..554425f --- /dev/null +++ b/drivers/0x07_i2c_cbm/Src/rp2350_i2c.c @@ -0,0 +1,235 @@ +/** + * @file rp2350_i2c.c + * @brief RP2350 I2C1 master-mode driver implementation. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal driver for the RP2350 Synopsys DW APB I2C controller. + * Configures I2C1 at 100 kHz on SDA=GPIO2 / SCL=GPIO3 with + * internal pull-ups. Provides address probing and bus scanning. + * All register accesses verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_i2c.h" +#include "rp2350_uart.h" + +/** + * @brief Configure GPIO2 and GPIO3 pads for I2C (input enabled, pull-up). + * + * Clears pad isolation, enables input, enables pull-up, and sets + * drive strength to 4 mA on both SDA and SCL pins. + * + * @retval None + */ +static void i2c_config_pads(void) +{ + uint32_t pad_val = (1U << PADS_BANK0_IE_SHIFT) | (1U << PADS_BANK0_PUE_SHIFT) | (1U << 4); + PADS_BANK0->GPIO[I2C_SDA_PIN] = pad_val; + PADS_BANK0->GPIO[I2C_SCL_PIN] = pad_val; +} + +/** + * @brief Set GPIO2 and GPIO3 IO mux function to I2C (FUNCSEL=3). + * @retval None + */ +static void i2c_config_gpio(void) +{ + IO_BANK0->GPIO[I2C_SDA_PIN].CTRL = IO_BANK0_CTRL_FUNCSEL_I2C; + IO_BANK0->GPIO[I2C_SCL_PIN].CTRL = IO_BANK0_CTRL_FUNCSEL_I2C; +} + +/** + * @brief Configure I2C1 as fast-mode master with 7-bit addressing. + * + * Sets MASTER_MODE, SPEED=FAST, IC_SLAVE_DISABLE, IC_RESTART_EN, + * and TX_EMPTY_CTRL bits in the CON register. + * + * @retval None + */ +static void i2c_config_con(void) +{ + I2C1->CON = (1U << I2C_CON_MASTER_MODE_SHIFT) + | (I2C_CON_SPEED_FAST << I2C_CON_SPEED_SHIFT) + | (1U << I2C_CON_IC_RESTART_EN_SHIFT) + | (1U << I2C_CON_IC_SLAVE_DISABLE_SHIFT) + | (1U << I2C_CON_TX_EMPTY_CTRL_SHIFT); +} + +/** + * @brief Program SCL timing for 100 kHz at 12 MHz clk_sys. + * + * Sets fast-mode SCL high/low counts, SDA hold time, and + * spike suppression length. + * + * @retval None + */ +static void i2c_config_timing(void) +{ + I2C1->FS_SCL_HCNT = I2C_FS_SCL_HCNT_VAL; + I2C1->FS_SCL_LCNT = I2C_FS_SCL_LCNT_VAL; + I2C1->FS_SPKLEN = I2C_FS_SPKLEN_VAL; + I2C1->SDA_HOLD = I2C_SDA_TX_HOLD_VAL; +} + +/** + * @brief Print a two-digit uppercase hex number over UART. + * @param val byte value to print (0x00-0xFF) + * @retval None + */ +static void print_hex8(uint8_t val) +{ + const char hex[] = "0123456789ABCDEF"; + char buf[3] = { hex[val >> 4], hex[val & 0x0F], '\0' }; + uart_puts(buf); +} + +/** + * @brief Print probe result: address if found, dashes if not, blank if reserved. + * @param addr 7-bit I2C address + * @retval None + */ +static void print_probe_result(uint8_t addr) +{ + if (addr < 0x08 || addr > 0x77) + { + uart_puts(" "); + } else if (i2c_probe(addr)) + { + print_hex8(addr); + uart_puts(" "); + } else { + uart_puts("-- "); + } +} + +/** + * @brief Print a single scan table entry for the given address. + * + * Prints the row header at 16-byte boundaries, then the address + * if a device responds, dashes if not, or blanks for reserved + * ranges. + * + * @param addr 7-bit I2C address (0x00-0x7F) + * @retval None + */ +static void print_scan_entry(uint8_t addr) +{ + if (addr % 16 == 0) { print_hex8(addr); uart_puts(": "); } + print_probe_result(addr); + if (addr % 16 == 15) + uart_puts("\r\n"); +} + +void i2c_release_reset(void) +{ + RESETS->RESET |= (1U << RESETS_RESET_I2C1_SHIFT); + RESETS->RESET &= ~(1U << RESETS_RESET_I2C1_SHIFT); + while (!(RESETS->RESET_DONE & (1U << RESETS_RESET_I2C1_SHIFT))) {} +} + +void i2c_init(void) +{ + i2c_config_pads(); + i2c_config_gpio(); + I2C1->ENABLE = 0U; + i2c_config_con(); + I2C1->TX_TL = 0U; + I2C1->RX_TL = 0U; + i2c_config_timing(); + I2C1->ENABLE = 1U; +} + +/** + * @brief Set the I2C1 target address for a probe transaction. + * @param addr 7-bit target address + * @retval None + */ +static void probe_set_target(uint8_t addr) +{ + I2C1->ENABLE = 0U; + I2C1->TAR = addr; + I2C1->ENABLE = 1U; +} + +/** + * @brief Issue a single read command with stop to probe the target. + * @retval None + */ +static void probe_send_read(void) +{ + (void)I2C1->CLR_TX_ABRT; + I2C1->DATA_CMD = (1U << I2C_DATA_CMD_CMD_SHIFT) | (1U << I2C_DATA_CMD_STOP_SHIFT); +} + +/** + * @brief Wait for probe response, checking for abort or RX data. + * @retval bool true if transaction was aborted + */ +static bool probe_wait_response(void) +{ + uint32_t timeout = I2C_TIMEOUT; + while (timeout > 0U) + { + if (I2C1->RAW_INTR_STAT & I2C_RAW_INTR_TX_ABRT) + return true; + if (I2C1->RXFLR) + return false; + timeout--; + } + return true; +} + +/** + * @brief Clean up after a probe: clear abort, stop, and drain RX. + * @param aborted true if the probe was aborted + * @retval None + */ +static void probe_cleanup(bool aborted) +{ + if (aborted) + (void)I2C1->CLR_TX_ABRT; + (void)I2C1->CLR_STOP_DET; + if (!aborted && I2C1->RXFLR) + (void)I2C1->DATA_CMD; +} + +bool i2c_probe(uint8_t addr) +{ + probe_set_target(addr); + probe_send_read(); + bool aborted = probe_wait_response(); + probe_cleanup(aborted); + return !aborted; +} + +void i2c_scan(void) +{ + uart_puts("\r\nI2C bus scan:\r\n"); + uart_puts(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n"); + for (uint8_t addr = 0; addr < 128; addr++) + { + print_scan_entry(addr); + } +} diff --git a/drivers/0x07_i2c_cbm/Src/rp2350_reset.c b/drivers/0x07_i2c_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x07_i2c_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x07_i2c_cbm/Src/rp2350_reset_handler.c b/drivers/0x07_i2c_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..62b58d2 --- /dev/null +++ b/drivers/0x07_i2c_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,80 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, then branches + * to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x07_i2c_cbm/Src/rp2350_stack.c b/drivers/0x07_i2c_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x07_i2c_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x07_i2c_cbm/Src/rp2350_uart.c b/drivers/0x07_i2c_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x07_i2c_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x07_i2c_cbm/Src/rp2350_xosc.c b/drivers/0x07_i2c_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..b33a71d --- /dev/null +++ b/drivers/0x07_i2c_cbm/Src/rp2350_xosc.c @@ -0,0 +1,54 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value &= ~CLK_PERI_CTRL_AUXSRC_MASK; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} + +void xosc_set_clk_ref(void) +{ + CLOCKS->CLK_REF_CTRL = CLK_REF_CTRL_SRC_XOSC; +} diff --git a/drivers/0x07_i2c_cbm/Src/vector_table.c b/drivers/0x07_i2c_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x07_i2c_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x07_i2c_cbm/build.log b/drivers/0x07_i2c_cbm/build.log new file mode 100644 index 0000000..f46ea5a Binary files /dev/null and b/drivers/0x07_i2c_cbm/build.log differ diff --git a/drivers/0x07_i2c_cbm/linker.ld b/drivers/0x07_i2c_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x07_i2c_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x07_i2c_rust/.cargo/config.toml b/drivers/0x07_i2c_rust/.cargo/config.toml new file mode 100644 index 0000000..1791d74 --- /dev/null +++ b/drivers/0x07_i2c_rust/.cargo/config.toml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2021–2024 The rp-rs Developers +# Copyright (c) 2021 rp-rs organization +# Copyright (c) 2025 Raspberry Pi Ltd. +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# You might want to make a similar file in your own repository if you are +# writing programs for Raspberry Silicon microcontrollers. +# + +[build] +target = "thumbv8m.main-none-eabihf" +# Set the default target to match the Cortex-M33 in the RP2350 +# target = "thumbv8m.main-none-eabihf" +# target = "thumbv6m-none-eabi" +# target = "riscv32imac-unknown-none-elf" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the hard-float ABI for Arm mode. +# +# The FPU is enabled by default, and float function arguments use FPU +# registers. +[target.thumbv8m.main-none-eabihf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as a linker script. +# This is usually provided by the cortex-m-rt crate, and by default the +# version in that crate will include a file called `memory.x` which describes +# the particular memory layout for your specific chip. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the soft-float ABI for RISC-V mode. +# +# Hazard 3 does not have an FPU and so float function arguments use integer +# registers. +[target.riscv32imac-unknown-none-elf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Trp235x_riscv.x also tells the linker to use +# `rp235x_riscv.x` as a linker script. This adds in RP2350 RISC-V specific +# things that the riscv-rt crate's `link.x` requires and then includes +# `link.x` automatically. This is the reverse of how we do it on Cortex-M. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x07_i2c_rust/.gitignore b/drivers/0x07_i2c_rust/.gitignore new file mode 100644 index 0000000..03381c1 --- /dev/null +++ b/drivers/0x07_i2c_rust/.gitignore @@ -0,0 +1,113 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,macos,windows,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux diff --git a/drivers/0x07_i2c_rust/.pico-rs b/drivers/0x07_i2c_rust/.pico-rs new file mode 100644 index 0000000..1b6702a --- /dev/null +++ b/drivers/0x07_i2c_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 \ No newline at end of file diff --git a/drivers/0x07_i2c_rust/.vscode/extensions.json b/drivers/0x07_i2c_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x07_i2c_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x07_i2c_rust/.vscode/launch.json b/drivers/0x07_i2c_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x07_i2c_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x07_i2c_rust/.vscode/settings.json b/drivers/0x07_i2c_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x07_i2c_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x07_i2c_rust/.vscode/tasks.json b/drivers/0x07_i2c_rust/.vscode/tasks.json new file mode 100644 index 0000000..4194af4 --- /dev/null +++ b/drivers/0x07_i2c_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/drivers/0x07_i2c_rust/Cargo.toml b/drivers/0x07_i2c_rust/Cargo.toml new file mode 100644 index 0000000..af4c097 --- /dev/null +++ b/drivers/0x07_i2c_rust/Cargo.toml @@ -0,0 +1,40 @@ +[package] +edition = "2024" +name = "i2c" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "i2c_lib" +path = "src/lib.rs" + +[[bin]] +name = "i2c" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +embedded-hal = "1.0.0" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x07_i2c_rust/LICENSE-APACHE b/drivers/0x07_i2c_rust/LICENSE-APACHE new file mode 100644 index 0000000..8d99cbc --- /dev/null +++ b/drivers/0x07_i2c_rust/LICENSE-APACHE @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2021–2024 The rp-rs Developers + Copyright (c) 2021 rp-rs organization + Copyright (c) 2025 Raspberry Pi Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/drivers/0x07_i2c_rust/LICENSE-MIT b/drivers/0x07_i2c_rust/LICENSE-MIT new file mode 100644 index 0000000..5369c70 --- /dev/null +++ b/drivers/0x07_i2c_rust/LICENSE-MIT @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2021–2024 The rp-rs Developers +Copyright (c) 2021 rp-rs organization +Copyright (c) 2025 Raspberry Pi Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + \ No newline at end of file diff --git a/drivers/0x07_i2c_rust/build.rs b/drivers/0x07_i2c_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x07_i2c_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x07_i2c_rust/rp2040.x b/drivers/0x07_i2c_rust/rp2040.x new file mode 100644 index 0000000..0cc665b --- /dev/null +++ b/drivers/0x07_i2c_rust/rp2040.x @@ -0,0 +1,91 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + /* + * Here we assume you have 2048 KiB of Flash. This is what the Pi Pico + * has, but your board may have more or less Flash and you should adjust + * this value to suit. + */ + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + /* + * RAM consists of 4 banks, SRAM0-SRAM3, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K + /* + * RAM banks 4 and 5 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k + + /* SRAM banks 0-3 can also be accessed directly. However, those ranges + alias with the RAM mapping, above. So don't use them at the same time! + SRAM0 : ORIGIN = 0x21000000, LENGTH = 64k + SRAM1 : ORIGIN = 0x21010000, LENGTH = 64k + SRAM2 : ORIGIN = 0x21020000, LENGTH = 64k + SRAM3 : ORIGIN = 0x21030000, LENGTH = 64k + */ +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader + * + * An executable block of code which sets up the QSPI interface for + * 'Execute-In-Place' (or XIP) mode. Also sends chip-specific commands to + * the external flash chip. + * + * Must go at the start of external flash, where the Boot ROM expects it. + */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 512 bytes of flash, + * where picotool can find it + */ + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x07_i2c_rust/rp2350.x b/drivers/0x07_i2c_rust/rp2350.x new file mode 100644 index 0000000..bda94d8 --- /dev/null +++ b/drivers/0x07_i2c_rust/rp2350.x @@ -0,0 +1,83 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 4K of flash + * where the Boot ROM (and picotool) can find it + */ + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + /* move .text to start /after/ the boot info */ + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + /* ### Boot ROM extra info + * + * Goes after everything in our program, so it can contain a signature. + */ + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + \ No newline at end of file diff --git a/drivers/0x07_i2c_rust/rp2350_riscv.x b/drivers/0x07_i2c_rust/rp2350_riscv.x new file mode 100644 index 0000000..84388aa --- /dev/null +++ b/drivers/0x07_i2c_rust/rp2350_riscv.x @@ -0,0 +1,259 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol is not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `32`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > FLASH + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + __start_block_addr = .; + KEEP(*(.start_block)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + . = ALIGN(4); + } > FLASH + + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > FLASH + + .data : ALIGN(32) + { + _sidata = LOADADDR(.data); + __sidata = LOADADDR(.data); + _sdata = .; + __sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(32); + _edata = .; + __edata = .; + } > RAM AT > FLASH + + .bss (NOLOAD) : ALIGN(32) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(32); + _ebss = .; + } > RAM + + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > RAM + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > RAM + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(start_to_end = __end_block_addr - __start_block_addr); +PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 32 == 0, " +ERROR(riscv-rt): the start of the RAM must be 32-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, " +BUG(riscv-rt): .data is not 32-byte aligned"); + +ASSERT(_sidata % 32 == 0, " +BUG(riscv-rt): the LMA of .data is not 32-byte aligned"); + +ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, " +BUG(riscv-rt): .bss is not 32-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(riscv-rt): The .text section must be placed inside the FLASH region. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/drivers/0x07_i2c_rust/src/board.rs b/drivers/0x07_i2c_rust/src/board.rs new file mode 100644 index 0000000..b7025ef --- /dev/null +++ b/drivers/0x07_i2c_rust/src/board.rs @@ -0,0 +1,335 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// I2C bus trait for device probing +use embedded_hal::i2c::I2c; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionI2C, FunctionNull, FunctionUart, Pin, PullDown, PullNone, PullUp}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// I2C bus speed in Hz (100 kHz standard mode). +pub(crate) const I2C_BAUD: u32 = 100_000; + +/// Delay between scan cycles in milliseconds. +pub(crate) const SCAN_DELAY_MS: u32 = 5_000; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Probe a 7-bit I2C address by attempting a 1-byte read. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address to probe. +/// +/// # Returns +/// +/// `true` if a device acknowledged, `false` otherwise. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `addr` - Device address. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +pub(crate) fn probe_addr(i2c: &mut impl I2c, addr: u8) -> bool { + let mut dummy = [0u8; 1]; + i2c.read(addr, &mut dummy).is_ok() +} + +/// Initialise all peripherals and run the I2C bus scanner demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + let mut i2c = init_i2c(pac.I2C1, p.gpio2, p.gpio3, &mut pac.RESETS, &clocks); + uart.write_full_blocking(b"I2C driver initialized: I2C1 @ 100000 Hz SDA=GPIO2 SCL=GPIO3\r\n"); + scan_loop(&uart, &mut i2c, &mut delay) +} + +/// Initialise I2C1 on SDA=GPIO2 / SCL=GPIO3. +/// +/// # Arguments +/// +/// * `i2c1` - PAC I2C1 peripheral singleton. +/// * `sda` - Default GPIO 2 pin (will be reconfigured for I2C). +/// * `scl` - Default GPIO 3 pin (will be reconfigured for I2C). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Configured I2C1 bus controller. +fn init_i2c( + i2c1: hal::pac::I2C1, + sda: Pin, + scl: Pin, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> impl I2c { + let sda = sda.reconfigure::(); + let scl = scl.reconfigure::(); + hal::I2C::i2c1( + i2c1, + sda, + scl, + I2C_BAUD.Hz(), + resets, + clocks.system_clock.freq(), + ) +} + +/// Run the I2C address scan loop forever. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `i2c` - Mutable reference to the I2C bus controller. +/// * `delay` - Mutable reference to the blocking delay provider. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `i2c` - The `i2c` parameter. +/// * `delay` - Delay value. +/// +/// # Returns +/// +/// A value of type `!`. +fn scan_loop(uart: &EnabledUart, i2c: &mut impl I2c, delay: &mut cortex_m::delay::Delay) -> ! { + let mut buf = [0u8; 80]; + loop { + let n = i2c_lib::i2c::format_scan_header(&mut buf); + uart.write_full_blocking(&buf[..n]); + scan_addresses(uart, i2c, &mut buf); + delay.delay_ms(SCAN_DELAY_MS); + } +} + +/// Scan all 128 addresses and print the formatted result. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `i2c` - Mutable reference to the I2C bus controller. +/// * `buf` - Scratch buffer for formatting output. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `i2c` - The `i2c` parameter. +/// * `buf` - The `buf` parameter. +fn scan_addresses(uart: &EnabledUart, i2c: &mut impl I2c, buf: &mut [u8; 80]) { + for addr in 0u8..128 { + let found = !i2c_lib::i2c::is_reserved(addr) && probe_addr(i2c, addr); + let n = i2c_lib::i2c::format_scan_entry(buf, addr, found); + uart.write_full_blocking(&buf[..n]); + } +} + diff --git a/drivers/0x07_i2c_rust/src/i2c.rs b/drivers/0x07_i2c_rust/src/i2c.rs new file mode 100644 index 0000000..a6d192f --- /dev/null +++ b/drivers/0x07_i2c_rust/src/i2c.rs @@ -0,0 +1,323 @@ +//! Implementation module +//! +//! **File:** `i2c.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// Lowest valid (non-reserved) 7-bit I2C address. +pub const SCAN_ADDR_MIN: u8 = 0x08; + +/// Highest valid (non-reserved) 7-bit I2C address. +pub const SCAN_ADDR_MAX: u8 = 0x77; + +/// Return `true` when `addr` falls in a reserved 7-bit I2C range. +/// +/// # Arguments +/// +/// * `addr` - 7-bit I2C address to check. +/// +/// # Returns +/// +/// `true` if the address is in the reserved low or high range. +/// +/// # Arguments +/// +/// * `addr` - Device address. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +pub fn is_reserved(addr: u8) -> bool { + addr < SCAN_ADDR_MIN || addr > SCAN_ADDR_MAX +} + +/// Convert a 4-bit value to its uppercase ASCII hex digit. +/// +/// # Arguments +/// +/// * `val` - Value in the range 0..=15. +/// +/// # Returns +/// +/// ASCII byte `b'0'`..`b'9'` or `b'A'`..`b'F'`. +/// +/// # Arguments +/// +/// * `val` - Value to use. +/// +/// # Returns +/// +/// An 8-bit unsigned integer value. +fn hex_digit(val: u8) -> u8 { + if val < 10 { + b'0' + val + } else { + b'A' + val - 10 + } +} + +/// Write the scan-table header into `buf` and return the byte count. +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 56 bytes). +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_scan_header(buf: &mut [u8]) -> usize { + let h = b"\r\nI2C bus scan:\r\n 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n"; + buf[..h.len()].copy_from_slice(h); + h.len() +} + +/// Format one cell of the 16-column scan table into `buf`. +/// +/// Prepends the row label when `addr` is at a 16-byte boundary and +/// appends `\r\n` when `addr` is the last column of a row. +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice for formatted output. +/// * `addr` - 7-bit I2C address being reported. +/// * `found` - `true` if a device acknowledged at this address. +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `addr` - Device address. +/// * `found` - The `found` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_scan_entry(buf: &mut [u8], addr: u8, found: bool) -> usize { + let mut pos = format_row_prefix(buf, addr); + pos += format_cell(&mut buf[pos..], addr, found); + if addr % 16 == 15 { + buf[pos] = b'\r'; + pos += 1; + buf[pos] = b'\n'; + pos += 1; + } + pos +} + +/// Write the row prefix ("XX: ") if addr is at a 16-byte boundary. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `addr` - Device address. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `addr` - Device address. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_row_prefix(buf: &mut [u8], addr: u8) -> usize { + if addr % 16 != 0 { + return 0; + } + buf[0] = hex_digit((addr >> 4) & 0x0F); + buf[1] = hex_digit(addr & 0x0F); + buf[2] = b':'; + buf[3] = b' '; + 4 +} + +/// Write a single cell: "XX " if found, "-- " if not, " " if reserved. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `addr` - Device address. +/// * `found` - The `found` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `addr` - Device address. +/// * `found` - The `found` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_cell(buf: &mut [u8], addr: u8, found: bool) -> usize { + let cell = cell_bytes(addr, found); + buf[..3].copy_from_slice(&cell); + 3 +} + +/// Return the 3-byte cell content for an I2C scan address. +/// +/// # Arguments +/// +/// * `addr` - Device address. +/// * `found` - The `found` parameter. +/// +/// # Returns +/// +/// A value of type `[u8; 3]`. +/// +/// # Arguments +/// +/// * `addr` - Device address. +/// * `found` - The `found` parameter. +/// +/// # Returns +/// +/// A value of type `[u8; 3]`. +fn cell_bytes(addr: u8, found: bool) -> [u8; 3] { + if is_reserved(addr) { + return [b' ', b' ', b' ']; + } + if found { + [hex_digit((addr >> 4) & 0x0F), hex_digit(addr & 0x0F), b' '] + } else { + [b'-', b'-', b' '] + } +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the is reserved below min operation. + #[test] + fn is_reserved_below_min() { + assert!(is_reserved(0x00)); + assert!(is_reserved(0x07)); + } + + /// Executes the is reserved at min boundary operation. + #[test] + fn is_reserved_at_min_boundary() { + assert!(!is_reserved(0x08)); + } + + /// Executes the is reserved mid range operation. + #[test] + fn is_reserved_mid_range() { + assert!(!is_reserved(0x50)); + } + + /// Executes the is reserved at max boundary operation. + #[test] + fn is_reserved_at_max_boundary() { + assert!(!is_reserved(0x77)); + } + + /// Executes the is reserved above max operation. + #[test] + fn is_reserved_above_max() { + assert!(is_reserved(0x78)); + assert!(is_reserved(0x7F)); + } + + /// Executes the format scan header content operation. + #[test] + fn format_scan_header_content() { + let mut buf = [0u8; 80]; + let n = format_scan_header(&mut buf); + let s = core::str::from_utf8(&buf[..n]).unwrap(); + assert!(s.starts_with("\r\nI2C bus scan:\r\n")); + assert!(s.contains("0 1 2 3")); + assert!(s.contains("D E F\r\n")); + } + + /// Executes the format scan entry reserved row start operation. + #[test] + fn format_scan_entry_reserved_row_start() { + let mut buf = [0u8; 16]; + let n = format_scan_entry(&mut buf, 0x00, false); + assert_eq!(&buf[..n], b"00: "); + } + + /// Executes the format scan entry found operation. + #[test] + fn format_scan_entry_found() { + let mut buf = [0u8; 16]; + let n = format_scan_entry(&mut buf, 0x51, true); + assert_eq!(&buf[..n], b"51 "); + } + + /// Executes the format scan entry not found operation. + #[test] + fn format_scan_entry_not_found() { + let mut buf = [0u8; 16]; + let n = format_scan_entry(&mut buf, 0x51, false); + assert_eq!(&buf[..n], b"-- "); + } + + /// Executes the format scan entry row start valid operation. + #[test] + fn format_scan_entry_row_start_valid() { + let mut buf = [0u8; 16]; + let n = format_scan_entry(&mut buf, 0x10, false); + assert_eq!(&buf[..n], b"10: -- "); + } + + /// Executes the format scan entry row end operation. + #[test] + fn format_scan_entry_row_end() { + let mut buf = [0u8; 16]; + let n = format_scan_entry(&mut buf, 0x1F, false); + assert_eq!(&buf[..n], b"-- \r\n"); + } + + /// Executes the hex digit values operation. + #[test] + fn hex_digit_values() { + assert_eq!(hex_digit(0), b'0'); + assert_eq!(hex_digit(9), b'9'); + assert_eq!(hex_digit(10), b'A'); + assert_eq!(hex_digit(15), b'F'); + } +} diff --git a/drivers/0x07_i2c_rust/src/lib.rs b/drivers/0x07_i2c_rust/src/lib.rs new file mode 100644 index 0000000..f9f956e --- /dev/null +++ b/drivers/0x07_i2c_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod i2c; diff --git a/drivers/0x07_i2c_rust/src/main.rs b/drivers/0x07_i2c_rust/src/main.rs new file mode 100644 index 0000000..c3f1647 --- /dev/null +++ b/drivers/0x07_i2c_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// I2C driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the I2C bus scanner demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"I2C Bus Scanner Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x08_lcd1602/.vscode/.vscode/c_cpp_properties.json b/drivers/0x08_lcd1602/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x08_lcd1602/.vscode/.vscode/cmake-kits.json b/drivers/0x08_lcd1602/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x08_lcd1602/.vscode/.vscode/extensions.json b/drivers/0x08_lcd1602/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602/.vscode/.vscode/launch.json b/drivers/0x08_lcd1602/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x08_lcd1602/.vscode/.vscode/settings.json b/drivers/0x08_lcd1602/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x08_lcd1602/.vscode/.vscode/tasks.json b/drivers/0x08_lcd1602/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x08_lcd1602/.vscode/c_cpp_properties.json b/drivers/0x08_lcd1602/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x08_lcd1602/.vscode/cmake-kits.json b/drivers/0x08_lcd1602/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x08_lcd1602/.vscode/extensions.json b/drivers/0x08_lcd1602/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602/.vscode/launch.json b/drivers/0x08_lcd1602/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x08_lcd1602/.vscode/settings.json b/drivers/0x08_lcd1602/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x08_lcd1602/.vscode/tasks.json b/drivers/0x08_lcd1602/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x08_lcd1602/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x08_lcd1602/0x08_lcd1602.c b/drivers/0x08_lcd1602/0x08_lcd1602.c new file mode 100644 index 0000000..b75c1b3 --- /dev/null +++ b/drivers/0x08_lcd1602/0x08_lcd1602.c @@ -0,0 +1,89 @@ +/** + * @file 0x08_lcd1602.c + * @brief HD44780 16x2 LCD (PCF8574 I2C backpack) driver for the Raspberry Pi Pico 2 + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * This driver demonstrates how to drive a 16x2 HD44780 LCD connected via a + * PCF8574 I2C backpack using the lcd1602.c/lcd1602.h driver. Line 0 shows + * a static title and line 1 displays a live up-counter that increments every + * second. The counter value is also printed over UART for debugging. + * + * Wiring: + * GPIO2 (SDA) -> PCF8574 backpack SDA (4.7 kohm pull-up to 3.3 V) + * GPIO3 (SCL) -> PCF8574 backpack SCL (4.7 kohm pull-up to 3.3 V) + * 3.3V or 5V -> PCF8574 backpack VCC + * GND -> PCF8574 backpack GND + */ + +#include +#include +#include "pico/stdlib.h" +#include "lcd1602.h" + +/** @brief I2C port number (0 or 1) */ +#define I2C_PORT 1 +/** @brief GPIO pin for I2C SDA */ +#define I2C_SDA_PIN 2 +/** @brief GPIO pin for I2C SCL */ +#define I2C_SCL_PIN 3 +/** @brief I2C bus clock rate in Hz */ +#define I2C_BAUD_HZ 100000 +/** @brief I2C address of the PCF8574 LCD backpack */ +#define LCD_I2C_ADDR 0x27 + +/** + * @brief Initialize the LCD, display the title, and log over UART + */ +static void setup_display(void) { + lcd_init(I2C_PORT, I2C_SDA_PIN, I2C_SCL_PIN, I2C_BAUD_HZ, LCD_I2C_ADDR, 4, 0x08); + lcd_set_cursor(0, 0); + lcd_puts("Reverse Eng."); + printf("LCD 1602 driver initialized at I2C addr 0x%02X\r\n", LCD_I2C_ADDR); +} + +/** + * @brief Format and display the next counter value on LCD line 1 + * + * @param count Pointer to the running counter (post-incremented) + */ +static void update_counter(uint32_t *count) { + char buf[17]; + snprintf(buf, sizeof(buf), "Count: %6lu", (*count)++); + lcd_set_cursor(1, 0); + lcd_puts(buf); + printf("%s\r\n", buf); + sleep_ms(1000); +} + +int main(void) { + stdio_init_all(); + setup_display(); + uint32_t count = 0; + while (true) + update_counter(&count); +} diff --git a/drivers/0x08_lcd1602/CMakeLists.txt b/drivers/0x08_lcd1602/CMakeLists.txt new file mode 100644 index 0000000..7767c8b --- /dev/null +++ b/drivers/0x08_lcd1602/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x08_lcd1602 C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x08_lcd1602 0x08_lcd1602.c lcd1602.c) + +pico_set_program_name(0x08_lcd1602 "0x08_lcd1602") +pico_set_program_version(0x08_lcd1602 "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x08_lcd1602 1) +pico_enable_stdio_usb(0x08_lcd1602 0) + +# Add the standard library to the build +target_link_libraries(0x08_lcd1602 + pico_stdlib + hardware_i2c) + +# Add the standard include files to the build +target_include_directories(0x08_lcd1602 PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x08_lcd1602) diff --git a/drivers/0x08_lcd1602/lcd1602.c b/drivers/0x08_lcd1602/lcd1602.c new file mode 100644 index 0000000..2ccfa06 --- /dev/null +++ b/drivers/0x08_lcd1602/lcd1602.c @@ -0,0 +1,186 @@ +/** + * @file lcd1602.c + * @brief Implementation of PCF8574-backed HD44780 (16x2) LCD driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lcd1602.h" +#include +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" + +/** + * @brief Map an I2C port number to its hardware instance pointer + * + * @param port I2C port number (0 for i2c0, 1 for i2c1) + * @return i2c_inst_t* Pointer to the corresponding I2C hardware instance + */ +static i2c_inst_t *get_i2c_inst(uint8_t port) { + return port == 0 ? i2c0 : i2c1; +} + +/** @brief I2C instance pointer for the LCD */ +static i2c_inst_t *lcd_i2c = NULL; +/** @brief I2C address of the PCF8574 backpack */ +static uint8_t lcd_addr = 0x27; +/** @brief Bit shift for 4-bit nibble position */ +static int lcd_nibble_shift = 4; +/** @brief PCF8574 bit mask controlling the backlight */ +static uint8_t lcd_backlight_mask = 0x08; + +/* PCF8574 -> LCD control pins */ +/** @brief PCF8574 bit mask for Register Select */ +#define PIN_RS 0x01 +/** @brief PCF8574 bit mask for Read/Write */ +#define PIN_RW 0x02 +/** @brief PCF8574 bit mask for Enable */ +#define PIN_EN 0x04 + +/** + * @brief Write one raw byte to the PCF8574 expander over I2C + * + * @param data Output byte to send to the expander + */ +static void pcf_write_byte(uint8_t data) { + if (!lcd_i2c) return; + i2c_write_blocking(lcd_i2c, lcd_addr, &data, 1, false); +} + +/** + * @brief Toggle EN to latch a nibble into the LCD controller + * + * @param data Current control/data bus byte (with RS and backlight already set) + */ +static void pcf_pulse_enable(uint8_t data) { + pcf_write_byte(data | PIN_EN); + sleep_us(1); + pcf_write_byte(data & ~PIN_EN); + sleep_us(50); +} + +/** + * @brief Write one 4-bit nibble to the LCD + * + * @param nibble Lower 4 bits to write + * @param mode 0 for command, non-zero for character data + */ +static void lcd_write4(uint8_t nibble, uint8_t mode) { + uint8_t data = (nibble & 0x0F) << lcd_nibble_shift; + data |= mode ? PIN_RS : 0; + data |= lcd_backlight_mask; + pcf_pulse_enable(data); +} + +/** + * @brief Send one full 8-bit command/data value as two nibbles + * + * @param value Byte to send to the LCD + * @param mode 0 for command, non-zero for character data + */ +static void lcd_send(uint8_t value, uint8_t mode) { + lcd_write4((value >> 4) & 0x0F, mode); + lcd_write4(value & 0x0F, mode); +} + +/** + * @brief Store LCD driver configuration in module-level state + * + * @param i2c_port I2C port number (0 or 1) + * @param pcf_addr 7-bit PCF8574 address + * @param nibble_shift Bit shift for 4-bit nibbles + * @param backlight_mask Backlight control bit mask + */ +static void lcd_store_config(uint8_t i2c_port, uint8_t pcf_addr, + int nibble_shift, uint8_t backlight_mask) { + lcd_i2c = get_i2c_inst(i2c_port); + lcd_addr = pcf_addr; + lcd_nibble_shift = nibble_shift; + lcd_backlight_mask = backlight_mask; +} + +/** + * @brief Execute the HD44780 4-bit mode power-on reset sequence + */ +static void lcd_hd44780_reset(void) { + lcd_write4(0x03, 0); + sleep_ms(5); + lcd_write4(0x03, 0); + sleep_us(150); + lcd_write4(0x03, 0); + sleep_us(150); + lcd_write4(0x02, 0); + sleep_us(150); +} + +/** + * @brief Send post-reset configuration commands to the HD44780 + * + * Sets 4-bit mode with 2 display lines, turns the display on with + * cursor hidden, clears the screen, and selects left-to-right entry mode. + */ +static void lcd_hd44780_configure(void) { + lcd_send(0x28, 0); + lcd_send(0x0C, 0); + lcd_send(0x01, 0); + sleep_ms(2); + lcd_send(0x06, 0); +} + +void lcd_i2c_init(uint8_t i2c_port, uint8_t pcf_addr, int nibble_shift, + uint8_t backlight_mask) { + lcd_store_config(i2c_port, pcf_addr, nibble_shift, backlight_mask); + lcd_hd44780_reset(); + lcd_hd44780_configure(); +} + +void lcd_init(uint8_t i2c_port, uint32_t sda_pin, uint32_t scl_pin, + uint32_t baud_hz, uint8_t pcf_addr, int nibble_shift, + uint8_t backlight_mask) { + i2c_inst_t *i2c = get_i2c_inst(i2c_port); + i2c_init(i2c, baud_hz); + gpio_set_function(sda_pin, GPIO_FUNC_I2C); + gpio_set_function(scl_pin, GPIO_FUNC_I2C); + gpio_pull_up(sda_pin); + gpio_pull_up(scl_pin); + lcd_i2c_init(i2c_port, pcf_addr, nibble_shift, backlight_mask); +} + +void lcd_clear(void) { + lcd_send(0x01, 0); + sleep_ms(2); +} + +void lcd_set_cursor(int line, int position) { + const uint8_t row_offsets[] = {0x00, 0x40}; + if (line > 1) line = 1; + lcd_send(0x80 | (position + row_offsets[line]), 0); +} + +void lcd_puts(const char *s) { + while (*s) + lcd_send((uint8_t)*s++, 1); +} diff --git a/drivers/0x08_lcd1602/lcd1602.h b/drivers/0x08_lcd1602/lcd1602.h new file mode 100644 index 0000000..d99ad8f --- /dev/null +++ b/drivers/0x08_lcd1602/lcd1602.h @@ -0,0 +1,93 @@ +/** + * @file lcd1602.h + * @brief Header for PCF8574-backed HD44780 (16x2) LCD driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LCD1602_H +#define LCD1602_H + +#include +#include + +/** + * @brief Initialize I2C bus and the LCD driver in one call + * + * Configures the I2C peripheral at the given baud rate, assigns GPIO + * alternate functions for SDA and SCL with internal pull-ups, and then + * performs the full HD44780 4-bit initialization sequence through the + * PCF8574 backpack. This is a convenience wrapper around lcd_i2c_init(). + * + * @param i2c_port I2C port number (0 for i2c0, 1 for i2c1) + * @param sda_pin GPIO pin number for SDA + * @param scl_pin GPIO pin number for SCL + * @param baud_hz I2C clock rate in Hz (e.g. 100000) + * @param pcf_addr PCF8574 I2C address (commonly 0x27 or 0x3F) + * @param nibble_shift Bit shift applied to 4-bit nibbles (commonly 4 or 0) + * @param backlight_mask PCF8574 bit mask that controls the backlight + */ +void lcd_init(uint8_t i2c_port, uint32_t sda_pin, uint32_t scl_pin, + uint32_t baud_hz, uint8_t pcf_addr, int nibble_shift, + uint8_t backlight_mask); + +/** + * @brief Initialize the LCD driver over I2C + * + * Configures the internal driver state and performs the HD44780 initialization + * sequence. The driver does not configure I2C pins or call i2c_init; that + * must be done by the caller prior to calling this function. + * + * @param i2c_port I2C port number (0 for i2c0, 1 for i2c1) + * @param pcf_addr PCF8574 I2C address (commonly 0x27 or 0x3F) + * @param nibble_shift Bit shift applied to 4-bit nibbles (commonly 4 or 0) + * @param backlight_mask PCF8574 bit mask that controls the backlight + */ +void lcd_i2c_init(uint8_t i2c_port, uint8_t pcf_addr, int nibble_shift, uint8_t backlight_mask); + +/** + * @brief Clear the LCD display + * + * Clears the display and returns the cursor to the home position. This + * call blocks for the duration required by the HD44780 controller. + */ +void lcd_clear(void); + +/** + * @brief Set the cursor position + * + * @param line Line number (0 or 1) + * @param position Column (0..15) + */ +void lcd_set_cursor(int line, int position); + +/** + * @brief Write a null-terminated string to the display + * + * @param s The string to write (ASCII) + */ +void lcd_puts(const char *s); + +#endif // LCD1602_H diff --git a/drivers/0x08_lcd1602/pico_sdk_import.cmake b/drivers/0x08_lcd1602/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x08_lcd1602/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x08_lcd1602_cbm/.vscode/c_cpp_properties.json b/drivers/0x08_lcd1602_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602_cbm/.vscode/extensions.json b/drivers/0x08_lcd1602_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602_cbm/.vscode/launch.json b/drivers/0x08_lcd1602_cbm/.vscode/launch.json new file mode 100644 index 0000000..5ad6e81 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/lcd1602.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDir": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDir": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602_cbm/.vscode/settings.json b/drivers/0x08_lcd1602_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602_cbm/.vscode/tasks.json b/drivers/0x08_lcd1602_cbm/.vscode/tasks.json new file mode 100644 index 0000000..dc73364 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/lcd1602.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/lcd1602.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602_cbm/Inc/rp2350.h b/drivers/0x08_lcd1602_cbm/Inc/rp2350.h new file mode 100644 index 0000000..1438808 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Inc/rp2350.h @@ -0,0 +1,335 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define I2C1_BASE 0x40098000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[12]; // GPOUT0..GPOUT3 registers Address offset: 0x00-0x2C + __IO uint32_t CLK_REF_CTRL; // Reference clock control Address offset: 0x30 + __IO uint32_t RESERVED1[5]; // CLK_REF_DIV..CLK_SYS_SELECTED Address offset: 0x34-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief I2C (Synopsys DW APB I2C Controller) + */ +typedef struct +{ + __IO uint32_t CON; // Control register Address offset: 0x00 + __IO uint32_t TAR; // Target address Address offset: 0x04 + __IO uint32_t SAR; // Slave address Address offset: 0x08 + __IO uint32_t RESERVED0; // Padding Address offset: 0x0C + __IO uint32_t DATA_CMD; // Rx/Tx data buffer and command Address offset: 0x10 + __IO uint32_t SS_SCL_HCNT; // Standard speed SCL high count Address offset: 0x14 + __IO uint32_t SS_SCL_LCNT; // Standard speed SCL low count Address offset: 0x18 + __IO uint32_t FS_SCL_HCNT; // Fast mode SCL high count Address offset: 0x1C + __IO uint32_t FS_SCL_LCNT; // Fast mode SCL low count Address offset: 0x20 + __IO uint32_t RESERVED1[2]; // Padding Address offset: 0x24-0x28 + __IO uint32_t INTR_STAT; // Interrupt status (RO) Address offset: 0x2C + __IO uint32_t INTR_MASK; // Interrupt mask Address offset: 0x30 + __IO uint32_t RAW_INTR_STAT; // Raw interrupt status (RO) Address offset: 0x34 + __IO uint32_t RX_TL; // Receive FIFO threshold level Address offset: 0x38 + __IO uint32_t TX_TL; // Transmit FIFO threshold level Address offset: 0x3C + __IO uint32_t CLR_INTR; // Clear combined interrupt (RO) Address offset: 0x40 + __IO uint32_t CLR_RX_UNDER; // Clear RX_UNDER interrupt (RO) Address offset: 0x44 + __IO uint32_t CLR_RX_OVER; // Clear RX_OVER interrupt (RO) Address offset: 0x48 + __IO uint32_t CLR_TX_OVER; // Clear TX_OVER interrupt (RO) Address offset: 0x4C + __IO uint32_t CLR_RD_REQ; // Clear RD_REQ interrupt (RO) Address offset: 0x50 + __IO uint32_t CLR_TX_ABRT; // Clear TX_ABRT interrupt (RO) Address offset: 0x54 + __IO uint32_t CLR_RX_DONE; // Clear RX_DONE interrupt (RO) Address offset: 0x58 + __IO uint32_t CLR_ACTIVITY; // Clear ACTIVITY interrupt (RO) Address offset: 0x5C + __IO uint32_t CLR_STOP_DET; // Clear STOP_DET interrupt (RO) Address offset: 0x60 + __IO uint32_t CLR_START_DET; // Clear START_DET interrupt (RO) Address offset: 0x64 + __IO uint32_t CLR_GEN_CALL; // Clear GEN_CALL interrupt (RO) Address offset: 0x68 + __IO uint32_t ENABLE; // I2C enable Address offset: 0x6C + __IO uint32_t STATUS; // I2C status (RO) Address offset: 0x70 + __IO uint32_t TXFLR; // Transmit FIFO level (RO) Address offset: 0x74 + __IO uint32_t RXFLR; // Receive FIFO level (RO) Address offset: 0x78 + __IO uint32_t SDA_HOLD; // SDA hold time Address offset: 0x7C + __IO uint32_t TX_ABRT_SRC; // Transmit abort source (RO) Address offset: 0x80 + __IO uint32_t RESERVED2; // SLV_DATA_NACK_ONLY Address offset: 0x84 + __IO uint32_t DMA_CR; // DMA control Address offset: 0x88 + __IO uint32_t DMA_TDLR; // DMA transmit data level Address offset: 0x8C + __IO uint32_t DMA_RDLR; // DMA receive data level Address offset: 0x90 + __IO uint32_t SDA_SETUP; // SDA setup time Address offset: 0x94 + __IO uint32_t ACK_GEN_CALL; // ACK general call Address offset: 0x98 + __IO uint32_t ENABLE_STATUS; // I2C enable status (RO) Address offset: 0x9C + __IO uint32_t FS_SPKLEN; // Fast mode spike suppression limit Address offset: 0xA0 +} I2C_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLK_REF bit definitions + */ +#define CLK_REF_CTRL_SRC_XOSC 2U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_MASK (0x07U << CLK_PERI_CTRL_AUXSRC_SHIFT) +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_I2C1_SHIFT 5U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_I2C 0x03U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U +#define IO_BANK0_CTRL_FUNCSEL_NULL 0x1FU + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U +#define PADS_BANK0_PUE_SHIFT 3U + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief GPIO pin definitions + */ +#define I2C_SDA_PIN 2U +#define I2C_SCL_PIN 3U + +/** + * @brief I2C CON register bit definitions + */ +#define I2C_CON_MASTER_MODE_SHIFT 0U +#define I2C_CON_SPEED_SHIFT 1U +#define I2C_CON_SPEED_FAST 2U +#define I2C_CON_IC_RESTART_EN_SHIFT 5U +#define I2C_CON_IC_SLAVE_DISABLE_SHIFT 6U +#define I2C_CON_TX_EMPTY_CTRL_SHIFT 8U + +/** + * @brief I2C DATA_CMD register bit definitions + */ +#define I2C_DATA_CMD_CMD_SHIFT 8U +#define I2C_DATA_CMD_STOP_SHIFT 9U +#define I2C_DATA_CMD_RESTART_SHIFT 10U + +/** + * @brief I2C RAW_INTR_STAT register bit masks + */ +#define I2C_RAW_INTR_TX_EMPTY (1U << 4) +#define I2C_RAW_INTR_TX_ABRT (1U << 6) +#define I2C_RAW_INTR_STOP_DET (1U << 9) + +/** + * @brief I2C TX_ABRT_SOURCE bit definitions + */ +#define I2C_TX_ABRT_7B_ADDR_NOACK (1U << 0) + +/** + * @brief I2C STATUS register bit definitions + */ +#define I2C_STATUS_TFNF_SHIFT 1U +#define I2C_STATUS_RFNE_SHIFT 3U + +/** + * @brief I2C clock timing for 100 kHz at 12 MHz clk_sys + */ +#define I2C_SYS_CLK_HZ 12000000U +#define I2C_BAUD_HZ 100000U +#define I2C_FS_SCL_HCNT_VAL 48U +#define I2C_FS_SCL_LCNT_VAL 72U +#define I2C_SDA_TX_HOLD_VAL 4U +#define I2C_FS_SPKLEN_VAL 4U + +/** + * @brief I2C timeout + */ +#define I2C_TIMEOUT 1000000U + +/** + * @brief LCD1602 PCF8574 I2C address + */ +#define LCD_I2C_ADDR 0x27U + +/** + * @brief PCF8574 -> HD44780 control pin mappings + */ +#define LCD_PIN_RS 0x01U +#define LCD_PIN_RW 0x02U +#define LCD_PIN_EN 0x04U +#define LCD_BACKLIGHT 0x08U +#define LCD_NIBBLE_SHIFT 4U + +/** + * @brief HD44780 command values + */ +#define LCD_CMD_CLEAR 0x01U +#define LCD_CMD_FUNCTION_SET_4BIT 0x28U +#define LCD_CMD_DISPLAY_ON 0x0CU +#define LCD_CMD_ENTRY_MODE 0x06U +#define LCD_CMD_SET_DDRAM 0x80U + +/** + * @brief HD44780 DDRAM row offsets + */ +#define LCD_ROW0_OFFSET 0x00U +#define LCD_ROW1_OFFSET 0x40U + +#endif /* __RP2350_H */ diff --git a/drivers/0x08_lcd1602_cbm/Inc/rp2350_delay.h b/drivers/0x08_lcd1602_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..79db58f --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Inc/rp2350_delay.h @@ -0,0 +1,51 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond and microsecond busy-wait delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +/** + * @brief Delay for the specified number of microseconds. + * @param us number of microseconds to delay + * @retval None + */ +void delay_us(uint32_t us); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x08_lcd1602_cbm/Inc/rp2350_i2c.h b/drivers/0x08_lcd1602_cbm/Inc/rp2350_i2c.h new file mode 100644 index 0000000..459c04d --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Inc/rp2350_i2c.h @@ -0,0 +1,76 @@ +/** + * @file rp2350_i2c.h + * @brief I2C driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * I2C1 master-mode driver at 100 kHz on SDA=GPIO2 / SCL=GPIO3. + * Provides init, target selection, and single-byte write. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_I2C_H +#define __RP2350_I2C_H + +#include "rp2350.h" + +/** + * @brief Release I2C1 from reset and wait for completion. + * @retval None + */ +void i2c_release_reset(void); + +/** + * @brief Initialize I2C1 as a 100 kHz master on SDA=GPIO2 / SCL=GPIO3. + * + * Configures GPIO pads with pull-ups, sets FUNCSEL to I2C, + * programs SCL timing for 100 kHz at 12 MHz clk_sys, and + * enables the controller in master mode with 7-bit addressing. + * + * @retval None + */ +void i2c_init(void); + +/** + * @brief Set the I2C1 target slave address. + * + * Disables the controller, writes the 7-bit address into the + * TAR register, and re-enables the controller. + * + * @param addr 7-bit I2C address + * @retval None + */ +void i2c_set_target(uint8_t addr); + +/** + * @brief Write one byte to the current target address with a STOP. + * + * Sends a single data byte over I2C1 with the STOP condition. + * Blocks until the transfer completes or an abort is detected. + * + * @param data byte to transmit + * @retval None + */ +void i2c_write_byte(uint8_t data); + +#endif /* __RP2350_I2C_H */ diff --git a/drivers/0x08_lcd1602_cbm/Inc/rp2350_lcd1602.h b/drivers/0x08_lcd1602_cbm/Inc/rp2350_lcd1602.h new file mode 100644 index 0000000..efc9a8e --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Inc/rp2350_lcd1602.h @@ -0,0 +1,70 @@ +/** + * @file rp2350_lcd1602.h + * @brief LCD1602 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * HD44780 16x2 LCD driver via PCF8574 I2C backpack. Uses I2C1 + * to communicate with the PCF8574 expander at address 0x27 in + * 4-bit mode with backlight control. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_LCD1602_H +#define __RP2350_LCD1602_H + +#include "rp2350.h" + +/** + * @brief Initialize the LCD in 4-bit mode via the PCF8574 backpack. + * + * Sets the I2C target address to LCD_I2C_ADDR, performs the + * HD44780 power-on reset sequence, and configures the display + * for 4-bit, 2-line, 5x8 font with backlight on. + * + * @retval None + */ +void lcd_init(void); + +/** + * @brief Clear the LCD display and return cursor to home. + * @retval None + */ +void lcd_clear(void); + +/** + * @brief Set the cursor position on the display. + * @param line line number (0 or 1) + * @param position column number (0-15) + * @retval None + */ +void lcd_set_cursor(uint8_t line, uint8_t position); + +/** + * @brief Write a null-terminated string to the display. + * @param str pointer to the string to write (ASCII) + * @retval None + */ +void lcd_puts(const char *str); + +#endif /* __RP2350_LCD1602_H */ diff --git a/drivers/0x08_lcd1602_cbm/Inc/rp2350_reset.h b/drivers/0x08_lcd1602_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x08_lcd1602_cbm/Inc/rp2350_reset_handler.h b/drivers/0x08_lcd1602_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x08_lcd1602_cbm/Inc/rp2350_stack.h b/drivers/0x08_lcd1602_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x08_lcd1602_cbm/Inc/rp2350_uart.h b/drivers/0x08_lcd1602_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x08_lcd1602_cbm/Inc/rp2350_xosc.h b/drivers/0x08_lcd1602_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..80fd75d --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,55 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +/** + * @brief Switch CLK_REF source to XOSC for a stable 12 MHz clk_sys. + * @retval None + */ +void xosc_set_clk_ref(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x08_lcd1602_cbm/Makefile b/drivers/0x08_lcd1602_cbm/Makefile new file mode 100644 index 0000000..b06b732 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Makefile @@ -0,0 +1,86 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C LCD1602 driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = lcd1602 + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_i2c.c \ + $(SRC_DIR)/rp2350_lcd1602.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x08_lcd1602_cbm/Src/image_def.c b/drivers/0x08_lcd1602_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x08_lcd1602_cbm/Src/main.c b/drivers/0x08_lcd1602_cbm/Src/main.c new file mode 100644 index 0000000..5f7c405 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/main.c @@ -0,0 +1,163 @@ +/** + * @file main.c + * @brief LCD1602 demonstration: static title and live counter display. + * @author Kevin Thomas + * @date 2026 + * + * Drives a 16x2 HD44780 LCD via a PCF8574 I2C backpack. Line 0 + * shows a static title ("Reverse Eng.") and line 1 displays a + * counter that increments every second. The counter value is + * also printed over UART for debugging. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * GPIO2 -> PCF8574 backpack SDA (4.7 kohm pull-up to 3.3 V) + * GPIO3 -> PCF8574 backpack SCL (4.7 kohm pull-up to 3.3 V) + * 3.3V -> PCF8574 backpack VCC + * GND -> PCF8574 backpack GND + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_i2c.h" +#include "rp2350_lcd1602.h" +#include "rp2350_uart.h" +#include "rp2350_xosc.h" +#include "rp2350_delay.h" + +/** @brief Delay between LCD counter updates in milliseconds */ +#define COUNT_DELAY_MS 1000 + +/** + * @brief Copy reversed digits from tmp into buf in correct order. + * @param tmp reversed digit buffer + * @param len number of digits + * @param buf output buffer + * @retval None + */ +static void reverse_copy(const char *tmp, int len, char *buf) +{ + for (int j = 0; j < len; j++) + buf[j] = tmp[len - 1 - j]; + buf[len] = '\0'; +} + +/** + * @brief Convert an unsigned 32-bit integer to a decimal string. + * + * Writes at most 10 decimal digits plus a null terminator + * into the provided buffer. The buffer must be at least + * 11 bytes. + * + * @param val value to convert + * @param buf destination buffer (minimum 11 bytes) + * @retval None + */ +static void uint_to_str(uint32_t val, char *buf) +{ + char tmp[11]; + int i = 0; + if (val == 0) { buf[0] = '0'; buf[1] = '\0'; return; } + while (val > 0) + { + tmp[i++] = (char)('0' + (val % 10)); + val /= 10; + } + reverse_copy(tmp, i, buf); +} + +/** + * @brief Copy a null-terminated string into buf at a given offset. + * @param dst destination buffer + * @param off starting offset in destination + * @param src source string + * @retval int new offset past the copied characters + */ +static int copy_str(char *dst, int off, const char *src) +{ + while (*src) + dst[off++] = *src++; + return off; +} + +/** + * @brief Format "Count: " followed by a right-justified counter value. + * + * Writes the formatted string into a 17-byte buffer and pads + * with spaces to fill the 16-column display width. + * + * @param count current counter value + * @param buf destination buffer (minimum 17 bytes) + * @retval None + */ +static void format_counter(uint32_t count, char *buf) +{ + char num[11]; + uint_to_str(count, num); + int i = copy_str(buf, 0, "Count: "); + i = copy_str(buf, i, num); + while (i < 16) buf[i++] = ' '; + buf[i] = '\0'; +} + +/** + * @brief Initialize clocks, I2C, LCD, UART, and display the static title. + * @retval None + */ +static void lcd_setup(void) +{ + xosc_set_clk_ref(); + i2c_release_reset(); + i2c_init(); + lcd_init(); + uart_puts("LCD 1602 driver initialized at I2C addr 0x27\r\n"); + lcd_set_cursor(0, 0); + lcd_puts("Reverse Eng."); +} + +/** + * @brief Format, display, and print the current counter value. + * @param count current counter value + * @retval None + */ +static void display_count(uint32_t count) +{ + char buf[17]; + format_counter(count, buf); + lcd_set_cursor(1, 0); + lcd_puts(buf); + uart_puts(buf); + uart_puts("\r\n"); +} + +int main(void) +{ + uint32_t count = 0; + lcd_setup(); + while (1) + { + display_count(count); + count++; + delay_ms(COUNT_DELAY_MS); + } +} diff --git a/drivers/0x08_lcd1602_cbm/Src/rp2350_delay.c b/drivers/0x08_lcd1602_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..65b0821 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/rp2350_delay.c @@ -0,0 +1,64 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond and microsecond delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} + +void delay_us(uint32_t us) +{ + if (us == 0) + return; + __asm__ volatile ( + "mov r4, #4\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (us) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x08_lcd1602_cbm/Src/rp2350_i2c.c b/drivers/0x08_lcd1602_cbm/Src/rp2350_i2c.c new file mode 100644 index 0000000..5777b64 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/rp2350_i2c.c @@ -0,0 +1,169 @@ +/** + * @file rp2350_i2c.c + * @brief RP2350 I2C1 master-mode driver implementation. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal driver for the RP2350 Synopsys DW APB I2C controller. + * Configures I2C1 at 100 kHz on SDA=GPIO2 / SCL=GPIO3 with + * internal pull-ups. Provides target selection and single-byte + * write. All register accesses verified against the RP2350 + * datasheet (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_i2c.h" + +/** + * @brief Configure GPIO2 and GPIO3 pads for I2C (input enabled, pull-up). + * + * Clears pad isolation, enables input, enables pull-up, and sets + * drive strength to 4 mA on both SDA and SCL pins. + * + * @retval None + */ +static void i2c_config_pads(void) +{ + uint32_t pad_val = (1U << PADS_BANK0_IE_SHIFT) | (1U << PADS_BANK0_PUE_SHIFT) | (1U << 4); + PADS_BANK0->GPIO[I2C_SDA_PIN] = pad_val; + PADS_BANK0->GPIO[I2C_SCL_PIN] = pad_val; +} + +/** + * @brief Set GPIO2 and GPIO3 IO mux function to I2C (FUNCSEL=3). + * @retval None + */ +static void i2c_config_gpio(void) +{ + IO_BANK0->GPIO[I2C_SDA_PIN].CTRL = IO_BANK0_CTRL_FUNCSEL_I2C; + IO_BANK0->GPIO[I2C_SCL_PIN].CTRL = IO_BANK0_CTRL_FUNCSEL_I2C; +} + +/** + * @brief Configure I2C1 as fast-mode master with 7-bit addressing. + * + * Sets MASTER_MODE, SPEED=FAST, IC_SLAVE_DISABLE, IC_RESTART_EN, + * and TX_EMPTY_CTRL bits in the CON register. + * + * @retval None + */ +static void i2c_config_con(void) +{ + I2C1->CON = (1U << I2C_CON_MASTER_MODE_SHIFT) + | (I2C_CON_SPEED_FAST << I2C_CON_SPEED_SHIFT) + | (1U << I2C_CON_IC_RESTART_EN_SHIFT) + | (1U << I2C_CON_IC_SLAVE_DISABLE_SHIFT) + | (1U << I2C_CON_TX_EMPTY_CTRL_SHIFT); +} + +/** + * @brief Program SCL timing for 100 kHz at 12 MHz clk_sys. + * + * Sets fast-mode SCL high/low counts, SDA hold time, and + * spike suppression length. + * + * @retval None + */ +static void i2c_config_timing(void) +{ + I2C1->FS_SCL_HCNT = I2C_FS_SCL_HCNT_VAL; + I2C1->FS_SCL_LCNT = I2C_FS_SCL_LCNT_VAL; + I2C1->FS_SPKLEN = I2C_FS_SPKLEN_VAL; + I2C1->SDA_HOLD = I2C_SDA_TX_HOLD_VAL; +} + +/** + * @brief Check for TX abort and clear if detected. + * @retval bool true if TX abort occurred + */ +static bool check_abort(void) +{ + if (I2C1->RAW_INTR_STAT & I2C_RAW_INTR_TX_ABRT) + { + (void)I2C1->CLR_TX_ABRT; + return true; + } + return false; +} + +/** + * @brief Check for stop condition detected and clear if so. + * @retval bool true if stop detected + */ +static bool check_stop(void) +{ + if (I2C1->RAW_INTR_STAT & I2C_RAW_INTR_STOP_DET) + { + (void)I2C1->CLR_STOP_DET; + return true; + } + return false; +} + +/** + * @brief Wait for a STOP_DET or TX_ABRT interrupt, then clear it. + * @retval None + */ +static void i2c_wait_done(void) +{ + uint32_t timeout = I2C_TIMEOUT; + while (timeout > 0U) + { + if (check_abort() || check_stop()) + break; + timeout--; + } +} + +void i2c_release_reset(void) +{ + RESETS->RESET |= (1U << RESETS_RESET_I2C1_SHIFT); + RESETS->RESET &= ~(1U << RESETS_RESET_I2C1_SHIFT); + while (!(RESETS->RESET_DONE & (1U << RESETS_RESET_I2C1_SHIFT))) {} +} + +void i2c_init(void) +{ + i2c_config_pads(); + i2c_config_gpio(); + I2C1->ENABLE = 0U; + i2c_config_con(); + I2C1->TX_TL = 0U; + I2C1->RX_TL = 0U; + i2c_config_timing(); + I2C1->ENABLE = 1U; +} + +void i2c_set_target(uint8_t addr) +{ + I2C1->ENABLE = 0U; + I2C1->TAR = addr; + I2C1->ENABLE = 1U; +} + +void i2c_write_byte(uint8_t data) +{ + (void)I2C1->CLR_TX_ABRT; + I2C1->DATA_CMD = data | (1U << I2C_DATA_CMD_STOP_SHIFT); + i2c_wait_done(); +} diff --git a/drivers/0x08_lcd1602_cbm/Src/rp2350_lcd1602.c b/drivers/0x08_lcd1602_cbm/Src/rp2350_lcd1602.c new file mode 100644 index 0000000..35da36c --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/rp2350_lcd1602.c @@ -0,0 +1,142 @@ +/** + * @file rp2350_lcd1602.c + * @brief LCD1602 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Drives a 16x2 HD44780 LCD through a PCF8574 I2C backpack + * in 4-bit mode. Each nibble is latched by pulsing the EN + * line via single-byte I2C writes to the PCF8574. All + * timing margins exceed HD44780 datasheet minimums. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_lcd1602.h" +#include "rp2350_i2c.h" +#include "rp2350_delay.h" + +/** + * @brief Pulse the EN line to latch a nibble into the HD44780. + * + * Writes the data byte with EN set, waits 1 us, then writes + * with EN cleared and waits 50 us for the controller to process. + * + * @param data PCF8574 output byte (RS, backlight, and nibble already set) + * @retval None + */ +static void lcd_pulse_enable(uint8_t data) +{ + i2c_write_byte(data | LCD_PIN_EN); + delay_us(1); + i2c_write_byte(data & ~LCD_PIN_EN); + delay_us(50); +} + +/** + * @brief Write one 4-bit nibble to the LCD controller. + * @param nibble lower 4 bits to send + * @param mode 0 for command, non-zero for character data + * @retval None + */ +static void lcd_write4(uint8_t nibble, uint8_t mode) +{ + uint8_t data = (nibble & 0x0FU) << LCD_NIBBLE_SHIFT; + data |= mode ? LCD_PIN_RS : 0U; + data |= LCD_BACKLIGHT; + lcd_pulse_enable(data); +} + +/** + * @brief Send a full 8-bit value as two nibbles (high then low). + * @param value byte to send to the LCD + * @param mode 0 for command, non-zero for character data + * @retval None + */ +static void lcd_send(uint8_t value, uint8_t mode) +{ + lcd_write4((value >> 4) & 0x0FU, mode); + lcd_write4(value & 0x0FU, mode); +} + +/** + * @brief Execute the HD44780 4-bit mode power-on reset sequence. + * + * Sends 0x03 three times with required inter-command delays, + * then sends 0x02 to switch from 8-bit to 4-bit interface. + * + * @retval None + */ +static void lcd_hd44780_reset(void) +{ + lcd_write4(0x03, 0); + delay_ms(5); + lcd_write4(0x03, 0); + delay_us(150); + lcd_write4(0x03, 0); + delay_us(150); + lcd_write4(0x02, 0); + delay_us(150); +} + +/** + * @brief Send post-reset configuration commands to the HD44780. + * + * Sets 4-bit mode with 2 display lines and 5x8 font, turns + * display on with cursor hidden, clears the screen, and + * selects left-to-right entry mode. + * + * @retval None + */ +static void lcd_hd44780_configure(void) +{ + lcd_send(LCD_CMD_FUNCTION_SET_4BIT, 0); + lcd_send(LCD_CMD_DISPLAY_ON, 0); + lcd_send(LCD_CMD_CLEAR, 0); + delay_ms(2); + lcd_send(LCD_CMD_ENTRY_MODE, 0); +} + +void lcd_init(void) +{ + i2c_set_target(LCD_I2C_ADDR); + lcd_hd44780_reset(); + lcd_hd44780_configure(); +} + +void lcd_clear(void) +{ + lcd_send(LCD_CMD_CLEAR, 0); + delay_ms(2); +} + +void lcd_set_cursor(uint8_t line, uint8_t position) +{ + uint8_t offset = (line == 0U) ? LCD_ROW0_OFFSET : LCD_ROW1_OFFSET; + lcd_send(LCD_CMD_SET_DDRAM | (position + offset), 0); +} + +void lcd_puts(const char *str) +{ + while (*str) + lcd_send((uint8_t)*str++, 1); +} diff --git a/drivers/0x08_lcd1602_cbm/Src/rp2350_reset.c b/drivers/0x08_lcd1602_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x08_lcd1602_cbm/Src/rp2350_reset_handler.c b/drivers/0x08_lcd1602_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..62b58d2 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,80 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, then branches + * to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x08_lcd1602_cbm/Src/rp2350_stack.c b/drivers/0x08_lcd1602_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x08_lcd1602_cbm/Src/rp2350_uart.c b/drivers/0x08_lcd1602_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x08_lcd1602_cbm/Src/rp2350_xosc.c b/drivers/0x08_lcd1602_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..b33a71d --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/rp2350_xosc.c @@ -0,0 +1,54 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value &= ~CLK_PERI_CTRL_AUXSRC_MASK; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} + +void xosc_set_clk_ref(void) +{ + CLOCKS->CLK_REF_CTRL = CLK_REF_CTRL_SRC_XOSC; +} diff --git a/drivers/0x08_lcd1602_cbm/Src/vector_table.c b/drivers/0x08_lcd1602_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x08_lcd1602_cbm/build.log b/drivers/0x08_lcd1602_cbm/build.log new file mode 100644 index 0000000..4c8710c Binary files /dev/null and b/drivers/0x08_lcd1602_cbm/build.log differ diff --git a/drivers/0x08_lcd1602_cbm/linker.ld b/drivers/0x08_lcd1602_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x08_lcd1602_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x08_lcd1602_rust/.cargo/config.toml b/drivers/0x08_lcd1602_rust/.cargo/config.toml new file mode 100644 index 0000000..1791d74 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/.cargo/config.toml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2021–2024 The rp-rs Developers +# Copyright (c) 2021 rp-rs organization +# Copyright (c) 2025 Raspberry Pi Ltd. +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# You might want to make a similar file in your own repository if you are +# writing programs for Raspberry Silicon microcontrollers. +# + +[build] +target = "thumbv8m.main-none-eabihf" +# Set the default target to match the Cortex-M33 in the RP2350 +# target = "thumbv8m.main-none-eabihf" +# target = "thumbv6m-none-eabi" +# target = "riscv32imac-unknown-none-elf" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the hard-float ABI for Arm mode. +# +# The FPU is enabled by default, and float function arguments use FPU +# registers. +[target.thumbv8m.main-none-eabihf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as a linker script. +# This is usually provided by the cortex-m-rt crate, and by default the +# version in that crate will include a file called `memory.x` which describes +# the particular memory layout for your specific chip. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the soft-float ABI for RISC-V mode. +# +# Hazard 3 does not have an FPU and so float function arguments use integer +# registers. +[target.riscv32imac-unknown-none-elf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Trp235x_riscv.x also tells the linker to use +# `rp235x_riscv.x` as a linker script. This adds in RP2350 RISC-V specific +# things that the riscv-rt crate's `link.x` requires and then includes +# `link.x` automatically. This is the reverse of how we do it on Cortex-M. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x08_lcd1602_rust/.gitignore b/drivers/0x08_lcd1602_rust/.gitignore new file mode 100644 index 0000000..03381c1 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/.gitignore @@ -0,0 +1,113 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,macos,windows,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux diff --git a/drivers/0x08_lcd1602_rust/.pico-rs b/drivers/0x08_lcd1602_rust/.pico-rs new file mode 100644 index 0000000..1b6702a --- /dev/null +++ b/drivers/0x08_lcd1602_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 \ No newline at end of file diff --git a/drivers/0x08_lcd1602_rust/.vscode/extensions.json b/drivers/0x08_lcd1602_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x08_lcd1602_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602_rust/.vscode/launch.json b/drivers/0x08_lcd1602_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602_rust/.vscode/settings.json b/drivers/0x08_lcd1602_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602_rust/.vscode/tasks.json b/drivers/0x08_lcd1602_rust/.vscode/tasks.json new file mode 100644 index 0000000..4194af4 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/drivers/0x08_lcd1602_rust/Cargo.toml b/drivers/0x08_lcd1602_rust/Cargo.toml new file mode 100644 index 0000000..fc2f7c4 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/Cargo.toml @@ -0,0 +1,40 @@ +[package] +edition = "2024" +name = "lcd1602" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "lcd1602_lib" +path = "src/lib.rs" + +[[bin]] +name = "lcd1602" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +embedded-hal = "1.0.0" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x08_lcd1602_rust/LICENSE-APACHE b/drivers/0x08_lcd1602_rust/LICENSE-APACHE new file mode 100644 index 0000000..8d99cbc --- /dev/null +++ b/drivers/0x08_lcd1602_rust/LICENSE-APACHE @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2021–2024 The rp-rs Developers + Copyright (c) 2021 rp-rs organization + Copyright (c) 2025 Raspberry Pi Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/drivers/0x08_lcd1602_rust/LICENSE-MIT b/drivers/0x08_lcd1602_rust/LICENSE-MIT new file mode 100644 index 0000000..5369c70 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/LICENSE-MIT @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2021–2024 The rp-rs Developers +Copyright (c) 2021 rp-rs organization +Copyright (c) 2025 Raspberry Pi Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + \ No newline at end of file diff --git a/drivers/0x08_lcd1602_rust/build.rs b/drivers/0x08_lcd1602_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x08_lcd1602_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x08_lcd1602_rust/rp2040.x b/drivers/0x08_lcd1602_rust/rp2040.x new file mode 100644 index 0000000..0cc665b --- /dev/null +++ b/drivers/0x08_lcd1602_rust/rp2040.x @@ -0,0 +1,91 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + /* + * Here we assume you have 2048 KiB of Flash. This is what the Pi Pico + * has, but your board may have more or less Flash and you should adjust + * this value to suit. + */ + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + /* + * RAM consists of 4 banks, SRAM0-SRAM3, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K + /* + * RAM banks 4 and 5 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k + + /* SRAM banks 0-3 can also be accessed directly. However, those ranges + alias with the RAM mapping, above. So don't use them at the same time! + SRAM0 : ORIGIN = 0x21000000, LENGTH = 64k + SRAM1 : ORIGIN = 0x21010000, LENGTH = 64k + SRAM2 : ORIGIN = 0x21020000, LENGTH = 64k + SRAM3 : ORIGIN = 0x21030000, LENGTH = 64k + */ +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader + * + * An executable block of code which sets up the QSPI interface for + * 'Execute-In-Place' (or XIP) mode. Also sends chip-specific commands to + * the external flash chip. + * + * Must go at the start of external flash, where the Boot ROM expects it. + */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 512 bytes of flash, + * where picotool can find it + */ + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x08_lcd1602_rust/rp2350.x b/drivers/0x08_lcd1602_rust/rp2350.x new file mode 100644 index 0000000..bda94d8 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/rp2350.x @@ -0,0 +1,83 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 4K of flash + * where the Boot ROM (and picotool) can find it + */ + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + /* move .text to start /after/ the boot info */ + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + /* ### Boot ROM extra info + * + * Goes after everything in our program, so it can contain a signature. + */ + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + \ No newline at end of file diff --git a/drivers/0x08_lcd1602_rust/rp2350_riscv.x b/drivers/0x08_lcd1602_rust/rp2350_riscv.x new file mode 100644 index 0000000..84388aa --- /dev/null +++ b/drivers/0x08_lcd1602_rust/rp2350_riscv.x @@ -0,0 +1,259 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol is not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `32`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > FLASH + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + __start_block_addr = .; + KEEP(*(.start_block)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + . = ALIGN(4); + } > FLASH + + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > FLASH + + .data : ALIGN(32) + { + _sidata = LOADADDR(.data); + __sidata = LOADADDR(.data); + _sdata = .; + __sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(32); + _edata = .; + __edata = .; + } > RAM AT > FLASH + + .bss (NOLOAD) : ALIGN(32) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(32); + _ebss = .; + } > RAM + + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > RAM + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > RAM + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(start_to_end = __end_block_addr - __start_block_addr); +PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 32 == 0, " +ERROR(riscv-rt): the start of the RAM must be 32-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, " +BUG(riscv-rt): .data is not 32-byte aligned"); + +ASSERT(_sidata % 32 == 0, " +BUG(riscv-rt): the LMA of .data is not 32-byte aligned"); + +ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, " +BUG(riscv-rt): .bss is not 32-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(riscv-rt): The .text section must be placed inside the FLASH region. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/drivers/0x08_lcd1602_rust/src/board.rs b/drivers/0x08_lcd1602_rust/src/board.rs new file mode 100644 index 0000000..296a60d --- /dev/null +++ b/drivers/0x08_lcd1602_rust/src/board.rs @@ -0,0 +1,575 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// I2C bus trait for LCD communication +use embedded_hal::i2c::I2c; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionI2C, FunctionNull, FunctionUart, Pin, PullDown, PullNone, PullUp}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// I2C bus speed in Hz (100 kHz standard mode). +pub(crate) const I2C_BAUD: u32 = 100_000; + +/// 7-bit I2C address of the PCF8574 LCD backpack. +pub(crate) const LCD_I2C_ADDR: u8 = 0x27; + +/// Number of bit positions to shift a 4-bit nibble. +pub(crate) const NIBBLE_SHIFT: u8 = 4; + +/// PCF8574 backlight enable mask. +pub(crate) const BACKLIGHT_MASK: u8 = 0x08; + +/// Delay between counter updates in milliseconds. +pub(crate) const COUNTER_DELAY_MS: u32 = 1_000; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Write one raw byte to the PCF8574 expander over I2C. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `data` - Byte to write. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `addr` - Device address. +/// * `data` - Data to send/write. +fn pcf_write_byte(i2c: &mut impl I2c, addr: u8, data: u8) { + let _ = i2c.write(addr, &[data]); +} + +/// Toggle EN to latch a nibble into the LCD controller. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `data` - Nibble byte without EN asserted. +/// * `delay` - Delay provider for timing. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `addr` - Device address. +/// * `data` - Data to send/write. +/// * `delay` - Delay value. +fn pcf_pulse_enable(i2c: &mut impl I2c, addr: u8, data: u8, delay: &mut cortex_m::delay::Delay) { + pcf_write_byte(i2c, addr, lcd1602_lib::lcd1602::nibble_with_en(data)); + delay.delay_us(1); + pcf_write_byte(i2c, addr, lcd1602_lib::lcd1602::nibble_without_en(data)); + delay.delay_us(50); +} + +/// Write one 4-bit nibble to the LCD. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `nibble` - 4-bit value to send. +/// * `mode` - Register select: 0 for command, 1 for data. +/// * `delay` - Delay provider for timing. +fn lcd_write4( + i2c: &mut impl I2c, + addr: u8, + nibble: u8, + mode: u8, + delay: &mut cortex_m::delay::Delay, +) { + let data = lcd1602_lib::lcd1602::build_nibble(nibble, NIBBLE_SHIFT, mode, BACKLIGHT_MASK); + pcf_pulse_enable(i2c, addr, data, delay); +} + +/// Send one full 8-bit command/data value as two nibbles. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `value` - 8-bit value to send. +/// * `mode` - Register select: 0 for command, 1 for data. +/// * `delay` - Delay provider for timing. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `addr` - Device address. +/// * `value` - Value to use. +/// * `mode` - The `mode` parameter. +/// * `delay` - Delay value. +fn lcd_send(i2c: &mut impl I2c, addr: u8, value: u8, mode: u8, delay: &mut cortex_m::delay::Delay) { + lcd_write4(i2c, addr, (value >> 4) & 0x0F, mode, delay); + lcd_write4(i2c, addr, value & 0x0F, mode, delay); +} + +/// Send three 0x03 nibbles with required power-on delays. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `addr` - Device address. +/// * `delay` - Delay value. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `addr` - Device address. +/// * `delay` - Delay value. +fn lcd_reset_pulse_3x(i2c: &mut impl I2c, addr: u8, delay: &mut cortex_m::delay::Delay) { + lcd_write4(i2c, addr, 0x03, 0, delay); + delay.delay_ms(5); + lcd_write4(i2c, addr, 0x03, 0, delay); + delay.delay_us(150); + lcd_write4(i2c, addr, 0x03, 0, delay); + delay.delay_us(150); +} + +/// Execute the HD44780 4-bit mode power-on reset sequence. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `delay` - Delay provider for timing. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `addr` - Device address. +/// * `delay` - Delay value. +fn lcd_hd44780_reset(i2c: &mut impl I2c, addr: u8, delay: &mut cortex_m::delay::Delay) { + lcd_reset_pulse_3x(i2c, addr, delay); + lcd_write4(i2c, addr, 0x02, 0, delay); + delay.delay_us(150); +} + +/// Send post-reset configuration commands to the HD44780. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `delay` - Delay provider for timing. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `addr` - Device address. +/// * `delay` - Delay value. +fn lcd_hd44780_configure(i2c: &mut impl I2c, addr: u8, delay: &mut cortex_m::delay::Delay) { + lcd_send(i2c, addr, 0x28, 0, delay); + lcd_send(i2c, addr, 0x0C, 0, delay); + lcd_send(i2c, addr, 0x01, 0, delay); + delay.delay_ms(2); + lcd_send(i2c, addr, 0x06, 0, delay); +} + +/// Set the LCD cursor position. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `line` - Display row (0 or 1). +/// * `position` - Column offset. +/// * `delay` - Delay provider for timing. +fn lcd_set_cursor( + i2c: &mut impl I2c, + addr: u8, + line: u8, + position: u8, + delay: &mut cortex_m::delay::Delay, +) { + lcd_send( + i2c, + addr, + lcd1602_lib::lcd1602::cursor_address(line, position), + 0, + delay, + ); +} + +/// Write a byte slice as character data to the LCD. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `s` - Byte slice of ASCII characters to display. +/// * `delay` - Delay provider for timing. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `addr` - Device address. +/// * `s` - The `s` parameter. +/// * `delay` - Delay value. +fn lcd_puts(i2c: &mut impl I2c, addr: u8, s: &[u8], delay: &mut cortex_m::delay::Delay) { + for &ch in s { + lcd_send(i2c, addr, ch, 1, delay); + } +} + +/// Initialize the LCD, display the title, and log over UART. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `uart` - UART peripheral for serial log output. +/// * `delay` - Delay provider for timing. +pub(crate) fn setup_display( + i2c: &mut impl I2c, + uart: &EnabledUart, + delay: &mut cortex_m::delay::Delay, +) { + lcd_hd44780_reset(i2c, LCD_I2C_ADDR, delay); + lcd_hd44780_configure(i2c, LCD_I2C_ADDR, delay); + lcd_show_title(i2c, delay); + uart.write_full_blocking(b"LCD 1602 driver initialized at I2C addr 0x27\r\n"); +} + +/// Write the title text on LCD row 0. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `delay` - Delay value. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `delay` - Delay value. +fn lcd_show_title(i2c: &mut impl I2c, delay: &mut cortex_m::delay::Delay) { + lcd_set_cursor(i2c, LCD_I2C_ADDR, 0, 0, delay); + lcd_puts(i2c, LCD_I2C_ADDR, b"Reverse Eng.", delay); +} + +/// Format and display the next counter value on LCD line 1. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `uart` - UART peripheral for serial log output. +/// * `delay` - Delay provider for timing. +/// * `count` - Mutable reference to the counter state. +pub(crate) fn update_counter( + i2c: &mut impl I2c, + uart: &EnabledUart, + delay: &mut cortex_m::delay::Delay, + count: &mut u32, +) { + let mut buf = [0u8; 16]; + let n = lcd1602_lib::lcd1602::format_counter(&mut buf, *count); + *count += 1; + lcd_display_counter(i2c, delay, &buf[..n]); + uart_log_counter(uart, &buf[..n]); + delay.delay_ms(COUNTER_DELAY_MS); +} + +/// Write counter text to LCD line 1. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `delay` - Delay value. +/// * `text` - The `text` parameter. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `delay` - Delay value. +/// * `text` - The `text` parameter. +fn lcd_display_counter(i2c: &mut impl I2c, delay: &mut cortex_m::delay::Delay, text: &[u8]) { + lcd_set_cursor(i2c, LCD_I2C_ADDR, 1, 0, delay); + lcd_puts(i2c, LCD_I2C_ADDR, text, delay); +} + +/// Log counter text over UART with trailing CRLF. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `text` - The `text` parameter. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `text` - The `text` parameter. +fn uart_log_counter(uart: &EnabledUart, text: &[u8]) { + uart.write_full_blocking(text); + uart.write_full_blocking(b"\r\n"); +} + +/// Initialise all peripherals and run the LCD 1602 counter demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + let mut i2c = init_i2c(pac.I2C1, p.gpio2, p.gpio3, &mut pac.RESETS, &clocks); + setup_display(&mut i2c, &uart, &mut delay); + counter_loop(&mut i2c, &uart, &mut delay) +} + +/// Initialise I2C1 on SDA=GPIO2 / SCL=GPIO3. +/// +/// # Arguments +/// +/// * `i2c1` - PAC I2C1 peripheral singleton. +/// * `sda` - Default GPIO 2 pin (will be reconfigured for I2C). +/// * `scl` - Default GPIO 3 pin (will be reconfigured for I2C). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Configured I2C1 bus controller. +fn init_i2c( + i2c1: hal::pac::I2C1, + sda: Pin, + scl: Pin, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> impl I2c { + let sda = sda.reconfigure::(); + let scl = scl.reconfigure::(); + hal::I2C::i2c1( + i2c1, + sda, + scl, + I2C_BAUD.Hz(), + resets, + clocks.system_clock.freq(), + ) +} + +/// Run the counter display loop forever. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus controller. +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `delay` - Mutable reference to the blocking delay provider. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `i2c` - The `i2c` parameter. +/// * `uart` - The `uart` parameter. +/// * `delay` - Delay value. +/// +/// # Returns +/// +/// A value of type `!`. +fn counter_loop(i2c: &mut impl I2c, uart: &EnabledUart, delay: &mut cortex_m::delay::Delay) -> ! { + let mut count: u32 = 0; + loop { + update_counter(i2c, uart, delay, &mut count); + } +} + diff --git a/drivers/0x08_lcd1602_rust/src/lcd1602.rs b/drivers/0x08_lcd1602_rust/src/lcd1602.rs new file mode 100644 index 0000000..1af51ed --- /dev/null +++ b/drivers/0x08_lcd1602_rust/src/lcd1602.rs @@ -0,0 +1,324 @@ +//! Implementation module +//! +//! **File:** `lcd1602.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// PCF8574 -> LCD control pin: Register Select. +pub const PIN_RS: u8 = 0x01; + +/// PCF8574 -> LCD control pin: Read/Write. +pub const PIN_RW: u8 = 0x02; + +/// PCF8574 -> LCD control pin: Enable. +pub const PIN_EN: u8 = 0x04; + +/// Build a PCF8574 output byte for a 4-bit LCD nibble. +/// +/// # Arguments +/// +/// * `nibble` - 4-bit data value (0x00..0x0F). +/// * `nibble_shift` - Number of bits to shift the nibble left. +/// * `mode` - Register select mode (0 = command, non-zero = data). +/// * `backlight_mask` - Bitmask to enable the backlight LED. +/// +/// # Returns +/// +/// Assembled PCF8574 output byte. +/// +/// # Arguments +/// +/// * `nibble` - The `nibble` parameter. +/// * `nibble_shift` - The `nibble_shift` parameter. +/// * `mode` - The `mode` parameter. +/// * `backlight_mask` - The `backlight_mask` parameter. +/// +/// # Returns +/// +/// An 8-bit unsigned integer value. +pub fn build_nibble(nibble: u8, nibble_shift: u8, mode: u8, backlight_mask: u8) -> u8 { + let mut data = (nibble & 0x0F) << nibble_shift; + if mode != 0 { + data |= PIN_RS; + } + data |= backlight_mask; + data +} + +/// Build the PCF8574 byte with EN asserted. +/// +/// # Arguments +/// +/// * `nibble_byte` - PCF8574 output byte before EN assertion. +/// +/// # Returns +/// +/// Byte with the EN bit set. +/// +/// # Arguments +/// +/// * `nibble_byte` - The `nibble_byte` parameter. +/// +/// # Returns +/// +/// An 8-bit unsigned integer value. +pub fn nibble_with_en(nibble_byte: u8) -> u8 { + nibble_byte | PIN_EN +} + +/// Build the PCF8574 byte with EN de-asserted. +/// +/// # Arguments +/// +/// * `nibble_byte` - PCF8574 output byte before EN de-assertion. +/// +/// # Returns +/// +/// Byte with the EN bit cleared. +/// +/// # Arguments +/// +/// * `nibble_byte` - The `nibble_byte` parameter. +/// +/// # Returns +/// +/// An 8-bit unsigned integer value. +pub fn nibble_without_en(nibble_byte: u8) -> u8 { + nibble_byte & !PIN_EN +} + +/// HD44780 row-offset lookup. +const ROW_OFFSETS: [u8; 2] = [0x00, 0x40]; + +/// Compute the DDRAM address byte for `lcd_set_cursor`. +/// +/// # Arguments +/// +/// * `line` - Display row (0 or 1; values > 1 are clamped to 1). +/// * `position` - Column offset within the row. +/// +/// # Returns +/// +/// HD44780 set-DDRAM-address command byte (0x80 | offset). +/// +/// # Arguments +/// +/// * `line` - The `line` parameter. +/// * `position` - The `position` parameter. +/// +/// # Returns +/// +/// An 8-bit unsigned integer value. +pub fn cursor_address(line: u8, position: u8) -> u8 { + let row = if line > 1 { 1 } else { line as usize }; + 0x80 | (position + ROW_OFFSETS[row]) +} + +/// Extract the six decimal digits of a counter value into an array. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// +/// # Returns +/// +/// A value of type `[u8; 6]`. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// +/// # Returns +/// +/// A value of type `[u8; 6]`. +fn fill_digit_array(c: u32) -> [u8; 6] { + [ + ((c / 100000) % 10) as u8, + ((c / 10000) % 10) as u8, + ((c / 1000) % 10) as u8, + ((c / 100) % 10) as u8, + ((c / 10) % 10) as u8, + (c % 10) as u8, + ] +} + +/// Write six counter digits into `buf` starting at `start`, suppressing leading zeros. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `digits` - The `digits` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `digits` - The `digits` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn write_counter_digits(buf: &mut [u8], mut pos: usize, digits: &[u8; 6]) -> usize { + let mut leading = true; + for (i, &d) in digits.iter().enumerate() { + if i == 5 || d != 0 { + leading = false; + } + buf[pos] = if leading { b' ' } else { b'0' + d }; + pos += 1; + } + pos +} + +/// Format a counter value as `"Count: NNNNNN"` (right-justified, 6 digits). +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 13 bytes). +/// * `count` - Counter value to format (0..999999). +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `count` - The `count` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_counter(buf: &mut [u8], count: u32) -> usize { + buf[..7].copy_from_slice(b"Count: "); + let digits = fill_digit_array(count); + write_counter_digits(buf, 7, &digits) +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the build nibble command mode operation. + #[test] + fn build_nibble_command_mode() { + let b = build_nibble(0x03, 4, 0, 0x08); + assert_eq!(b, 0x38); + } + + /// Executes the build nibble data mode operation. + #[test] + fn build_nibble_data_mode() { + let b = build_nibble(0x04, 4, 1, 0x08); + assert_eq!(b, 0x49); + } + + /// Executes the build nibble no backlight operation. + #[test] + fn build_nibble_no_backlight() { + let b = build_nibble(0x0F, 4, 0, 0x00); + assert_eq!(b, 0xF0); + } + + /// Executes the nibble with en sets bit operation. + #[test] + fn nibble_with_en_sets_bit() { + assert_eq!(nibble_with_en(0x38), 0x3C); + } + + /// Executes the nibble without en clears bit operation. + #[test] + fn nibble_without_en_clears_bit() { + assert_eq!(nibble_without_en(0x3C), 0x38); + } + + /// Executes the cursor address line0 col0 operation. + #[test] + fn cursor_address_line0_col0() { + assert_eq!(cursor_address(0, 0), 0x80); + } + + /// Executes the cursor address line1 col0 operation. + #[test] + fn cursor_address_line1_col0() { + assert_eq!(cursor_address(1, 0), 0xC0); + } + + /// Executes the cursor address line0 col5 operation. + #[test] + fn cursor_address_line0_col5() { + assert_eq!(cursor_address(0, 5), 0x85); + } + + /// Executes the cursor address line1 col15 operation. + #[test] + fn cursor_address_line1_col15() { + assert_eq!(cursor_address(1, 15), 0xCF); + } + + /// Executes the cursor address clamps line operation. + #[test] + fn cursor_address_clamps_line() { + assert_eq!(cursor_address(5, 0), 0xC0); + } + + /// Executes the format counter zero operation. + #[test] + fn format_counter_zero() { + let mut buf = [0u8; 16]; + let n = format_counter(&mut buf, 0); + assert_eq!(&buf[..n], b"Count: 0"); + } + + /// Executes the format counter one operation. + #[test] + fn format_counter_one() { + let mut buf = [0u8; 16]; + let n = format_counter(&mut buf, 1); + assert_eq!(&buf[..n], b"Count: 1"); + } + + /// Executes the format counter large operation. + #[test] + fn format_counter_large() { + let mut buf = [0u8; 16]; + let n = format_counter(&mut buf, 123456); + assert_eq!(&buf[..n], b"Count: 123456"); + } + + /// Executes the format counter six digits operation. + #[test] + fn format_counter_six_digits() { + let mut buf = [0u8; 16]; + let n = format_counter(&mut buf, 999999); + assert_eq!(&buf[..n], b"Count: 999999"); + } +} diff --git a/drivers/0x08_lcd1602_rust/src/lib.rs b/drivers/0x08_lcd1602_rust/src/lib.rs new file mode 100644 index 0000000..41b5280 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod lcd1602; diff --git a/drivers/0x08_lcd1602_rust/src/main.rs b/drivers/0x08_lcd1602_rust/src/main.rs new file mode 100644 index 0000000..ae29d80 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// LCD1602 driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the LCD 1602 counter demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"LCD 1602 Counter Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x09_dht11/.vscode/.vscode/c_cpp_properties.json b/drivers/0x09_dht11/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x09_dht11/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x09_dht11/.vscode/.vscode/cmake-kits.json b/drivers/0x09_dht11/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x09_dht11/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x09_dht11/.vscode/.vscode/extensions.json b/drivers/0x09_dht11/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x09_dht11/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x09_dht11/.vscode/.vscode/launch.json b/drivers/0x09_dht11/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x09_dht11/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x09_dht11/.vscode/.vscode/settings.json b/drivers/0x09_dht11/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x09_dht11/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x09_dht11/.vscode/.vscode/tasks.json b/drivers/0x09_dht11/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x09_dht11/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x09_dht11/.vscode/c_cpp_properties.json b/drivers/0x09_dht11/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x09_dht11/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x09_dht11/.vscode/cmake-kits.json b/drivers/0x09_dht11/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x09_dht11/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x09_dht11/.vscode/extensions.json b/drivers/0x09_dht11/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x09_dht11/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x09_dht11/.vscode/launch.json b/drivers/0x09_dht11/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x09_dht11/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x09_dht11/.vscode/settings.json b/drivers/0x09_dht11/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x09_dht11/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x09_dht11/.vscode/tasks.json b/drivers/0x09_dht11/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x09_dht11/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x09_dht11/0x09_dht11.c b/drivers/0x09_dht11/0x09_dht11.c new file mode 100644 index 0000000..665bc75 --- /dev/null +++ b/drivers/0x09_dht11/0x09_dht11.c @@ -0,0 +1,72 @@ +/** + * @file 0x09_dht11.c + * @brief DHT11 temperature and humidity sensor driver for the Raspberry Pi Pico 2 + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * This driver demonstrates how to read temperature and humidity from a DHT11 + * sensor using the dht11.c/dht11.h driver. The sensor is polled every 2 + * seconds (the minimum safe interval for the DHT11) and the results are + * printed over UART. A failed read is reported so wiring issues are visible. + * + * Wiring: + * GPIO4 -> DHT11 DATA pin (10 kohm pull-up to 3.3 V recommended) + * 3.3V -> DHT11 VCC + * GND -> DHT11 GND + */ + +#include +#include "pico/stdlib.h" +#include "dht11.h" + +/** @brief GPIO pin connected to the DHT11 data line */ +#define DHT11_GPIO 4 + +/** + * @brief Read the DHT11 sensor and print the result over UART + * + * Attempts a single read. On success prints humidity and temperature; + * on failure prints a wiring-check message. Waits 2 s before returning + * to respect the DHT11 minimum polling interval. + */ +static void print_reading(void) { + float humidity = 0.0f; + float temperature = 0.0f; + if (dht11_read(&humidity, &temperature)) + printf("Humidity: %.1f%% Temperature: %.1f C\r\n", humidity, temperature); + else + printf("DHT11 read failed - check wiring on GPIO %d\r\n", DHT11_GPIO); + sleep_ms(2000); +} + +int main(void) { + stdio_init_all(); + dht11_init(DHT11_GPIO); + printf("DHT11 driver initialized on GPIO %d\r\n", DHT11_GPIO); + while (true) + print_reading(); +} diff --git a/drivers/0x09_dht11/CMakeLists.txt b/drivers/0x09_dht11/CMakeLists.txt new file mode 100644 index 0000000..e739b77 --- /dev/null +++ b/drivers/0x09_dht11/CMakeLists.txt @@ -0,0 +1,56 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x09_dht11 C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x09_dht11 0x09_dht11.c dht11.c) + +pico_set_program_name(0x09_dht11 "0x09_dht11") +pico_set_program_version(0x09_dht11 "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x09_dht11 1) +pico_enable_stdio_usb(0x09_dht11 0) + +# Add the standard library to the build +target_link_libraries(0x09_dht11 + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x09_dht11 PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x09_dht11) diff --git a/drivers/0x09_dht11/dht11.c b/drivers/0x09_dht11/dht11.c new file mode 100644 index 0000000..28f147f --- /dev/null +++ b/drivers/0x09_dht11/dht11.c @@ -0,0 +1,140 @@ +/** + * @file dht11.c + * @brief Implementation of DHT11 temperature and humidity sensor driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "dht11.h" +#include "hardware/gpio.h" +#include "pico/time.h" + +/** @brief GPIO pin connected to the DHT11 sensor */ +static uint dht_pin; + +/** + * @brief Send the DHT11 start signal on the data pin + * + * Drives the pin LOW for 18 ms then HIGH for 40 us before switching + * the pin to input mode to listen for the sensor response. + */ +static void send_start_signal(void) { + gpio_set_dir(dht_pin, GPIO_OUT); + gpio_put(dht_pin, 0); + sleep_ms(18); + gpio_put(dht_pin, 1); + sleep_us(40); + gpio_set_dir(dht_pin, GPIO_IN); +} + +/** + * @brief Wait for the pin to leave a given logic level + * + * Spins until the pin no longer reads the specified level, returning + * false if a timeout of 10 000 iterations is exceeded. + * + * @param level Logic level to wait through (0 or 1) + * @return bool true once the level changed, false on timeout + */ +static bool wait_for_level(int level) { + uint32_t timeout = 10000; + while (gpio_get(dht_pin) == level) + if (--timeout == 0) return false; + return true; +} + +/** + * @brief Wait for the DHT11 response after the start signal + * + * The sensor pulls LOW then HIGH then LOW again; each transition + * is awaited with a timeout. + * + * @return bool true if the full response was received, false on timeout + */ +static bool wait_response(void) { + if (!wait_for_level(1)) return false; + if (!wait_for_level(0)) return false; + if (!wait_for_level(1)) return false; + return true; +} + +/** + * @brief Read a single bit from the DHT11 data stream + * + * Waits for the low-period to end, measures the high-period duration, + * and shifts the result into the appropriate byte of the data array. + * + * @param data 5-byte array accumulating the received bits + * @param i Bit index (0-39) + * @return bool true on success, false on timeout + */ +static bool read_bit(uint8_t *data, int i) { + if (!wait_for_level(0)) return false; + uint32_t start = time_us_32(); + if (!wait_for_level(1)) return false; + uint32_t duration = time_us_32() - start; + data[i / 8] <<= 1; + if (duration > 40) data[i / 8] |= 1; + return true; +} + +/** + * @brief Read all 40 data bits from the DHT11 + * + * @param data 5-byte array filled with the received data + * @return bool true if all 40 bits were read, false on timeout + */ +static bool read_40_bits(uint8_t *data) { + for (int i = 0; i < 40; i++) + if (!read_bit(data, i)) return false; + return true; +} + +/** + * @brief Verify the DHT11 checksum byte + * + * @param data 5-byte received data (bytes 0-3 plus checksum in byte 4) + * @return bool true if the checksum matches, false otherwise + */ +static bool validate_checksum(const uint8_t *data) { + return data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF); +} + +void dht11_init(uint8_t pin) { + dht_pin = pin; + gpio_init(pin); + gpio_pull_up(pin); +} + +bool dht11_read(float *humidity, float *temperature) { + uint8_t data[5] = {0}; + send_start_signal(); + if (!wait_response()) return false; + if (!read_40_bits(data)) return false; + if (!validate_checksum(data)) return false; + *humidity = data[0] + data[1] * 0.1f; + *temperature = data[2] + data[3] * 0.1f; + return true; +} diff --git a/drivers/0x09_dht11/dht11.h b/drivers/0x09_dht11/dht11.h new file mode 100644 index 0000000..30e298c --- /dev/null +++ b/drivers/0x09_dht11/dht11.h @@ -0,0 +1,57 @@ +/** + * @file dht11.h + * @brief Header for DHT11 temperature and humidity sensor driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef DHT11_H +#define DHT11_H + +#include +#include + +/** + * @brief Initialize the DHT11 driver + * + * Configures the GPIO pin for the DHT11 sensor. This must be called before + * using dht11_read(). + * + * @param pin GPIO pin number connected to DHT11 signal + */ +void dht11_init(uint8_t pin); + +/** + * @brief Read temperature and humidity from DHT11 sensor + * + * Performs the DHT11 communication protocol to read sensor data. + * + * @param humidity Pointer to store humidity value (0-100%) + * @param temperature Pointer to store temperature value in Celsius + * @return true if read successful, false on error or timeout + */ +bool dht11_read(float *humidity, float *temperature); + +#endif // DHT11_H diff --git a/drivers/0x09_dht11/pico_sdk_import.cmake b/drivers/0x09_dht11/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x09_dht11/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x09_dht11_cbm/.vscode/c_cpp_properties.json b/drivers/0x09_dht11_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x09_dht11_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x09_dht11_cbm/.vscode/extensions.json b/drivers/0x09_dht11_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x09_dht11_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x09_dht11_cbm/.vscode/launch.json b/drivers/0x09_dht11_cbm/.vscode/launch.json new file mode 100644 index 0000000..5f5f38e --- /dev/null +++ b/drivers/0x09_dht11_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/dht11.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDir": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDir": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x09_dht11_cbm/.vscode/settings.json b/drivers/0x09_dht11_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x09_dht11_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x09_dht11_cbm/.vscode/tasks.json b/drivers/0x09_dht11_cbm/.vscode/tasks.json new file mode 100644 index 0000000..7eb85b9 --- /dev/null +++ b/drivers/0x09_dht11_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/dht11.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/dht11.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x09_dht11_cbm/Inc/rp2350.h b/drivers/0x09_dht11_cbm/Inc/rp2350.h new file mode 100644 index 0000000..f5d4b43 --- /dev/null +++ b/drivers/0x09_dht11_cbm/Inc/rp2350.h @@ -0,0 +1,245 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define TIMER0_BASE 0x400B0000UL +#define TICKS_BASE 0x40108000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[12]; // GPOUT0..GPOUT3 registers Address offset: 0x00-0x2C + __IO uint32_t CLK_REF_CTRL; // Reference clock control Address offset: 0x30 + __IO uint32_t RESERVED1[5]; // CLK_REF_DIV..CLK_SYS_SELECTED Address offset: 0x34-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define TIMER0 ((volatile uint32_t *) TIMER0_BASE) +#define TICKS ((volatile uint32_t *) TICKS_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLK_REF bit definitions + */ +#define CLK_REF_CTRL_SRC_XOSC 2U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_MASK (0x07U << CLK_PERI_CTRL_AUXSRC_SHIFT) +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_TIMER0_SHIFT 23U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U +#define IO_BANK0_CTRL_FUNCSEL_NULL 0x1FU + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U +#define PADS_BANK0_PUE_SHIFT 3U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_IN_OFFSET (0x004U / 4U) +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) +#define SIO_GPIO_OE_CLR_OFFSET (0x040U / 4U) + +/** + * @brief TIMER0 register offsets (word indices from TIMER0_BASE) + */ +#define TIMER_TIMERAWL_OFFSET (0x028U / 4U) + +/** + * @brief Tick generator register offsets (word indices from TICKS_BASE) + */ +#define TICKS_TIMER0_CTRL_OFFSET (0x018U / 4U) +#define TICKS_TIMER0_CYCLES_OFFSET (0x01CU / 4U) +#define TICKS_TIMER0_ENABLE 1U +#define TICKS_TIMER0_CYCLES_12MHZ 12U + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief DHT11 GPIO pin definition + */ +#define DHT11_PIN 4U + +/** + * @brief DHT11 protocol timing constants (microseconds) + */ +#define DHT11_START_LOW_MS 18U +#define DHT11_START_HIGH_US 40U +#define DHT11_BIT_THRESHOLD_US 40U +#define DHT11_TIMEOUT 10000U + +/** + * @brief DHT11 data format + */ +#define DHT11_DATA_BYTES 5U +#define DHT11_DATA_BITS 40U + +#endif /* __RP2350_H */ diff --git a/drivers/0x09_dht11_cbm/Inc/rp2350_delay.h b/drivers/0x09_dht11_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..79db58f --- /dev/null +++ b/drivers/0x09_dht11_cbm/Inc/rp2350_delay.h @@ -0,0 +1,51 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond and microsecond busy-wait delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +/** + * @brief Delay for the specified number of microseconds. + * @param us number of microseconds to delay + * @retval None + */ +void delay_us(uint32_t us); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x09_dht11_cbm/Inc/rp2350_dht11.h b/drivers/0x09_dht11_cbm/Inc/rp2350_dht11.h new file mode 100644 index 0000000..fd2f62d --- /dev/null +++ b/drivers/0x09_dht11_cbm/Inc/rp2350_dht11.h @@ -0,0 +1,64 @@ +/** + * @file rp2350_dht11.h + * @brief DHT11 temperature and humidity sensor driver for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Single-wire protocol driver for the DHT11 sensor on GPIO4. + * Uses SIO for GPIO direction switching and TIMER0 TIMERAWL + * for microsecond pulse-width measurement. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DHT11_H +#define __RP2350_DHT11_H + +#include "rp2350.h" + +/** + * @brief Release TIMER0 from reset in the reset controller. + * @retval None + */ +void dht11_timer_release_reset(void); + +/** + * @brief Start the TIMER0 tick generator for 1 us ticks at 12 MHz. + * @retval None + */ +void dht11_timer_start_tick(void); + +/** + * @brief Configure GPIO4 pad and funcsel for SIO with pull-up. + * @retval None + */ +void dht11_init(void); + +/** + * @brief Read temperature and humidity from the DHT11 sensor. + * @param humidity pointer to store humidity integer percentage + * @param temperature pointer to store temperature integer Celsius + * @retval bool true on success, false on timeout or checksum error + */ +bool dht11_read(uint8_t *humidity, uint8_t *temperature); + +#endif /* __RP2350_DHT11_H */ diff --git a/drivers/0x09_dht11_cbm/Inc/rp2350_reset.h b/drivers/0x09_dht11_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x09_dht11_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x09_dht11_cbm/Inc/rp2350_reset_handler.h b/drivers/0x09_dht11_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x09_dht11_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x09_dht11_cbm/Inc/rp2350_stack.h b/drivers/0x09_dht11_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x09_dht11_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x09_dht11_cbm/Inc/rp2350_uart.h b/drivers/0x09_dht11_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x09_dht11_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x09_dht11_cbm/Inc/rp2350_xosc.h b/drivers/0x09_dht11_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..80fd75d --- /dev/null +++ b/drivers/0x09_dht11_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,55 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +/** + * @brief Switch CLK_REF source to XOSC for a stable 12 MHz clk_sys. + * @retval None + */ +void xosc_set_clk_ref(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x09_dht11_cbm/Makefile b/drivers/0x09_dht11_cbm/Makefile new file mode 100644 index 0000000..c6d336a --- /dev/null +++ b/drivers/0x09_dht11_cbm/Makefile @@ -0,0 +1,85 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C DHT11 driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = dht11 + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_dht11.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x09_dht11_cbm/Src/image_def.c b/drivers/0x09_dht11_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x09_dht11_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x09_dht11_cbm/Src/main.c b/drivers/0x09_dht11_cbm/Src/main.c new file mode 100644 index 0000000..19a0a25 --- /dev/null +++ b/drivers/0x09_dht11_cbm/Src/main.c @@ -0,0 +1,133 @@ +/** + * @file main.c + * @brief DHT11 temperature and humidity sensor demonstration. + * @author Kevin Thomas + * @date 2026 + * + * Reads the DHT11 sensor on GPIO4 every 2 seconds, printing + * humidity and temperature over UART. Reports read failures + * for wiring diagnostics. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * GPIO4 -> DHT11 DATA (10k pull-up to 3.3V recommended) + * 3.3V -> DHT11 VCC + * GND -> DHT11 GND + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_dht11.h" +#include "rp2350_uart.h" +#include "rp2350_delay.h" +#include "rp2350_xosc.h" + +/** + * @brief Minimum polling interval for the DHT11 sensor in milliseconds. + */ +/** @brief Interval between DHT11 sensor reads in milliseconds */ +#define DHT11_POLL_MS 2000U + +/** + * @brief Convert an unsigned integer (0-255) to a decimal string. + * @param value number to convert + * @param buf output buffer (at least 4 bytes) + * @retval None + */ +static void uint_to_str(uint8_t value, char *buf) +{ + uint8_t idx = 0; + if (value >= 100) + buf[idx++] = (char)('0' + value / 100); + if (value >= 10) + buf[idx++] = (char)('0' + (value / 10) % 10); + buf[idx++] = (char)('0' + value % 10); + buf[idx] = '\0'; +} + +/** + * @brief Print a successful DHT11 reading over UART. + * @param humidity humidity percentage (integer) + * @param temperature temperature in Celsius (integer) + * @retval None + */ +static void print_reading(uint8_t humidity, uint8_t temperature) +{ + char buf[4]; + uart_puts("Humidity: "); + uint_to_str(humidity, buf); + uart_puts(buf); + uart_puts("% Temperature: "); + uint_to_str(temperature, buf); + uart_puts(buf); + uart_puts(" C\r\n"); +} + +/** + * @brief Print a read failure message over UART. + * @retval None + */ +static void print_failure(void) +{ + uart_puts("DHT11 read failed - check wiring on GPIO4\r\n"); +} + +/** + * @brief Initialize clocks, timer, DHT11, and announce over UART. + * @retval None + */ +static void dht11_setup(void) +{ + uint8_t dummy_h; + uint8_t dummy_t; + xosc_set_clk_ref(); + dht11_timer_release_reset(); + dht11_timer_start_tick(); + dht11_init(); + delay_ms(DHT11_POLL_MS); + dht11_read(&dummy_h, &dummy_t); + delay_ms(DHT11_POLL_MS); + uart_puts("DHT11 driver initialized on GPIO4\r\n"); +} + +/** + * @brief Read the sensor once and print results or failure. + * @retval None + */ +static void poll_sensor(void) +{ + uint8_t humidity; + uint8_t temperature; + if (dht11_read(&humidity, &temperature)) + print_reading(humidity, temperature); + else + print_failure(); + delay_ms(DHT11_POLL_MS); +} + +int main(void) +{ + dht11_setup(); + while (1) + poll_sensor(); +} diff --git a/drivers/0x09_dht11_cbm/Src/rp2350_delay.c b/drivers/0x09_dht11_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..65b0821 --- /dev/null +++ b/drivers/0x09_dht11_cbm/Src/rp2350_delay.c @@ -0,0 +1,64 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond and microsecond delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} + +void delay_us(uint32_t us) +{ + if (us == 0) + return; + __asm__ volatile ( + "mov r4, #4\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (us) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x09_dht11_cbm/Src/rp2350_dht11.c b/drivers/0x09_dht11_cbm/Src/rp2350_dht11.c new file mode 100644 index 0000000..eda4ac6 --- /dev/null +++ b/drivers/0x09_dht11_cbm/Src/rp2350_dht11.c @@ -0,0 +1,285 @@ +/** + * @file rp2350_dht11.c + * @brief DHT11 temperature and humidity sensor driver for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Implements the single-wire DHT11 protocol using bare-metal + * SIO GPIO control and TIMER0 microsecond timestamps. Reads + * 40 bits (humidity high, humidity low, temperature high, + * temperature low, checksum) with timeout protection. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_dht11.h" +#include "rp2350_delay.h" + +/** + * @brief Bit mask for the DHT11 data pin. + */ +#define DHT11_PIN_MASK (1U << DHT11_PIN) + +/** + * @brief Read the TIMER0 raw low register for a microsecond timestamp. + * @retval uint32_t current microsecond count + */ +static uint32_t time_us(void) +{ + return TIMER0[TIMER_TIMERAWL_OFFSET]; +} + +/** + * @brief Set GPIO4 as output via SIO output-enable set register. + * @retval None + */ +static void set_output(void) +{ + SIO[SIO_GPIO_OE_SET_OFFSET] = DHT11_PIN_MASK; +} + +/** + * @brief Set GPIO4 as input via SIO output-enable clear register. + * @retval None + */ +static void set_input(void) +{ + SIO[SIO_GPIO_OE_CLR_OFFSET] = DHT11_PIN_MASK; +} + +/** + * @brief Drive GPIO4 low via SIO output clear register. + * @retval None + */ +static void drive_low(void) +{ + SIO[SIO_GPIO_OUT_CLR_OFFSET] = DHT11_PIN_MASK; +} + +/** + * @brief Drive GPIO4 high via SIO output set register. + * @retval None + */ +static void drive_high(void) +{ + SIO[SIO_GPIO_OUT_SET_OFFSET] = DHT11_PIN_MASK; +} + +/** + * @brief Read the current level of GPIO4 from SIO input register. + * @retval bool true if pin is high, false if low + */ +static bool read_pin(void) +{ + return (SIO[SIO_GPIO_IN_OFFSET] & DHT11_PIN_MASK) != 0; +} + +/** + * @brief Wait for the pin to leave a given logic level with timeout. + * @param level logic level to wait through (true=high, false=low) + * @retval bool true once the level changed, false on timeout + */ +static bool wait_for_level(bool level) +{ + uint32_t count = DHT11_TIMEOUT; + while (read_pin() == level) + if (--count == 0) + return false; + return true; +} + +/** + * @brief Send the 18 ms low / 40 us high start signal to the DHT11. + * @retval None + */ +static void send_start_signal(void) +{ + set_output(); + drive_low(); + delay_ms(DHT11_START_LOW_MS); + drive_high(); + delay_us(DHT11_START_HIGH_US); + set_input(); +} + +/** + * @brief Wait for the DHT11 response (low-high-low handshake). + * @retval bool true if response received, false on timeout + */ +static bool wait_response(void) +{ + if (!wait_for_level(true)) + return false; + if (!wait_for_level(false)) + return false; + if (!wait_for_level(true)) + return false; + return true; +} + +/** + * @brief Wait for low-to-high transition and measure high-pulse width. + * @param duration_out pointer to store the pulse duration in microseconds + * @retval bool true on success, false on timeout + */ +static bool measure_high_pulse(uint32_t *duration_out) +{ + uint32_t start; + if (!wait_for_level(false)) + return false; + start = time_us(); + if (!wait_for_level(true)) + return false; + *duration_out = time_us() - start; + return true; +} + +/** + * @brief Read a single bit by measuring the high-pulse duration. + * @param data 5-byte array accumulating received bits + * @param bit bit index (0-39) + * @retval bool true on success, false on timeout + */ +static bool read_bit(uint8_t *data, uint8_t bit) +{ + uint32_t duration; + if (!measure_high_pulse(&duration)) + return false; + data[bit / 8U] <<= 1; + if (duration > DHT11_BIT_THRESHOLD_US) + data[bit / 8U] |= 1U; + return true; +} + +/** + * @brief Read all 40 data bits from the DHT11 sensor. + * @param data 5-byte array filled with received data + * @retval bool true if all bits read, false on timeout + */ +static bool read_40_bits(uint8_t *data) +{ + for (uint8_t i = 0; i < DHT11_DATA_BITS; i++) + if (!read_bit(data, i)) + return false; + return true; +} + +/** + * @brief Verify the DHT11 checksum (sum of bytes 0-3 vs byte 4). + * @param data 5 bytes of received sensor data + * @retval bool true if checksum matches, false otherwise + */ +static bool validate_checksum(const uint8_t *data) +{ + uint8_t sum; + sum = data[0] + data[1] + data[2] + data[3]; + return data[4] == sum; +} + +/** + * @brief Clear the TIMER0 reset bit in the reset controller. + * @retval None + */ +static void timer_clear_reset(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_TIMER0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until TIMER0 is out of reset. + * @retval None + */ +static void timer_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_TIMER0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO4 pad: enable input, pull-up, clear isolation. + * @retval None + */ +static void configure_pad(void) +{ + uint32_t value; + value = PADS_BANK0->GPIO[DHT11_PIN]; + value &= ~(1U << PADS_BANK0_OD_SHIFT); + value |= (1U << PADS_BANK0_IE_SHIFT); + value |= (1U << PADS_BANK0_PUE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + PADS_BANK0->GPIO[DHT11_PIN] = value; +} + +/** + * @brief Set GPIO4 funcsel to SIO for software-controlled IO. + * @retval None + */ +static void configure_funcsel(void) +{ + IO_BANK0->GPIO[DHT11_PIN].CTRL = IO_BANK0_CTRL_FUNCSEL_SIO; +} + +void dht11_timer_release_reset(void) +{ + timer_clear_reset(); + timer_wait_reset_done(); +} + +void dht11_timer_start_tick(void) +{ + TICKS[TICKS_TIMER0_CYCLES_OFFSET] = TICKS_TIMER0_CYCLES_12MHZ; + TICKS[TICKS_TIMER0_CTRL_OFFSET] = TICKS_TIMER0_ENABLE; +} + +void dht11_init(void) +{ + configure_pad(); + configure_funcsel(); + set_input(); +} + +/** + * @brief Acquire 40 bits from the DHT11 and validate the checksum. + * @param data 5-byte output array for sensor data + * @retval bool true on success, false on timeout or checksum error + */ +static bool acquire_data(uint8_t *data) +{ + send_start_signal(); + if (!wait_response()) + return false; + if (!read_40_bits(data)) + return false; + return validate_checksum(data); +} + +bool dht11_read(uint8_t *humidity, uint8_t *temperature) +{ + uint8_t data[DHT11_DATA_BYTES] = {0}; + if (!acquire_data(data)) + return false; + *humidity = data[0]; + *temperature = data[2]; + return true; +} diff --git a/drivers/0x09_dht11_cbm/Src/rp2350_reset.c b/drivers/0x09_dht11_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x09_dht11_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x09_dht11_cbm/Src/rp2350_reset_handler.c b/drivers/0x09_dht11_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..62b58d2 --- /dev/null +++ b/drivers/0x09_dht11_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,80 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, then branches + * to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x09_dht11_cbm/Src/rp2350_stack.c b/drivers/0x09_dht11_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x09_dht11_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x09_dht11_cbm/Src/rp2350_uart.c b/drivers/0x09_dht11_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x09_dht11_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x09_dht11_cbm/Src/rp2350_xosc.c b/drivers/0x09_dht11_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..b33a71d --- /dev/null +++ b/drivers/0x09_dht11_cbm/Src/rp2350_xosc.c @@ -0,0 +1,54 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value &= ~CLK_PERI_CTRL_AUXSRC_MASK; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} + +void xosc_set_clk_ref(void) +{ + CLOCKS->CLK_REF_CTRL = CLK_REF_CTRL_SRC_XOSC; +} diff --git a/drivers/0x09_dht11_cbm/Src/vector_table.c b/drivers/0x09_dht11_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x09_dht11_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x09_dht11_cbm/build.log b/drivers/0x09_dht11_cbm/build.log new file mode 100644 index 0000000..ce4d94c Binary files /dev/null and b/drivers/0x09_dht11_cbm/build.log differ diff --git a/drivers/0x09_dht11_cbm/linker.ld b/drivers/0x09_dht11_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x09_dht11_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x09_dht11_rust/.cargo/config.toml b/drivers/0x09_dht11_rust/.cargo/config.toml new file mode 100644 index 0000000..1791d74 --- /dev/null +++ b/drivers/0x09_dht11_rust/.cargo/config.toml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2021–2024 The rp-rs Developers +# Copyright (c) 2021 rp-rs organization +# Copyright (c) 2025 Raspberry Pi Ltd. +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# You might want to make a similar file in your own repository if you are +# writing programs for Raspberry Silicon microcontrollers. +# + +[build] +target = "thumbv8m.main-none-eabihf" +# Set the default target to match the Cortex-M33 in the RP2350 +# target = "thumbv8m.main-none-eabihf" +# target = "thumbv6m-none-eabi" +# target = "riscv32imac-unknown-none-elf" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the hard-float ABI for Arm mode. +# +# The FPU is enabled by default, and float function arguments use FPU +# registers. +[target.thumbv8m.main-none-eabihf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as a linker script. +# This is usually provided by the cortex-m-rt crate, and by default the +# version in that crate will include a file called `memory.x` which describes +# the particular memory layout for your specific chip. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +# This is the soft-float ABI for RISC-V mode. +# +# Hazard 3 does not have an FPU and so float function arguments use integer +# registers. +[target.riscv32imac-unknown-none-elf] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Trp235x_riscv.x also tells the linker to use +# `rp235x_riscv.x` as a linker script. This adds in RP2350 RISC-V specific +# things that the riscv-rt crate's `link.x` requires and then includes +# `link.x` automatically. This is the reverse of how we do it on Cortex-M. +# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a +# secondary linker script. This is required to make defmt_rtt work. +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] + +# Use picotool for loading. +# +# Load an elf, skipping unchanged flash sectors, verify it, and execute it +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" +#runner = "probe-rs run --chip ${CHIP} --protocol swd" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x09_dht11_rust/.gitignore b/drivers/0x09_dht11_rust/.gitignore new file mode 100644 index 0000000..03381c1 --- /dev/null +++ b/drivers/0x09_dht11_rust/.gitignore @@ -0,0 +1,113 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,macos,windows,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,macos,windows,linux diff --git a/drivers/0x09_dht11_rust/.pico-rs b/drivers/0x09_dht11_rust/.pico-rs new file mode 100644 index 0000000..1b6702a --- /dev/null +++ b/drivers/0x09_dht11_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 \ No newline at end of file diff --git a/drivers/0x09_dht11_rust/.vscode/extensions.json b/drivers/0x09_dht11_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x09_dht11_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x09_dht11_rust/.vscode/launch.json b/drivers/0x09_dht11_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x09_dht11_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x09_dht11_rust/.vscode/settings.json b/drivers/0x09_dht11_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x09_dht11_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x09_dht11_rust/.vscode/tasks.json b/drivers/0x09_dht11_rust/.vscode/tasks.json new file mode 100644 index 0000000..4194af4 --- /dev/null +++ b/drivers/0x09_dht11_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/drivers/0x09_dht11_rust/Cargo.toml b/drivers/0x09_dht11_rust/Cargo.toml new file mode 100644 index 0000000..7696d36 --- /dev/null +++ b/drivers/0x09_dht11_rust/Cargo.toml @@ -0,0 +1,39 @@ +[package] +edition = "2024" +name = "dht11" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "dht11_lib" +path = "src/lib.rs" + +[[bin]] +name = "dht11" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x09_dht11_rust/LICENSE-APACHE b/drivers/0x09_dht11_rust/LICENSE-APACHE new file mode 100644 index 0000000..8d99cbc --- /dev/null +++ b/drivers/0x09_dht11_rust/LICENSE-APACHE @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2021–2024 The rp-rs Developers + Copyright (c) 2021 rp-rs organization + Copyright (c) 2025 Raspberry Pi Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/drivers/0x09_dht11_rust/LICENSE-MIT b/drivers/0x09_dht11_rust/LICENSE-MIT new file mode 100644 index 0000000..5369c70 --- /dev/null +++ b/drivers/0x09_dht11_rust/LICENSE-MIT @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2021–2024 The rp-rs Developers +Copyright (c) 2021 rp-rs organization +Copyright (c) 2025 Raspberry Pi Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + \ No newline at end of file diff --git a/drivers/0x09_dht11_rust/build.rs b/drivers/0x09_dht11_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x09_dht11_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x09_dht11_rust/rp2040.x b/drivers/0x09_dht11_rust/rp2040.x new file mode 100644 index 0000000..0cc665b --- /dev/null +++ b/drivers/0x09_dht11_rust/rp2040.x @@ -0,0 +1,91 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + /* + * Here we assume you have 2048 KiB of Flash. This is what the Pi Pico + * has, but your board may have more or less Flash and you should adjust + * this value to suit. + */ + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + /* + * RAM consists of 4 banks, SRAM0-SRAM3, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K + /* + * RAM banks 4 and 5 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k + + /* SRAM banks 0-3 can also be accessed directly. However, those ranges + alias with the RAM mapping, above. So don't use them at the same time! + SRAM0 : ORIGIN = 0x21000000, LENGTH = 64k + SRAM1 : ORIGIN = 0x21010000, LENGTH = 64k + SRAM2 : ORIGIN = 0x21020000, LENGTH = 64k + SRAM3 : ORIGIN = 0x21030000, LENGTH = 64k + */ +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader + * + * An executable block of code which sets up the QSPI interface for + * 'Execute-In-Place' (or XIP) mode. Also sends chip-specific commands to + * the external flash chip. + * + * Must go at the start of external flash, where the Boot ROM expects it. + */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 512 bytes of flash, + * where picotool can find it + */ + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x09_dht11_rust/rp2350.x b/drivers/0x09_dht11_rust/rp2350.x new file mode 100644 index 0000000..bda94d8 --- /dev/null +++ b/drivers/0x09_dht11_rust/rp2350.x @@ -0,0 +1,83 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 4K of flash + * where the Boot ROM (and picotool) can find it + */ + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + /* move .text to start /after/ the boot info */ + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + /* ### Boot ROM extra info + * + * Goes after everything in our program, so it can contain a signature. + */ + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + \ No newline at end of file diff --git a/drivers/0x09_dht11_rust/rp2350_riscv.x b/drivers/0x09_dht11_rust/rp2350_riscv.x new file mode 100644 index 0000000..84388aa --- /dev/null +++ b/drivers/0x09_dht11_rust/rp2350_riscv.x @@ -0,0 +1,259 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021–2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + /* + * The RP2350 has either external or internal flash. + * + * 2 MiB is a safe default here, although a Pico 2 has 4 MiB. + */ + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + /* + * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping. + * This is usually good for performance, as it distributes load on + * those banks evenly. + */ + RAM : ORIGIN = 0x20000000, LENGTH = 512K + /* + * RAM banks 8 and 9 use a direct mapping. They can be used to have + * memory areas dedicated for some specific job, improving predictability + * of access times. + * Example: Separate stacks for core0 and core1. + */ + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol is not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `32`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > FLASH + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + __start_block_addr = .; + KEEP(*(.start_block)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + . = ALIGN(4); + } > FLASH + + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > FLASH + + .data : ALIGN(32) + { + _sidata = LOADADDR(.data); + __sidata = LOADADDR(.data); + _sdata = .; + __sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(32); + _edata = .; + __edata = .; + } > RAM AT > FLASH + + .bss (NOLOAD) : ALIGN(32) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(32); + _ebss = .; + } > RAM + + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > RAM + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > RAM + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(start_to_end = __end_block_addr - __start_block_addr); +PROVIDE(end_to_start = __start_block_addr - __end_block_addr); + + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(FLASH) % 4 == 0, " +ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned"); + +ASSERT(ORIGIN(RAM) % 32 == 0, " +ERROR(riscv-rt): the start of the RAM must be 32-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, " +BUG(riscv-rt): .data is not 32-byte aligned"); + +ASSERT(_sidata % 32 == 0, " +BUG(riscv-rt): the LMA of .data is not 32-byte aligned"); + +ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, " +BUG(riscv-rt): .bss is not 32-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " +ERROR(riscv-rt): The .text section must be placed inside the FLASH region. +Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/drivers/0x09_dht11_rust/src/board.rs b/drivers/0x09_dht11_rust/src/board.rs new file mode 100644 index 0000000..a235e98 --- /dev/null +++ b/drivers/0x09_dht11_rust/src/board.rs @@ -0,0 +1,580 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// DHT11 pure-logic functions (checksum, parsing, formatting) +use dht11_lib::dht11; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Timer device type for the HAL timer peripheral. +#[cfg(rp2350)] +pub(crate) type HalTimer = hal::Timer; +/// Timer type alias for RP2040 (non-generic). +#[cfg(rp2040)] +pub(crate) type HalTimer = hal::Timer; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// GPIO pin number connected to the DHT11 data line. +pub(crate) const DHT11_GPIO: u8 = 4; + +/// Polling interval in milliseconds (DHT11 minimum is 2 seconds). +pub(crate) const POLL_MS: u32 = 2_000; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Set or clear the DHT11 GPIO output level via SIO registers. +/// +/// # Arguments +/// +/// * `sio` - The `sio` parameter. +/// * `high` - The `high` parameter. +/// +/// # Arguments +/// +/// * `sio` - The `sio` parameter. +/// * `high` - The `high` parameter. +fn sio_set_level(sio: &hal::pac::sio::RegisterBlock, high: bool) { + if high { + sio.gpio_out_set() + .write(|w| unsafe { w.bits(1u32 << DHT11_GPIO) }); + } else { + sio.gpio_out_clr() + .write(|w| unsafe { w.bits(1u32 << DHT11_GPIO) }); + } +} + +/// Drive the DHT11 data pin to a given level (enables output). +/// +/// # Arguments +/// +/// * `high` - `true` to drive HIGH, `false` to drive LOW. +/// +/// # Arguments +/// +/// * `high` - The `high` parameter. +fn gpio_drive(high: bool) { + unsafe { + let sio = &*hal::pac::SIO::PTR; + sio_set_level(sio, high); + sio.gpio_oe_set().write(|w| w.bits(1u32 << DHT11_GPIO)); + } +} + +/// Release the DHT11 data pin back to input mode (disable output driver). +fn gpio_release() { + unsafe { + let sio = &*hal::pac::SIO::PTR; + sio.gpio_oe_clr().write(|w| w.bits(1u32 << DHT11_GPIO)); + } +} + +/// Read the current logic level of the DHT11 data pin. +/// +/// # Returns +/// +/// `true` if the pin reads HIGH, `false` if LOW. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn gpio_read() -> bool { + unsafe { (*hal::pac::SIO::PTR).gpio_in().read().bits() & (1u32 << DHT11_GPIO) != 0 } +} + +/// Read the free-running microsecond timer (lower 32 bits). +/// +/// # Arguments +/// +/// * `timer` - Reference to the HAL timer peripheral. +/// +/// # Returns +/// +/// Current timer value in microseconds (wrapping at 2^32). +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +fn time_us_32(timer: &HalTimer) -> u32 { + timer.get_counter().ticks() as u32 +} + +/// Send the DHT11 start signal on the data pin. +/// +/// Drives the pin LOW for 18 ms then HIGH for 40 us before switching +/// the pin to input mode to listen for the sensor response. +/// +/// # Arguments +/// +/// * `delay` - Mutable reference to the blocking delay provider. +/// +/// # Arguments +/// +/// * `delay` - Delay value. +fn send_start_signal(delay: &mut cortex_m::delay::Delay) { + gpio_drive(false); + delay.delay_ms(18); + gpio_drive(true); + delay.delay_us(40); + gpio_release(); +} + +/// Spin until the pin leaves the given logic level, or time out. +/// +/// # Arguments +/// +/// * `level` - Logic level to wait through (`true` = HIGH, `false` = LOW). +/// +/// # Returns +/// +/// `true` once the level changed, `false` on timeout. +/// +/// # Arguments +/// +/// * `level` - The `level` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn wait_for_level(level: bool) -> bool { + let mut timeout: u32 = dht11::LEVEL_WAIT_TIMEOUT; + while gpio_read() == level { + timeout -= 1; + if timeout == 0 { + return false; + } + } + true +} + +/// Wait for the DHT11 response after the start signal. +/// +/// The sensor pulls LOW then HIGH then LOW again; each transition +/// is awaited with a timeout. +/// +/// # Returns +/// +/// `true` if the full response was received, `false` on timeout. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn wait_response() -> bool { + wait_for_level(true) && wait_for_level(false) && wait_for_level(true) +} + +/// Measure the high-period duration for a single DHT11 bit. +/// +/// # Arguments +/// +/// * `timer` - Reference to the HAL timer for microsecond measurement. +/// +/// # Returns +/// +/// Duration in microseconds, or `None` on timeout. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// An Optional value. +fn measure_bit_duration(timer: &HalTimer) -> Option { + if !wait_for_level(false) { + return None; + } + let start = time_us_32(timer); + if !wait_for_level(true) { + return None; + } + Some(time_us_32(timer).wrapping_sub(start)) +} + +/// Read a single bit from the DHT11 data stream. +/// +/// Waits for the low-period to end, measures the high-period duration, +/// and accumulates the result into the data array via +/// [`dht11::accumulate_bit`]. +/// +/// # Arguments +/// +/// * `data` - 5-byte array accumulating the received bits. +/// * `i` - Bit index (0–39). +/// * `timer` - Reference to the HAL timer for microsecond measurement. +/// +/// # Returns +/// +/// `true` on success, `false` on timeout. +/// +/// # Arguments +/// +/// * `data` - Data to send/write. +/// * `i` - The `i` parameter. +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn read_bit(data: &mut [u8; 5], i: usize, timer: &HalTimer) -> bool { + let Some(duration) = measure_bit_duration(timer) else { + return false; + }; + dht11::accumulate_bit(data, i, duration); + true +} + +/// Read all 40 data bits from the DHT11. +/// +/// # Arguments +/// +/// * `data` - 5-byte array filled with the received data. +/// * `timer` - Reference to the HAL timer for microsecond measurement. +/// +/// # Returns +/// +/// `true` if all 40 bits were read, `false` on timeout. +/// +/// # Arguments +/// +/// * `data` - Data to send/write. +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn read_40_bits(data: &mut [u8; 5], timer: &HalTimer) -> bool { + for i in 0..40 { + if !read_bit(data, i, timer) { + return false; + } + } + true +} + +/// Run the full DHT11 acquisition sequence (start, response, 40 bits, checksum). +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// * `delay` - Delay value. +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// * `delay` - Delay value. +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn acquire_data(timer: &HalTimer, delay: &mut cortex_m::delay::Delay, data: &mut [u8; 5]) -> bool { + send_start_signal(delay); + wait_response() && read_40_bits(data, timer) && dht11::validate_checksum(data) +} + +/// Execute the full DHT11 read protocol. +/// +/// Sends the start signal, waits for the sensor response, reads 40 bits +/// of data, validates the checksum, and parses humidity and temperature. +/// +/// # Arguments +/// +/// * `timer` - Reference to the HAL timer for microsecond measurement. +/// * `delay` - Mutable reference to the blocking delay provider. +/// +/// # Returns +/// +/// `Some((humidity, temperature))` on success, `None` on failure. +pub(crate) fn read_sensor( + timer: &HalTimer, + delay: &mut cortex_m::delay::Delay, +) -> Option<(f32, f32)> { + let mut data = [0u8; 5]; + if !acquire_data(timer, delay, &mut data) { + return None; + } + Some(( + dht11::parse_humidity(&data), + dht11::parse_temperature(&data), + )) +} + +/// Format the sensor result (or error) with CRLF into `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `result` - The `result` parameter. +/// * `f32)>` - The `f32)>` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `result` - The `result` parameter. +/// * `f32)>` - The `f32)>` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_sensor_output(buf: &mut [u8], result: Option<(f32, f32)>) -> usize { + let n = match result { + Some((h, t)) => dht11::format_reading(buf, h, t), + None => dht11::format_error(buf, DHT11_GPIO), + }; + buf[n] = b'\r'; + buf[n + 1] = b'\n'; + n + 2 +} + +/// Read the sensor, format the result, write it over UART, and wait. +/// +/// On a successful read, prints humidity and temperature; on failure, +/// prints a wiring-check message. Always waits [`POLL_MS`] before +/// returning to respect the DHT11 minimum polling interval. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `timer` - Reference to the HAL timer for microsecond measurement. +/// * `delay` - Mutable reference to the blocking delay provider. +pub(crate) fn poll_sensor( + uart: &EnabledUart, + timer: &HalTimer, + delay: &mut cortex_m::delay::Delay, +) { + let mut buf = [0u8; 64]; + let result = read_sensor(timer, delay); + let n = format_sensor_output(&mut buf, result); + uart.write_full_blocking(&buf[..n]); + delay.delay_ms(POLL_MS); +} + +/// Initialise all peripherals and run the DHT11 sensor demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + #[cfg(rp2350)] + let timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks); + #[cfg(rp2040)] + let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS); + let _ = p.gpio4.into_pull_up_input(); + uart.write_full_blocking(b"DHT11 driver initialized on GPIO 4\r\n"); + loop { + poll_sensor(&uart, &timer, &mut delay); + } +} + diff --git a/drivers/0x09_dht11_rust/src/dht11.rs b/drivers/0x09_dht11_rust/src/dht11.rs new file mode 100644 index 0000000..b410b40 --- /dev/null +++ b/drivers/0x09_dht11_rust/src/dht11.rs @@ -0,0 +1,531 @@ +//! Implementation module +//! +//! **File:** `dht11.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// Bit-duration threshold in microseconds. +/// +/// If the high pulse is longer than this value, the bit is a 1; otherwise 0. +pub const BIT_THRESHOLD_US: u32 = 40; + +/// Timeout in spin-loop iterations for waiting on a pin level change. +pub const LEVEL_WAIT_TIMEOUT: u32 = 10_000; + +/// Accumulate a single received bit into the 5-byte data array. +/// +/// Shifts `data[byte_index]` left by one and sets the LSB to 1 if the +/// measured high-pulse `duration_us` exceeds [`BIT_THRESHOLD_US`]. +/// +/// # Arguments +/// +/// * `data` - 5-byte array accumulating the received bits. +/// * `bit_index` - Bit index (0..39). +/// * `duration_us` - Measured duration of the high pulse in microseconds. +/// +/// # Arguments +/// +/// * `data` - Data to send/write. +/// * `bit_index` - The `bit_index` parameter. +/// * `duration_us` - The `duration_us` parameter. +pub fn accumulate_bit(data: &mut [u8; 5], bit_index: usize, duration_us: u32) { + let byte = bit_index / 8; + data[byte] <<= 1; + if duration_us > BIT_THRESHOLD_US { + data[byte] |= 1; + } +} + +/// Verify the DHT11 checksum byte. +/// +/// The checksum (byte 4) must equal the lower 8 bits of the sum of +/// bytes 0 through 3. +/// +/// # Arguments +/// +/// * `data` - 5-byte received data (bytes 0–3 plus checksum in byte 4). +/// +/// # Returns +/// +/// `true` if the checksum matches, `false` otherwise. +/// +/// # Arguments +/// +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +pub fn validate_checksum(data: &[u8; 5]) -> bool { + data[4] + == (data[0] + .wrapping_add(data[1]) + .wrapping_add(data[2]) + .wrapping_add(data[3])) +} + +/// Parse humidity from the raw DHT11 data bytes. +/// +/// Humidity is encoded as an integer part in byte 0 and a fractional +/// part (tenths) in byte 1. +/// +/// # Arguments +/// +/// * `data` - 5-byte received data. +/// +/// # Returns +/// +/// Humidity as a percentage (e.g. 65.3 for 65.3%). +/// +/// # Arguments +/// +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// A value of type `f32`. +pub fn parse_humidity(data: &[u8; 5]) -> f32 { + data[0] as f32 + data[1] as f32 * 0.1 +} + +/// Parse temperature from the raw DHT11 data bytes. +/// +/// Temperature is encoded as an integer part in byte 2 and a fractional +/// part (tenths) in byte 3. +/// +/// # Arguments +/// +/// * `data` - 5-byte received data. +/// +/// # Returns +/// +/// Temperature in degrees Celsius. +/// +/// # Arguments +/// +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// A value of type `f32`. +pub fn parse_temperature(data: &[u8; 5]) -> f32 { + data[2] as f32 + data[3] as f32 * 0.1 +} + +/// Format a sensor reading as `"Humidity: XX.X% Temperature: XX.X C"`. +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 40 bytes). +/// * `humidity` - Humidity percentage. +/// * `temperature` - Temperature in degrees Celsius. +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `humidity` - The `humidity` parameter. +/// * `temperature` - The `temperature` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_reading(buf: &mut [u8], humidity: f32, temperature: f32) -> usize { + let mut pos = copy_slice(buf, 0, b"Humidity: "); + pos += format_f32_1(&mut buf[pos..], humidity); + pos += copy_slice(buf, pos, b"% Temperature: "); + pos += format_f32_1(&mut buf[pos..], temperature); + pos += copy_slice(buf, pos, b" C"); + pos +} + +/// Copy a byte slice into `buf` at the given offset, returning bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `offset` - The `offset` parameter. +/// * `src` - The `src` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `offset` - The `offset` parameter. +/// * `src` - The `src` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn copy_slice(buf: &mut [u8], offset: usize, src: &[u8]) -> usize { + buf[offset..offset + src.len()].copy_from_slice(src); + src.len() +} + +/// Format a failed-read error message. +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 42 bytes). +/// * `gpio` - GPIO pin number to include in the message. +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `gpio` - The `gpio` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_error(buf: &mut [u8], gpio: u8) -> usize { + let mut pos = 0; + let prefix = b"DHT11 read failed - check wiring on GPIO "; + buf[pos..pos + prefix.len()].copy_from_slice(prefix); + pos += prefix.len(); + pos += format_u8(&mut buf[pos..], gpio); + pos +} + +/// Return the number of decimal digits needed for a `u8`. +/// +/// # Arguments +/// +/// * `val` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `val` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn u8_digit_count(val: u8) -> usize { + if val >= 100 { + 3 + } else if val >= 10 { + 2 + } else { + 1 + } +} + +/// Write `count` decimal digits of `val` into `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `val` - Value to use. +/// * `count` - The `count` parameter. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `val` - Value to use. +/// * `count` - The `count` parameter. +fn write_u8_digits(buf: &mut [u8], val: u8, count: usize) { + if count >= 3 { + buf[0] = b'0' + val / 100; + } + if count >= 2 { + buf[count - 2] = b'0' + (val / 10) % 10; + } + buf[count - 1] = b'0' + val % 10; +} + +/// Format a `u8` as decimal ASCII digits. +/// +/// # Arguments +/// +/// * `buf` - Output buffer. +/// * `val` - Value to format. +/// +/// # Returns +/// +/// Number of bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `val` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_u8(buf: &mut [u8], val: u8) -> usize { + let n = u8_digit_count(val); + write_u8_digits(buf, val, n); + n +} + +/// Format an `f32` with one decimal place (e.g. `"25.3"`). +/// +/// # Arguments +/// +/// * `buf` - Output buffer. +/// * `val` - Value to format. +/// +/// # Returns +/// +/// Number of bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `val` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_f32_1(buf: &mut [u8], val: f32) -> usize { + let scaled = (val * 10.0) as u32; + let frac = (scaled % 10) as u8; + let pos = format_u32_minimal(buf, scaled / 10); + buf[pos] = b'.'; + buf[pos + 1] = b'0' + frac; + pos + 2 +} + +/// Write a conditional digit into `buf` if `val` meets the threshold. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `val` - Value to use. +/// * `threshold` - The `threshold` parameter. +/// * `divisor` - The `divisor` parameter. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `val` - Value to use. +/// * `threshold` - The `threshold` parameter. +/// * `divisor` - The `divisor` parameter. +fn write_digit_if(buf: &mut [u8], pos: &mut usize, val: u32, threshold: u32, divisor: u32) { + if val >= threshold { + buf[*pos] = b'0' + ((val / divisor) % 10) as u8; + *pos += 1; + } +} + +/// Format a u32 as minimal decimal digits (no leading zeros). +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_u32_minimal(buf: &mut [u8], value: u32) -> usize { + let mut pos = 0; + write_digit_if(buf, &mut pos, value, 100, 100); + write_digit_if(buf, &mut pos, value, 10, 10); + buf[pos] = b'0' + (value % 10) as u8; + pos + 1 +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the accumulate bit zero short pulse operation. + #[test] + fn accumulate_bit_zero_short_pulse() { + let mut data = [0u8; 5]; + accumulate_bit(&mut data, 0, 30); + assert_eq!(data[0], 0); + } + + /// Executes the accumulate bit one long pulse operation. + #[test] + fn accumulate_bit_one_long_pulse() { + let mut data = [0u8; 5]; + accumulate_bit(&mut data, 0, 70); + assert_eq!(data[0], 1); + } + + /// Executes the accumulate bit threshold exact operation. + #[test] + fn accumulate_bit_threshold_exact() { + let mut data = [0u8; 5]; + accumulate_bit(&mut data, 0, BIT_THRESHOLD_US); + assert_eq!(data[0], 0); + } + + /// Executes the accumulate bit two bits operation. + #[test] + fn accumulate_bit_two_bits() { + let mut data = [0u8; 5]; + accumulate_bit(&mut data, 0, 70); + accumulate_bit(&mut data, 1, 30); + assert_eq!(data[0], 0b10); + } + + /// Executes the accumulate bit crosses byte operation. + #[test] + fn accumulate_bit_crosses_byte() { + let mut data = [0u8; 5]; + accumulate_bit(&mut data, 7, 70); + accumulate_bit(&mut data, 8, 70); + assert_eq!(data[0], 1); + assert_eq!(data[1], 1); + } + + /// Executes the validate checksum valid operation. + #[test] + fn validate_checksum_valid() { + let data = [0x28, 0x00, 0x1A, 0x00, 0x42]; + assert!(validate_checksum(&data)); + } + + /// Executes the validate checksum invalid operation. + #[test] + fn validate_checksum_invalid() { + let data = [0x28, 0x00, 0x1A, 0x00, 0xFF]; + assert!(!validate_checksum(&data)); + } + + /// Executes the validate checksum wraps operation. + #[test] + fn validate_checksum_wraps() { + let data = [0xFF, 0x01, 0x00, 0x00, 0x00]; + assert!(validate_checksum(&data)); + } + + /// Executes the parse humidity integer only operation. + #[test] + fn parse_humidity_integer_only() { + let data = [0x41, 0x00, 0x00, 0x00, 0x41]; + let h = parse_humidity(&data); + assert!((h - 65.0).abs() < 0.01); + } + + /// Executes the parse humidity with fraction operation. + #[test] + fn parse_humidity_with_fraction() { + let data = [0x41, 0x03, 0x00, 0x00, 0x44]; + let h = parse_humidity(&data); + assert!((h - 65.3).abs() < 0.01); + } + + /// Executes the parse temperature integer only operation. + #[test] + fn parse_temperature_integer_only() { + let data = [0x00, 0x00, 0x19, 0x00, 0x19]; + let t = parse_temperature(&data); + assert!((t - 25.0).abs() < 0.01); + } + + /// Executes the parse temperature with fraction operation. + #[test] + fn parse_temperature_with_fraction() { + let data = [0x00, 0x00, 0x19, 0x05, 0x1E]; + let t = parse_temperature(&data); + assert!((t - 25.5).abs() < 0.01); + } + + /// Executes the format reading typical operation. + #[test] + fn format_reading_typical() { + let mut buf = [0u8; 48]; + let n = format_reading(&mut buf, 65.0, 25.0); + assert_eq!(&buf[..n], b"Humidity: 65.0% Temperature: 25.0 C"); + } + + /// Executes the format reading with fractions operation. + #[test] + fn format_reading_with_fractions() { + let mut buf = [0u8; 48]; + let n = format_reading(&mut buf, 65.3, 25.5); + assert_eq!(&buf[..n], b"Humidity: 65.3% Temperature: 25.5 C"); + } + + /// Executes the format error gpio4 operation. + #[test] + fn format_error_gpio4() { + let mut buf = [0u8; 48]; + let n = format_error(&mut buf, 4); + assert_eq!(&buf[..n], b"DHT11 read failed - check wiring on GPIO 4"); + } + + /// Executes the format error gpio15 operation. + #[test] + fn format_error_gpio15() { + let mut buf = [0u8; 48]; + let n = format_error(&mut buf, 15); + assert_eq!(&buf[..n], b"DHT11 read failed - check wiring on GPIO 15"); + } + + /// Executes the format u8 single digit operation. + #[test] + fn format_u8_single_digit() { + let mut buf = [0u8; 4]; + let n = format_u8(&mut buf, 4); + assert_eq!(&buf[..n], b"4"); + } + + /// Executes the format u8 two digits operation. + #[test] + fn format_u8_two_digits() { + let mut buf = [0u8; 4]; + let n = format_u8(&mut buf, 15); + assert_eq!(&buf[..n], b"15"); + } + + /// Executes the format u8 three digits operation. + #[test] + fn format_u8_three_digits() { + let mut buf = [0u8; 4]; + let n = format_u8(&mut buf, 255); + assert_eq!(&buf[..n], b"255"); + } +} diff --git a/drivers/0x09_dht11_rust/src/lib.rs b/drivers/0x09_dht11_rust/src/lib.rs new file mode 100644 index 0000000..bc3d4aa --- /dev/null +++ b/drivers/0x09_dht11_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod dht11; diff --git a/drivers/0x09_dht11_rust/src/main.rs b/drivers/0x09_dht11_rust/src/main.rs new file mode 100644 index 0000000..c80f3f1 --- /dev/null +++ b/drivers/0x09_dht11_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// DHT11 driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the DHT11 sensor demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"DHT11 Sensor Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x0a_ir/.vscode/.vscode/c_cpp_properties.json b/drivers/0x0a_ir/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0a_ir/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0a_ir/.vscode/.vscode/cmake-kits.json b/drivers/0x0a_ir/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0a_ir/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0a_ir/.vscode/.vscode/extensions.json b/drivers/0x0a_ir/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0a_ir/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0a_ir/.vscode/.vscode/launch.json b/drivers/0x0a_ir/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0a_ir/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0a_ir/.vscode/.vscode/settings.json b/drivers/0x0a_ir/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0a_ir/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0a_ir/.vscode/.vscode/tasks.json b/drivers/0x0a_ir/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0a_ir/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0a_ir/.vscode/c_cpp_properties.json b/drivers/0x0a_ir/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0a_ir/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0a_ir/.vscode/cmake-kits.json b/drivers/0x0a_ir/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0a_ir/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0a_ir/.vscode/extensions.json b/drivers/0x0a_ir/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0a_ir/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0a_ir/.vscode/launch.json b/drivers/0x0a_ir/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0a_ir/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0a_ir/.vscode/settings.json b/drivers/0x0a_ir/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0a_ir/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0a_ir/.vscode/tasks.json b/drivers/0x0a_ir/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0a_ir/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0a_ir/0x0a_ir.c b/drivers/0x0a_ir/0x0a_ir.c new file mode 100644 index 0000000..8c5027b --- /dev/null +++ b/drivers/0x0a_ir/0x0a_ir.c @@ -0,0 +1,65 @@ +/** + * @file 0x0a_ir.c + * @brief NEC IR (infrared) receiver driver for the Raspberry Pi Pico 2 + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * This driver demonstrates NEC infrared remote decoding using the + * ir.c/ir.h driver. The IR receiver module output is connected to GPIO 5. + * ir_getkey() blocks until a valid NEC frame is received or a timeout + * occurs. Each decoded command byte is printed over UART in hex and decimal. + * + * Wiring: + * GPIO5 -> IR receiver module OUT pin (e.g. VS1838B or TSOP4838) + * 3.3V -> IR receiver module VCC + * GND -> IR receiver module GND + */ + +#include +#include "pico/stdlib.h" +#include "ir.h" + +/** @brief GPIO pin connected to the IR receiver output */ +#define IR_GPIO 5 + +/** + * @brief Poll for an NEC frame and print the command if received + */ +static void poll_and_print(void) { + int command = ir_getkey(); + if (command >= 0) + printf("NEC command: 0x%02X (%d)\r\n", command, command); +} + +int main(void) { + stdio_init_all(); + ir_init(IR_GPIO); + printf("NEC IR driver initialized on GPIO %d\r\n", IR_GPIO); + printf("Press a button on your NEC remote...\r\n"); + while (true) + poll_and_print(); +} diff --git a/drivers/0x0a_ir/CMakeLists.txt b/drivers/0x0a_ir/CMakeLists.txt new file mode 100644 index 0000000..e28c847 --- /dev/null +++ b/drivers/0x0a_ir/CMakeLists.txt @@ -0,0 +1,56 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0a_ir C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0a_ir 0x0a_ir.c ir.c) + +pico_set_program_name(0x0a_ir "0x0a_ir") +pico_set_program_version(0x0a_ir "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0a_ir 1) +pico_enable_stdio_usb(0x0a_ir 0) + +# Add the standard library to the build +target_link_libraries(0x0a_ir + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0a_ir PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0a_ir) diff --git a/drivers/0x0a_ir/ir.c b/drivers/0x0a_ir/ir.c new file mode 100644 index 0000000..9d5b42a --- /dev/null +++ b/drivers/0x0a_ir/ir.c @@ -0,0 +1,130 @@ +/** + * @file ir.c + * @brief Implementation of NEC IR receiver (decoder) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ir.h" +#include "pico/stdlib.h" +#include "pico/time.h" +#include "hardware/gpio.h" + +/** @brief GPIO pin connected to the IR receiver */ +static unsigned int ir_pin = 0; + +/** + * @brief Wait for a GPIO pin to reach a given logic level + * + * Spins until the pin matches the requested level or a microsecond timeout + * is exceeded. Returns the elapsed time in microseconds, or -1 on timeout. + * + * @param gpio GPIO pin to monitor + * @param level Desired logic level (true = HIGH, false = LOW) + * @param timeout_us Maximum wait in microseconds + * @return int64_t Elapsed microseconds, or -1 on timeout + */ +static int64_t wait_for_level(unsigned int gpio, bool level, uint32_t timeout_us) { + absolute_time_t start = get_absolute_time(); + while (gpio_get(gpio) != level) { + if (absolute_time_diff_us(start, get_absolute_time()) > (int64_t)timeout_us) + return -1; + } + return absolute_time_diff_us(start, get_absolute_time()); +} + +/** + * @brief Wait for the NEC 9 ms leader pulse and 4.5 ms space + * + * @return bool true if a valid leader was detected, false on timeout + */ +static bool wait_leader(void) { + if (wait_for_level(ir_pin, 0, 150000) < 0) return false; + int64_t t = wait_for_level(ir_pin, 1, 12000); + if (t < 8000 || t > 10000) return false; + t = wait_for_level(ir_pin, 0, 7000); + if (t < 3500 || t > 5000) return false; + return true; +} + +/** + * @brief Read a single NEC-encoded bit from the IR receiver + * + * Measures the mark/space timing and shifts the result into the + * appropriate byte of the data array. + * + * @param data 4-byte array accumulating received bits + * @param i Bit index (0-31) + * @return bool true on success, false on timeout or protocol error + */ +static bool read_nec_bit(uint8_t *data, int i) { + if (wait_for_level(ir_pin, 1, 1000) < 0) return false; + int64_t t = wait_for_level(ir_pin, 0, 2500); + if (t < 200) return false; + int byte_idx = i / 8; + int bit_idx = i % 8; + if (t > 1200) data[byte_idx] |= (1 << bit_idx); + return true; +} + +/** + * @brief Read all 32 data bits of an NEC frame + * + * @param data 4-byte array filled with the received address and command + * @return bool true if all 32 bits were read, false on timeout + */ +static bool read_32_bits(uint8_t *data) { + for (int i = 0; i < 32; ++i) + if (!read_nec_bit(data, i)) return false; + return true; +} + +/** + * @brief Validate an NEC frame and extract the command byte + * + * Checks that the address and command pairs are bitwise-inverted. + * + * @param data 4-byte NEC frame (addr, ~addr, cmd, ~cmd) + * @return int Command byte (0-255) on success, -1 on validation failure + */ +static int validate_nec_frame(const uint8_t *data) { + if ((uint8_t)(data[0] + data[1]) == 0xFF && (uint8_t)(data[2] + data[3]) == 0xFF) + return data[2]; + return -1; +} + +void ir_init(uint8_t pin) { + ir_pin = pin; + gpio_init(pin); + gpio_set_dir(pin, GPIO_IN); + gpio_pull_up(pin); +} + +int ir_getkey(void) { + if (!wait_leader()) return -1; + uint8_t data[4] = {0, 0, 0, 0}; + if (!read_32_bits(data)) return -1; + return validate_nec_frame(data); +} diff --git a/drivers/0x0a_ir/ir.h b/drivers/0x0a_ir/ir.h new file mode 100644 index 0000000..fba6521 --- /dev/null +++ b/drivers/0x0a_ir/ir.h @@ -0,0 +1,56 @@ +/** + * @file ir.h + * @brief Header for NEC IR receiver (decoder) API + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IR_H +#define IR_H + +#include +#include + +/** + * @brief Initialize the IR receiver GPIO + * + * Configures the given `pin` as an input with pull-up for the NEC IR receiver. + * Call this once during board initialization before calling `ir_getkey()`. + * + * @param pin GPIO pin number connected to IR receiver output + */ +void ir_init(uint8_t pin); + +/** + * @brief Blocking NEC IR decoder + * + * Blocks while waiting for a complete NEC frame. On success returns the + * decoded command byte (0..255). On timeout or protocol error returns -1. + * + * @return decoded command byte (0..255) or -1 on failure + */ +int ir_getkey(void); + +#endif // IR_H diff --git a/drivers/0x0a_ir/pico_sdk_import.cmake b/drivers/0x0a_ir/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x0a_ir/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x0a_ir_cbm/.vscode/c_cpp_properties.json b/drivers/0x0a_ir_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x0a_ir_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x0a_ir_cbm/.vscode/extensions.json b/drivers/0x0a_ir_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0a_ir_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0a_ir_cbm/.vscode/launch.json b/drivers/0x0a_ir_cbm/.vscode/launch.json new file mode 100644 index 0000000..e2d3496 --- /dev/null +++ b/drivers/0x0a_ir_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/ir.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDir": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDir": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x0a_ir_cbm/.vscode/settings.json b/drivers/0x0a_ir_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x0a_ir_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x0a_ir_cbm/.vscode/tasks.json b/drivers/0x0a_ir_cbm/.vscode/tasks.json new file mode 100644 index 0000000..a1a0d13 --- /dev/null +++ b/drivers/0x0a_ir_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/ir.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/ir.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x0a_ir_cbm/Inc/rp2350.h b/drivers/0x0a_ir_cbm/Inc/rp2350.h new file mode 100644 index 0000000..ed70f0b --- /dev/null +++ b/drivers/0x0a_ir_cbm/Inc/rp2350.h @@ -0,0 +1,253 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define TIMER0_BASE 0x400B0000UL +#define TICKS_BASE 0x40108000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register 0x00 + __IO uint32_t STATUS; // Status register 0x04 + __IO uint32_t DORMANT; // Dormant mode 0x08 + __IO uint32_t STARTUP; // Startup delay 0x0C + __IO uint32_t COUNT; // Frequency count 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[12]; // GPOUT0..GPOUT3 registers 0x00-0x2C + __IO uint32_t CLK_REF_CTRL; // Reference clock control 0x30 + __IO uint32_t RESERVED1[5]; // CLK_REF_DIV..CLK_SYS_SELECTED 0x34-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control 0x00 + __IO uint32_t WDSEL; // Watchdog select 0x04 + __IO uint32_t RESET_DONE; // Reset done status 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status 0x00 + __IO uint32_t CTRL; // GPIO control 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define TIMER0 ((volatile uint32_t *) TIMER0_BASE) +#define TICKS ((volatile uint32_t *) TICKS_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLK_REF bit definitions + */ +#define CLK_REF_CTRL_SRC_XOSC 2U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_MASK (0x07U << CLK_PERI_CTRL_AUXSRC_SHIFT) +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_TIMER0_SHIFT 23U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U +#define IO_BANK0_CTRL_FUNCSEL_NULL 0x1FU + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U +#define PADS_BANK0_PUE_SHIFT 3U +#define PADS_BANK0_PDE_SHIFT 2U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_IN_OFFSET (0x004U / 4U) +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) +#define SIO_GPIO_OE_CLR_OFFSET (0x040U / 4U) + +/** + * @brief TIMER0 register offsets (word indices from TIMER0_BASE) + */ +#define TIMER_TIMERAWL_OFFSET (0x028U / 4U) + +/** + * @brief Tick generator register offsets (word indices from TICKS_BASE) + */ +#define TICKS_TIMER0_CTRL_OFFSET (0x018U / 4U) +#define TICKS_TIMER0_CYCLES_OFFSET (0x01CU / 4U) +#define TICKS_TIMER0_ENABLE 1U +#define TICKS_TIMER0_CYCLES_12MHZ 12U + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief NEC IR receiver GPIO pin definition + */ +#define IR_PIN 5U + +/** + * @brief NEC protocol timing thresholds (microseconds) + */ +#define NEC_LEADER_WAIT_US 150000U +#define NEC_LEADER_MARK_MIN_US 8000U +#define NEC_LEADER_MARK_MAX_US 10000U +#define NEC_LEADER_MARK_TIMEOUT_US 12000U +#define NEC_LEADER_SPACE_MIN_US 3500U +#define NEC_LEADER_SPACE_MAX_US 5000U +#define NEC_LEADER_SPACE_TIMEOUT_US 7000U +#define NEC_BIT_MARK_TIMEOUT_US 1000U +#define NEC_BIT_SPACE_TIMEOUT_US 2500U +#define NEC_BIT_SPACE_MIN_US 200U +#define NEC_BIT_ONE_THRESHOLD_US 1200U + +/** + * @brief NEC frame data format + */ +#define NEC_DATA_BYTES 4U +#define NEC_DATA_BITS 32U + +#endif /* __RP2350_H */ diff --git a/drivers/0x0a_ir_cbm/Inc/rp2350_delay.h b/drivers/0x0a_ir_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..79db58f --- /dev/null +++ b/drivers/0x0a_ir_cbm/Inc/rp2350_delay.h @@ -0,0 +1,51 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond and microsecond busy-wait delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +/** + * @brief Delay for the specified number of microseconds. + * @param us number of microseconds to delay + * @retval None + */ +void delay_us(uint32_t us); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x0a_ir_cbm/Inc/rp2350_ir.h b/drivers/0x0a_ir_cbm/Inc/rp2350_ir.h new file mode 100644 index 0000000..0e97201 --- /dev/null +++ b/drivers/0x0a_ir_cbm/Inc/rp2350_ir.h @@ -0,0 +1,61 @@ +/** + * @file rp2350_ir.h + * @brief NEC IR receiver driver for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Decodes NEC infrared remote frames on GPIO5 using SIO + * input reads and TIMER0 TIMERAWL for microsecond timing. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_IR_H +#define __RP2350_IR_H + +#include "rp2350.h" + +/** + * @brief Release TIMER0 from reset in the reset controller. + * @retval None + */ +void ir_timer_release_reset(void); + +/** + * @brief Start the TIMER0 tick generator for 1 us ticks at 12 MHz. + * @retval None + */ +void ir_timer_start_tick(void); + +/** + * @brief Configure GPIO5 pad and funcsel for SIO input with pull-up. + * @retval None + */ +void ir_init(void); + +/** + * @brief Block until a valid NEC frame is received or timeout. + * @retval int command byte (0-255) on success, -1 on failure + */ +int ir_getkey(void); + +#endif /* __RP2350_IR_H */ diff --git a/drivers/0x0a_ir_cbm/Inc/rp2350_reset.h b/drivers/0x0a_ir_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x0a_ir_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x0a_ir_cbm/Inc/rp2350_reset_handler.h b/drivers/0x0a_ir_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x0a_ir_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x0a_ir_cbm/Inc/rp2350_stack.h b/drivers/0x0a_ir_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x0a_ir_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x0a_ir_cbm/Inc/rp2350_uart.h b/drivers/0x0a_ir_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x0a_ir_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x0a_ir_cbm/Inc/rp2350_xosc.h b/drivers/0x0a_ir_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..80fd75d --- /dev/null +++ b/drivers/0x0a_ir_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,55 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +/** + * @brief Switch CLK_REF source to XOSC for a stable 12 MHz clk_sys. + * @retval None + */ +void xosc_set_clk_ref(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x0a_ir_cbm/Makefile b/drivers/0x0a_ir_cbm/Makefile new file mode 100644 index 0000000..7fec945 --- /dev/null +++ b/drivers/0x0a_ir_cbm/Makefile @@ -0,0 +1,83 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C NEC IR receiver driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\,$(BUILD_DIR)) mkdir $(subst /,\,$(BUILD_DIR)) + RM = if exist $(subst /,\,$(BUILD_DIR)) rmdir /s /q $(subst /,\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = ir + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_ir.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x0a_ir_cbm/Src/image_def.c b/drivers/0x0a_ir_cbm/Src/image_def.c new file mode 100644 index 0000000..ed17cda --- /dev/null +++ b/drivers/0x0a_ir_cbm/Src/image_def.c @@ -0,0 +1,42 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = +{ + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x0a_ir_cbm/Src/main.c b/drivers/0x0a_ir_cbm/Src/main.c new file mode 100644 index 0000000..f718c3d --- /dev/null +++ b/drivers/0x0a_ir_cbm/Src/main.c @@ -0,0 +1,129 @@ +/** + * @file main.c + * @brief NEC IR receiver demonstration. + * @author Kevin Thomas + * @date 2026 + * + * Decodes NEC infrared remote frames on GPIO5 and prints + * each command byte in hex and decimal over UART. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * GPIO5 -> IR receiver OUT (e.g. VS1838B or TSOP4838) + * 3.3V -> IR receiver VCC + * GND -> IR receiver GND + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_ir.h" +#include "rp2350_uart.h" +#include "rp2350_xosc.h" + +/** + * @brief Hex digit lookup table for byte-to-hex conversion. + */ +static const char hex_lut[16] = "0123456789ABCDEF"; + +/** + * @brief Print a byte as a two-digit hex string over UART. + * @param value byte to print + * @retval None + */ +static void print_hex(uint8_t value) +{ + char buf[3]; + buf[0] = hex_lut[value >> 4]; + buf[1] = hex_lut[value & 0x0FU]; + buf[2] = '\0'; + uart_puts(buf); +} + +/** + * @brief Convert a uint8_t to a decimal string. + * @param value number to convert (0-255) + * @param buf output buffer (at least 4 bytes) + * @retval None + */ +static void uint8_to_str(uint8_t value, char *buf) +{ + uint8_t idx = 0; + if (value >= 100) + buf[idx++] = (char)('0' + value / 100); + if (value >= 10) + buf[idx++] = (char)('0' + (value / 10) % 10); + buf[idx++] = (char)('0' + value % 10); + buf[idx] = '\0'; +} + +/** + * @brief Print a byte as a decimal string over UART. + * @param value byte to print (0-255) + * @retval None + */ +static void print_dec(uint8_t value) +{ + char buf[4]; + uint8_to_str(value, buf); + uart_puts(buf); +} + +/** + * @brief Print a decoded NEC command in hex and decimal over UART. + * @param command decoded command byte + * @retval None + */ +static void print_command(uint8_t command) +{ + uart_puts("NEC command: 0x"); + print_hex(command); + uart_puts(" ("); + print_dec(command); + uart_puts(")\r\n"); +} + +/** + * @brief Initialize clocks, timer, IR receiver, and announce over UART. + * @retval None + */ +static void ir_setup(void) +{ + xosc_set_clk_ref(); + ir_timer_release_reset(); + ir_timer_start_tick(); + ir_init(); + uart_puts("NEC IR driver initialized on GPIO5\r\n"); + uart_puts("Press a button on your NEC remote...\r\n"); +} + +int main(void) +{ + int command; + ir_setup(); + while (1) + { + command = ir_getkey(); + if (command >= 0) + print_command((uint8_t)command); + } +} diff --git a/drivers/0x0a_ir_cbm/Src/rp2350_delay.c b/drivers/0x0a_ir_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..65b0821 --- /dev/null +++ b/drivers/0x0a_ir_cbm/Src/rp2350_delay.c @@ -0,0 +1,64 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond and microsecond delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} + +void delay_us(uint32_t us) +{ + if (us == 0) + return; + __asm__ volatile ( + "mov r4, #4\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (us) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x0a_ir_cbm/Src/rp2350_ir.c b/drivers/0x0a_ir_cbm/Src/rp2350_ir.c new file mode 100644 index 0000000..2c248c3 --- /dev/null +++ b/drivers/0x0a_ir_cbm/Src/rp2350_ir.c @@ -0,0 +1,254 @@ +/** + * @file rp2350_ir.c + * @brief NEC IR receiver driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Decodes NEC infrared remote frames using bare-metal SIO + * GPIO input and TIMER0 microsecond timestamps. Validates + * the 32-bit frame (addr, ~addr, cmd, ~cmd) and returns + * the command byte. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_ir.h" + +/** + * @brief Bit mask for the IR receiver pin. + */ +#define IR_PIN_MASK (1U << IR_PIN) + +/** + * @brief Read the TIMER0 raw low register for a microsecond timestamp. + * @retval uint32_t current microsecond count + */ +static uint32_t time_us(void) +{ + return TIMER0[TIMER_TIMERAWL_OFFSET]; +} + +/** + * @brief Read the current level of GPIO5 from SIO input register. + * @retval bool true if pin is high, false if low + */ +static bool read_pin(void) +{ + return (SIO[SIO_GPIO_IN_OFFSET] & IR_PIN_MASK) != 0; +} + +/** + * @brief Wait for the pin to reach a given level with us timeout. + * @param level desired logic level (true=high, false=low) + * @param timeout_us maximum wait in microseconds + * @retval int32_t elapsed microseconds, or -1 on timeout + */ +static int32_t wait_for_level(bool level, uint32_t timeout_us) +{ + uint32_t start = time_us(); + while (read_pin() != level) + { + if ((time_us() - start) > timeout_us) + return -1; + } + return (int32_t)(time_us() - start); +} + +/** + * @brief Validate the 9 ms leader mark pulse duration. + * @retval bool true if within expected range, false on timeout or invalid + */ +static bool validate_leader_mark(void) +{ + int32_t t = wait_for_level(true, NEC_LEADER_MARK_TIMEOUT_US); + if (t < (int32_t)NEC_LEADER_MARK_MIN_US) + return false; + return t <= (int32_t)NEC_LEADER_MARK_MAX_US; +} + +/** + * @brief Validate the 4.5 ms leader space duration. + * @retval bool true if within expected range, false on timeout or invalid + */ +static bool validate_leader_space(void) +{ + int32_t t = wait_for_level(false, NEC_LEADER_SPACE_TIMEOUT_US); + if (t < (int32_t)NEC_LEADER_SPACE_MIN_US) + return false; + return t <= (int32_t)NEC_LEADER_SPACE_MAX_US; +} + +/** + * @brief Wait for the NEC 9 ms leader pulse and 4.5 ms space. + * @retval bool true if valid leader detected, false on timeout + */ +static bool wait_leader(void) +{ + if (wait_for_level(false, NEC_LEADER_WAIT_US) < 0) + return false; + if (!validate_leader_mark()) + return false; + return validate_leader_space(); +} + +/** + * @brief Wait for the bit mark, then measure the space duration. + * @param duration_out pointer to store the space duration + * @retval bool true on success, false on timeout or invalid + */ +static bool measure_bit_space(int32_t *duration_out) +{ + if (wait_for_level(true, NEC_BIT_MARK_TIMEOUT_US) < 0) + return false; + *duration_out = wait_for_level(false, NEC_BIT_SPACE_TIMEOUT_US); + return *duration_out >= (int32_t)NEC_BIT_SPACE_MIN_US; +} + +/** + * @brief Read a single NEC-encoded bit by measuring space duration. + * @param data 4-byte array accumulating received bits + * @param bit bit index (0-31) + * @retval bool true on success, false on timeout + */ +static bool read_nec_bit(uint8_t *data, uint8_t bit) +{ + int32_t t; + if (!measure_bit_space(&t)) + return false; + if (t > (int32_t)NEC_BIT_ONE_THRESHOLD_US) + data[bit / 8U] |= (1U << (bit % 8U)); + return true; +} + +/** + * @brief Read all 32 data bits of an NEC frame. + * @param data 4-byte array filled with received data + * @retval bool true if all bits read, false on timeout + */ +static bool read_32_bits(uint8_t *data) +{ + for (uint8_t i = 0; i < NEC_DATA_BITS; i++) + if (!read_nec_bit(data, i)) + return false; + return true; +} + +/** + * @brief Validate NEC frame and extract command byte. + * @param data 4-byte NEC frame (addr, ~addr, cmd, ~cmd) + * @retval int command byte (0-255) on success, -1 on failure + */ +static int validate_nec_frame(const uint8_t *data) +{ + uint8_t addr_check = (uint8_t)(data[0] + data[1]); + uint8_t cmd_check = (uint8_t)(data[2] + data[3]); + if (addr_check == 0xFFU && cmd_check == 0xFFU) + return (int)data[2]; + return -1; +} + +/** + * @brief Clear the TIMER0 reset bit in the reset controller. + * @retval None + */ +static void timer_clear_reset(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_TIMER0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until TIMER0 is out of reset. + * @retval None + */ +static void timer_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_TIMER0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO5 pad: enable input, pull-up, clear isolation. + * @retval None + */ +static void configure_pad(void) +{ + uint32_t value; + value = PADS_BANK0->GPIO[IR_PIN]; + value &= ~(1U << PADS_BANK0_OD_SHIFT); + value |= (1U << PADS_BANK0_IE_SHIFT); + value |= (1U << PADS_BANK0_PUE_SHIFT); + value &= ~(1U << PADS_BANK0_PDE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + PADS_BANK0->GPIO[IR_PIN] = value; +} + +/** + * @brief Set GPIO5 funcsel to SIO for software-controlled IO. + * @retval None + */ +static void configure_funcsel(void) +{ + IO_BANK0->GPIO[IR_PIN].CTRL = IO_BANK0_CTRL_FUNCSEL_SIO; +} + +/** + * @brief Set GPIO5 as input via SIO output-enable clear register. + * @retval None + */ +static void set_input(void) +{ + SIO[SIO_GPIO_OE_CLR_OFFSET] = IR_PIN_MASK; +} + +void ir_timer_release_reset(void) +{ + timer_clear_reset(); + timer_wait_reset_done(); +} + +void ir_timer_start_tick(void) +{ + TICKS[TICKS_TIMER0_CYCLES_OFFSET] = TICKS_TIMER0_CYCLES_12MHZ; + TICKS[TICKS_TIMER0_CTRL_OFFSET] = TICKS_TIMER0_ENABLE; +} + +void ir_init(void) +{ + configure_pad(); + configure_funcsel(); + set_input(); +} + +int ir_getkey(void) +{ + uint8_t data[NEC_DATA_BYTES] = {0}; + if (!wait_leader()) + return -1; + if (!read_32_bits(data)) + return -1; + int result = validate_nec_frame(data); + if (result < 0) + return -1; + return result; +} diff --git a/drivers/0x0a_ir_cbm/Src/rp2350_reset.c b/drivers/0x0a_ir_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x0a_ir_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x0a_ir_cbm/Src/rp2350_reset_handler.c b/drivers/0x0a_ir_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..62b58d2 --- /dev/null +++ b/drivers/0x0a_ir_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,80 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, then branches + * to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x0a_ir_cbm/Src/rp2350_stack.c b/drivers/0x0a_ir_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x0a_ir_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x0a_ir_cbm/Src/rp2350_uart.c b/drivers/0x0a_ir_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x0a_ir_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x0a_ir_cbm/Src/rp2350_xosc.c b/drivers/0x0a_ir_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..b33a71d --- /dev/null +++ b/drivers/0x0a_ir_cbm/Src/rp2350_xosc.c @@ -0,0 +1,54 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value &= ~CLK_PERI_CTRL_AUXSRC_MASK; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} + +void xosc_set_clk_ref(void) +{ + CLOCKS->CLK_REF_CTRL = CLK_REF_CTRL_SRC_XOSC; +} diff --git a/drivers/0x0a_ir_cbm/Src/vector_table.c b/drivers/0x0a_ir_cbm/Src/vector_table.c new file mode 100644 index 0000000..dd7ca9e --- /dev/null +++ b/drivers/0x0a_ir_cbm/Src/vector_table.c @@ -0,0 +1,45 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = +{ + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x0a_ir_cbm/build.log b/drivers/0x0a_ir_cbm/build.log new file mode 100644 index 0000000..5c237c4 Binary files /dev/null and b/drivers/0x0a_ir_cbm/build.log differ diff --git a/drivers/0x0a_ir_cbm/linker.ld b/drivers/0x0a_ir_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x0a_ir_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x0a_ir_rust/.cargo/config.toml b/drivers/0x0a_ir_rust/.cargo/config.toml new file mode 100644 index 0000000..70fb5fa --- /dev/null +++ b/drivers/0x0a_ir_rust/.cargo/config.toml @@ -0,0 +1,32 @@ +[build] +target = "thumbv8m.main-none-eabihf" + +[target.thumbv6m-none-eabi] +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.thumbv8m.main-none-eabihf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.riscv32imac-unknown-none-elf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x0a_ir_rust/.pico-rs b/drivers/0x0a_ir_rust/.pico-rs new file mode 100644 index 0000000..1b6702a --- /dev/null +++ b/drivers/0x0a_ir_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 \ No newline at end of file diff --git a/drivers/0x0a_ir_rust/.vscode/extensions.json b/drivers/0x0a_ir_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x0a_ir_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0a_ir_rust/.vscode/launch.json b/drivers/0x0a_ir_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x0a_ir_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x0a_ir_rust/.vscode/settings.json b/drivers/0x0a_ir_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x0a_ir_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x0a_ir_rust/.vscode/tasks.json b/drivers/0x0a_ir_rust/.vscode/tasks.json new file mode 100644 index 0000000..d288f27 --- /dev/null +++ b/drivers/0x0a_ir_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} diff --git a/drivers/0x0a_ir_rust/Cargo.lock b/drivers/0x0a_ir_rust/Cargo.lock new file mode 100644 index 0000000..1db3b82 --- /dev/null +++ b/drivers/0x0a_ir_rust/Cargo.lock @@ -0,0 +1,749 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "crc-any" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "frunk" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28aef0f9aa070bce60767c12ba9cb41efeaf1a2bc6427f87b7d83f11239a16d7" +dependencies = [ + "frunk_core", + "frunk_derives", +] + +[[package]] +name = "frunk_core" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476eeaa382e3462b84da5d6ba3da97b5786823c2d0d3a0d04ef088d073da225c" + +[[package]] +name = "frunk_derives" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b4095fc99e1d858e5b8c7125d2638372ec85aa0fe6c807105cf10b0265ca6c" +dependencies = [ + "frunk_proc_macro_helpers", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "frunk_proc_macro_helpers" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1952b802269f2db12ab7c0bd328d0ae8feaabf19f352a7b0af7bb0c5693abfce" +dependencies = [ + "frunk_core", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fugit" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "ir" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "fugit", + "panic-halt", + "panic-probe", + "regex", + "rp2040-boot2", + "rp2040-hal", + "rp235x-hal", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "panic-halt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" + +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "riscv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", +] + +[[package]] +name = "riscv-rt" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d35e32cf1383183e8885d8a9aa4402a087fd094dc34c2cb6df6687d0229dfe" +dependencies = [ + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "riscv-rt-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp-binary-info" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f582261945fa215d40e2988b4df595d11c0908c0fff97a0fe23df766d117b790" + +[[package]] +name = "rp-hal-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288358786b1458fb2caac8c4b40fb529ef4200d6c46467e2695b7a8ba573ae8" +dependencies = [ + "fugit", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rp2040-hal" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb79a4590775204387f334672e6f79c0d734d0a159da23d60677b3c10fa1245" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "itertools 0.10.5", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "rp-binary-info", + "rp-hal-common", + "rp2040-hal-macros", + "rp2040-pac", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp2040-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e" +dependencies = [ + "cortex-m-rt", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rp2040-pac" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cbcd3f7a0ca7bbe61dc4eb7e202842bee4e27b769a7bf3a4a72fa399d6e404" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rp235x-hal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2939c82776b0b4ae110168b4298b5adf831e6cff249b057bf2a2187453b959c" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "cortex-m-rt", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "gcd", + "itertools 0.13.0", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "riscv", + "riscv-rt", + "rp-binary-info", + "rp-hal-common", + "rp235x-hal-macros", + "rp235x-pac", + "sha2-const-stable", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp235x-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74edd7a5979e9763bbb98e9746e711bac7464ee3397af7288e6c288ff0d3c764" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp235x-pac" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffcb6931deee4242886b5a1df62db5e2555b0eb6ae1e8be101f3ea3e58e65c6" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless", + "portable-atomic", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/drivers/0x0a_ir_rust/Cargo.toml b/drivers/0x0a_ir_rust/Cargo.toml new file mode 100644 index 0000000..9eb2dce --- /dev/null +++ b/drivers/0x0a_ir_rust/Cargo.toml @@ -0,0 +1,39 @@ +[package] +edition = "2024" +name = "ir" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "ir_lib" +path = "src/lib.rs" + +[[bin]] +name = "ir" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x0a_ir_rust/build.rs b/drivers/0x0a_ir_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x0a_ir_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x0a_ir_rust/rp2040.x b/drivers/0x0a_ir_rust/rp2040.x new file mode 100644 index 0000000..6e1a654 --- /dev/null +++ b/drivers/0x0a_ir_rust/rp2040.x @@ -0,0 +1,44 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; \ No newline at end of file diff --git a/drivers/0x0a_ir_rust/rp2350.x b/drivers/0x0a_ir_rust/rp2350.x new file mode 100644 index 0000000..c183dcd --- /dev/null +++ b/drivers/0x0a_ir_rust/rp2350.x @@ -0,0 +1,47 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); \ No newline at end of file diff --git a/drivers/0x0a_ir_rust/rp2350_riscv.x b/drivers/0x0a_ir_rust/rp2350_riscv.x new file mode 100644 index 0000000..194563d --- /dev/null +++ b/drivers/0x0a_ir_rust/rp2350_riscv.x @@ -0,0 +1,58 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +PROVIDE(__pre_init = default_pre_init); +PROVIDE(_setup_interrupts = default_setup_interrupts); +PROVIDE(_mp_hook = default_mp_hook); +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + . = ABSOLUTE(_stext); + } > FLASH +} \ No newline at end of file diff --git a/drivers/0x0a_ir_rust/src/board.rs b/drivers/0x0a_ir_rust/src/board.rs new file mode 100644 index 0000000..a877434 --- /dev/null +++ b/drivers/0x0a_ir_rust/src/board.rs @@ -0,0 +1,491 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// IR pure-logic functions and timing constants +use ir_lib::ir; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Timer device type for the HAL timer peripheral. +#[cfg(rp2350)] +pub(crate) type HalTimer = hal::Timer; +/// Timer type alias for RP2040 (non-generic). +#[cfg(rp2040)] +pub(crate) type HalTimer = hal::Timer; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// GPIO pin number connected to the IR receiver output. +pub(crate) const IR_GPIO: u8 = 5; + +/// Delay between decode attempts in milliseconds. +pub(crate) const POLL_MS: u32 = 10; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Read the free-running microsecond timer (lower 32 bits). +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +fn time_us_32(timer: &HalTimer) -> u32 { + timer.get_counter().ticks() as u32 +} + +/// Read the current logic level of the IR input pin through the SIO block. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn gpio_read() -> bool { + unsafe { (*hal::pac::SIO::PTR).gpio_in().read().bits() & (1u32 << IR_GPIO) != 0 } +} + +/// Wait for the IR pin to reach the requested level or time out. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// * `level` - The `level` parameter. +/// * `timeout_us` - The `timeout_us` parameter. +/// +/// # Returns +/// +/// A value of type `Option`. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// * `level` - The `level` parameter. +/// * `timeout_us` - The `timeout_us` parameter. +/// +/// # Returns +/// +/// An Optional value. +fn wait_for_level(timer: &HalTimer, level: bool, timeout_us: u32) -> Option { + let start = time_us_32(timer); + while gpio_read() != level { + let elapsed = time_us_32(timer).wrapping_sub(start) as i64; + if elapsed > timeout_us as i64 { + return None; + } + } + Some(time_us_32(timer).wrapping_sub(start) as i64) +} + +/// Wait for the IR receiver to go idle (LOW). +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn wait_for_idle(timer: &HalTimer) -> bool { + wait_for_level(timer, false, ir::LEADER_START_TIMEOUT_US).is_some() +} + +/// Validate the NEC leader mark pulse width. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn validate_leader_mark(timer: &HalTimer) -> bool { + let Some(w) = wait_for_level(timer, true, ir::LEADER_MARK_TIMEOUT_US) else { + return false; + }; + ir::is_valid_leader_mark(w) +} + +/// Validate the NEC leader space width. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn validate_leader_space(timer: &HalTimer) -> bool { + let Some(w) = wait_for_level(timer, false, ir::LEADER_SPACE_TIMEOUT_US) else { + return false; + }; + ir::is_valid_leader_space(w) +} + +/// Wait for the NEC leader burst and space. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn wait_leader(timer: &HalTimer) -> bool { + wait_for_idle(timer) && validate_leader_mark(timer) && validate_leader_space(timer) +} + +/// Wait for the bit mark and measure the bit space width. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// A value of type `Option`. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// An Optional value. +fn measure_bit_space(timer: &HalTimer) -> Option { + if wait_for_level(timer, true, ir::BIT_MARK_TIMEOUT_US).is_none() { + return None; + } + let w = wait_for_level(timer, false, ir::BIT_SPACE_TIMEOUT_US)?; + if !ir::is_valid_bit_space(w) { + return None; + } + Some(w) +} + +/// Read one NEC bit and store it in the frame buffer. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// * `data` - Data to send/write. +/// * `bit_index` - The `bit_index` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// * `data` - Data to send/write. +/// * `bit_index` - The `bit_index` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn read_nec_bit(timer: &HalTimer, data: &mut [u8; 4], bit_index: usize) -> bool { + let Some(space_width) = measure_bit_space(timer) else { + return false; + }; + ir::accumulate_nec_bit(data, bit_index, space_width); + true +} + +/// Read a full 32-bit NEC frame. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +fn read_32_bits(timer: &HalTimer, data: &mut [u8; 4]) -> bool { + let mut bit_index = 0usize; + while bit_index < ir::FRAME_BITS { + if !read_nec_bit(timer, data, bit_index) { + return false; + } + bit_index += 1; + } + true +} + +/// Block until an NEC key is decoded or return `None` on failure. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// A value of type `Option`. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// +/// # Returns +/// +/// An Optional value. +pub(crate) fn ir_getkey(timer: &HalTimer) -> Option { + if !wait_leader(timer) { + return None; + } + let mut data = [0u8; 4]; + if !read_32_bits(timer, &mut data) { + return None; + } + ir::validate_nec_frame(&data) +} + +/// Poll the decoder and print the key code when a valid frame is received. +pub(crate) fn poll_receiver( + uart: &EnabledUart, + timer: &HalTimer, + delay: &mut cortex_m::delay::Delay, +) { + let mut buf = [0u8; 26]; + if let Some(command) = ir_getkey(timer) { + let len = ir::format_command(&mut buf, command); + uart.write_full_blocking(&buf[..len]); + } + delay.delay_ms(POLL_MS); +} + +/// Initialise all peripherals and run the NEC IR receiver demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + #[cfg(rp2350)] + let timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks); + #[cfg(rp2040)] + let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS); + let _ = p.gpio5.into_pull_up_input(); + announce_ir(&uart); + loop { + poll_receiver(&uart, &timer, &mut delay); + } +} + +/// Print the IR driver initialisation banner over UART. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +fn announce_ir(uart: &EnabledUart) { + uart.write_full_blocking(b"NEC IR driver initialized on GPIO 5\r\n"); + uart.write_full_blocking(b"Press a button on your NEC remote...\r\n"); +} + diff --git a/drivers/0x0a_ir_rust/src/ir.rs b/drivers/0x0a_ir_rust/src/ir.rs new file mode 100644 index 0000000..9f4edf1 --- /dev/null +++ b/drivers/0x0a_ir_rust/src/ir.rs @@ -0,0 +1,463 @@ +//! Implementation module +//! +//! **File:** `ir.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// Leader wait timeout in microseconds. +pub const LEADER_START_TIMEOUT_US: u32 = 150_000; + +/// Maximum duration accepted for the NEC leader mark wait. +pub const LEADER_MARK_TIMEOUT_US: u32 = 12_000; + +/// Minimum valid NEC leader mark width in microseconds. +pub const LEADER_MARK_MIN_US: i64 = 8_000; + +/// Maximum valid NEC leader mark width in microseconds. +pub const LEADER_MARK_MAX_US: i64 = 10_000; + +/// Maximum duration accepted for the NEC leader space wait. +pub const LEADER_SPACE_TIMEOUT_US: u32 = 7_000; + +/// Minimum valid NEC leader space width in microseconds. +pub const LEADER_SPACE_MIN_US: i64 = 3_500; + +/// Maximum valid NEC leader space width in microseconds. +pub const LEADER_SPACE_MAX_US: i64 = 5_000; + +/// Maximum duration accepted while waiting for the bit mark to end. +pub const BIT_MARK_TIMEOUT_US: u32 = 1_000; + +/// Maximum duration accepted while measuring the data space. +pub const BIT_SPACE_TIMEOUT_US: u32 = 2_500; + +/// Minimum valid data space width in microseconds. +pub const BIT_SPACE_MIN_US: i64 = 200; + +/// Space width above which a NEC bit is interpreted as logical 1. +pub const BIT_ONE_THRESHOLD_US: i64 = 1_200; + +/// Total number of bits in an NEC frame. +pub const FRAME_BITS: usize = 32; + +/// Return true if the measured NEC leader mark width is valid. +/// +/// # Arguments +/// +/// * `duration_us` - The `duration_us` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `duration_us` - The `duration_us` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +pub fn is_valid_leader_mark(duration_us: i64) -> bool { + (LEADER_MARK_MIN_US..=LEADER_MARK_MAX_US).contains(&duration_us) +} + +/// Return true if the measured NEC leader space width is valid. +/// +/// # Arguments +/// +/// * `duration_us` - The `duration_us` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `duration_us` - The `duration_us` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +pub fn is_valid_leader_space(duration_us: i64) -> bool { + (LEADER_SPACE_MIN_US..=LEADER_SPACE_MAX_US).contains(&duration_us) +} + +/// Return true if the measured NEC bit space width is valid. +/// +/// # Arguments +/// +/// * `duration_us` - The `duration_us` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `duration_us` - The `duration_us` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +pub fn is_valid_bit_space(duration_us: i64) -> bool { + duration_us >= BIT_SPACE_MIN_US +} + +/// Accumulate a single NEC bit into the 4-byte frame buffer. +/// +/// Matches the C implementation exactly: bytes are filled LSB-first. +/// +/// # Arguments +/// +/// * `data` - Data to send/write. +/// * `bit_index` - The `bit_index` parameter. +/// * `duration_us` - The `duration_us` parameter. +/// +/// # Arguments +/// +/// * `data` - Data to send/write. +/// * `bit_index` - The `bit_index` parameter. +/// * `duration_us` - The `duration_us` parameter. +pub fn accumulate_nec_bit(data: &mut [u8; 4], bit_index: usize, duration_us: i64) { + let byte_idx = bit_index / 8; + let bit_idx = bit_index % 8; + if duration_us > BIT_ONE_THRESHOLD_US { + data[byte_idx] |= 1u8 << bit_idx; + } +} + +/// Validate an NEC frame and return the command byte on success. +/// +/// # Arguments +/// +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// A value of type `Option`. +/// +/// # Arguments +/// +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// An Optional value. +pub fn validate_nec_frame(data: &[u8; 4]) -> Option { + if data[0].wrapping_add(data[1]) == 0xFF && data[2].wrapping_add(data[3]) == 0xFF { + Some(data[2]) + } else { + None + } +} + +/// Format the decoded command as hexadecimal and decimal followed by CRLF. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `command` - The `command` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `command` - The `command` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_command(buf: &mut [u8], command: u8) -> usize { + let prefix = b"NEC command: 0x"; + let mut pos = copy_slice(buf, 0, prefix); + pos += format_hex_u8(buf, pos, command); + pos += copy_slice(buf, pos, b" ("); + pos += format_u8(buf, pos, command); + pos += copy_slice(buf, pos, b")\r\n"); + pos +} + +/// Copy a byte slice into `buf` at the given offset, returning bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `offset` - The `offset` parameter. +/// * `src` - The `src` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `offset` - The `offset` parameter. +/// * `src` - The `src` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn copy_slice(buf: &mut [u8], offset: usize, src: &[u8]) -> usize { + buf[offset..offset + src.len()].copy_from_slice(src); + src.len() +} + +/// Format an unsigned 8-bit integer at the given buffer offset. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_u8(buf: &mut [u8], pos: usize, value: u8) -> usize { + let n = u8_digit_count(value); + write_u8_digits(buf, pos, value, n); + n +} + +/// Return the number of decimal digits in a u8. +/// +/// # Arguments +/// +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn u8_digit_count(value: u8) -> usize { + if value >= 100 { + 3 + } else if value >= 10 { + 2 + } else { + 1 + } +} + +/// Write the decimal digits of a u8 into `buf` at `pos`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `value` - Value to use. +/// * `n` - Nibble or number. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `value` - Value to use. +/// * `n` - Nibble or number. +fn write_u8_digits(buf: &mut [u8], pos: usize, value: u8, n: usize) { + if n >= 3 { + buf[pos] = b'0' + value / 100; + } + if n >= 2 { + buf[pos + n - 2] = b'0' + (value / 10) % 10; + } + buf[pos + n - 1] = b'0' + value % 10; +} + +/// Format an unsigned 8-bit integer as two uppercase hexadecimal digits. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_hex_u8(buf: &mut [u8], pos: usize, value: u8) -> usize { + buf[pos] = hex_digit((value >> 4) & 0x0F); + buf[pos + 1] = hex_digit(value & 0x0F); + 2 +} + +/// Convert a 4-bit value to its uppercase ASCII hex digit. +/// +/// # Arguments +/// +/// * `value` - Value to use. +/// +/// # Returns +/// +/// An 8-bit unsigned integer value. +/// +/// # Arguments +/// +/// * `value` - Value to use. +/// +/// # Returns +/// +/// An 8-bit unsigned integer value. +fn hex_digit(value: u8) -> u8 { + if value < 10 { + b'0' + value + } else { + b'A' + (value - 10) + } +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the leader mark accepts lower bound operation. + #[test] + fn leader_mark_accepts_lower_bound() { + assert!(is_valid_leader_mark(8_000)); + } + + /// Executes the leader mark rejects below lower bound operation. + #[test] + fn leader_mark_rejects_below_lower_bound() { + assert!(!is_valid_leader_mark(7_999)); + } + + /// Executes the leader space accepts upper bound operation. + #[test] + fn leader_space_accepts_upper_bound() { + assert!(is_valid_leader_space(5_000)); + } + + /// Executes the leader space rejects above upper bound operation. + #[test] + fn leader_space_rejects_above_upper_bound() { + assert!(!is_valid_leader_space(5_001)); + } + + /// Executes the bit space rejects short pulse operation. + #[test] + fn bit_space_rejects_short_pulse() { + assert!(!is_valid_bit_space(199)); + } + + /// Executes the bit space accepts threshold operation. + #[test] + fn bit_space_accepts_threshold() { + assert!(is_valid_bit_space(200)); + } + + /// Executes the accumulate zero bit leaves byte clear operation. + #[test] + fn accumulate_zero_bit_leaves_byte_clear() { + let mut data = [0u8; 4]; + accumulate_nec_bit(&mut data, 0, 800); + assert_eq!(data[0], 0); + } + + /// Executes the accumulate one bit sets lsb operation. + #[test] + fn accumulate_one_bit_sets_lsb() { + let mut data = [0u8; 4]; + accumulate_nec_bit(&mut data, 0, 1_300); + assert_eq!(data[0], 1); + } + + /// Executes the accumulate crosses into next byte operation. + #[test] + fn accumulate_crosses_into_next_byte() { + let mut data = [0u8; 4]; + accumulate_nec_bit(&mut data, 8, 1_300); + assert_eq!(data[0], 0); + assert_eq!(data[1], 1); + } + + /// Executes the validate frame returns command operation. + #[test] + fn validate_frame_returns_command() { + let data = [0x00, 0xFF, 0x45, 0xBA]; + assert_eq!(validate_nec_frame(&data), Some(0x45)); + } + + /// Executes the validate frame rejects bad inverse operation. + #[test] + fn validate_frame_rejects_bad_inverse() { + let data = [0x00, 0xFE, 0x45, 0xBA]; + assert_eq!(validate_nec_frame(&data), None); + } + + /// Executes the format command single digit operation. + #[test] + fn format_command_single_digit() { + let mut buf = [0u8; 24]; + let n = format_command(&mut buf, 7); + assert_eq!(&buf[..n], b"NEC command: 0x07 (7)\r\n"); + } + + /// Executes the format command three digits operation. + #[test] + fn format_command_three_digits() { + let mut buf = [0u8; 26]; + let n = format_command(&mut buf, 255); + assert_eq!(&buf[..n], b"NEC command: 0xFF (255)\r\n"); + } + + /// Executes the format hex digit alpha operation. + #[test] + fn format_hex_digit_alpha() { + assert_eq!(hex_digit(0x0A), b'A'); + } +} diff --git a/drivers/0x0a_ir_rust/src/lib.rs b/drivers/0x0a_ir_rust/src/lib.rs new file mode 100644 index 0000000..1438f2e --- /dev/null +++ b/drivers/0x0a_ir_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod ir; diff --git a/drivers/0x0a_ir_rust/src/main.rs b/drivers/0x0a_ir_rust/src/main.rs new file mode 100644 index 0000000..98f51a3 --- /dev/null +++ b/drivers/0x0a_ir_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// IR driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the NEC IR receiver demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"NEC IR Receiver Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x0b_spi/.vscode/.vscode/c_cpp_properties.json b/drivers/0x0b_spi/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0b_spi/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0b_spi/.vscode/.vscode/cmake-kits.json b/drivers/0x0b_spi/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0b_spi/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0b_spi/.vscode/.vscode/extensions.json b/drivers/0x0b_spi/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0b_spi/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0b_spi/.vscode/.vscode/launch.json b/drivers/0x0b_spi/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0b_spi/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0b_spi/.vscode/.vscode/settings.json b/drivers/0x0b_spi/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0b_spi/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0b_spi/.vscode/.vscode/tasks.json b/drivers/0x0b_spi/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0b_spi/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0b_spi/.vscode/c_cpp_properties.json b/drivers/0x0b_spi/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0b_spi/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0b_spi/.vscode/cmake-kits.json b/drivers/0x0b_spi/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0b_spi/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0b_spi/.vscode/extensions.json b/drivers/0x0b_spi/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0b_spi/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0b_spi/.vscode/launch.json b/drivers/0x0b_spi/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0b_spi/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0b_spi/.vscode/settings.json b/drivers/0x0b_spi/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0b_spi/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0b_spi/.vscode/tasks.json b/drivers/0x0b_spi/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0b_spi/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0b_spi/0x0b_spi.c b/drivers/0x0b_spi/0x0b_spi.c new file mode 100644 index 0000000..eb80697 --- /dev/null +++ b/drivers/0x0b_spi/0x0b_spi.c @@ -0,0 +1,86 @@ +/** + * @file 0x0b_spi.c + * @brief SPI loopback demo using spi.c/spi.h driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates SPI in master mode using the spi driver (spi.h / spi.c). + * A loopback wiring (MOSI -> MISO) is used to verify full-duplex transfer. + * Transmitted and received data is printed over UART every second. + * + * Wiring (loopback test): + * GPIO19 (MOSI) -> GPIO16 (MISO) + * GPIO18 (SCK) -> logic analyzer or slave SCK + * GPIO17 (CS) -> slave CS (active-low) + */ + +#include +#include +#include "pico/stdlib.h" +#include "spi.h" + +/** @brief SPI port number (0 or 1) */ +#define SPI_PORT 0 +/** @brief SPI bus clock rate in Hz */ +#define SPI_BAUD_HZ (1000 * 1000) +/** @brief GPIO pin for SPI MISO */ +#define PIN_MISO 16 +/** @brief GPIO pin for SPI chip select */ +#define PIN_CS 17 +/** @brief GPIO pin for SPI clock */ +#define PIN_SCK 18 +/** @brief GPIO pin for SPI MOSI */ +#define PIN_MOSI 19 + +/** + * @brief Perform one SPI loopback transfer and print the result + * + * Asserts chip-select, transfers the buffer, deasserts chip-select, + * prints TX and RX data over UART, then clears the receive buffer. + * + * @param tx_buf Transmit buffer + * @param rx_buf Receive buffer (cleared after printing) + * @param len Number of bytes to transfer + */ +static void loopback_transfer(const uint8_t *tx_buf, uint8_t *rx_buf, size_t len) { + spi_driver_cs_select(PIN_CS); + spi_driver_transfer(SPI_PORT, tx_buf, rx_buf, len); + spi_driver_cs_deselect(PIN_CS); + printf("TX: %s\r\n", tx_buf); + printf("RX: %s\r\n\r\n", rx_buf); + memset(rx_buf, 0, len); + sleep_ms(1000); +} + +int main(void) { + stdio_init_all(); + spi_driver_init(SPI_PORT, PIN_MOSI, PIN_MISO, PIN_SCK, PIN_CS, SPI_BAUD_HZ); + const uint8_t tx_buf[] = "SPI loopback OK"; + uint8_t rx_buf[sizeof(tx_buf)] = {0}; + while (true) + loopback_transfer(tx_buf, rx_buf, sizeof(tx_buf)); +} diff --git a/drivers/0x0b_spi/CMakeLists.txt b/drivers/0x0b_spi/CMakeLists.txt new file mode 100644 index 0000000..8c19e5c --- /dev/null +++ b/drivers/0x0b_spi/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0b_spi C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0b_spi 0x0b_spi.c spi.c) + +pico_set_program_name(0x0b_spi "0x0b_spi") +pico_set_program_version(0x0b_spi "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0b_spi 1) +pico_enable_stdio_usb(0x0b_spi 0) + +# Add the standard library to the build +target_link_libraries(0x0b_spi + pico_stdlib + hardware_spi) + +# Add the standard include files to the build +target_include_directories(0x0b_spi PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0b_spi) diff --git a/drivers/0x0b_spi/pico_sdk_import.cmake b/drivers/0x0b_spi/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x0b_spi/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x0b_spi/spi.c b/drivers/0x0b_spi/spi.c new file mode 100644 index 0000000..967f01f --- /dev/null +++ b/drivers/0x0b_spi/spi.c @@ -0,0 +1,79 @@ +/** + * @file spi.c + * @brief Implementation of SPI bus driver (master mode) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "spi.h" +#include "pico/stdlib.h" +#include "hardware/spi.h" + +/** + * @brief Map an SPI port number to its hardware instance pointer + * + * @param port SPI port number (0 for spi0, 1 for spi1) + * @return spi_inst_t* Pointer to the corresponding SPI hardware instance + */ +static spi_inst_t *get_spi_inst(uint8_t port) { + return port == 0 ? spi0 : spi1; +} + +/** + * @brief Assign GPIO alternate functions for the SPI data and clock pins + * + * @param mosi GPIO pin for MOSI + * @param miso GPIO pin for MISO + * @param sck GPIO pin for SCK + */ +static void setup_spi_pins(uint32_t mosi, uint32_t miso, uint32_t sck) { + gpio_set_function(mosi, GPIO_FUNC_SPI); + gpio_set_function(miso, GPIO_FUNC_SPI); + gpio_set_function(sck, GPIO_FUNC_SPI); +} + +void spi_driver_init(uint8_t port, uint32_t mosi, uint32_t miso, + uint32_t sck, uint32_t cs, uint32_t baud_hz) { + spi_inst_t *spi = get_spi_inst(port); + spi_init(spi, baud_hz); + setup_spi_pins(mosi, miso, sck); + gpio_init(cs); + gpio_set_dir(cs, GPIO_OUT); + gpio_put(cs, 1); +} + +void spi_driver_cs_select(uint32_t cs) { + gpio_put(cs, 0); +} + +void spi_driver_cs_deselect(uint32_t cs) { + gpio_put(cs, 1); +} + +void spi_driver_transfer(uint8_t port, const uint8_t *tx, uint8_t *rx, + uint32_t len) { + spi_inst_t *spi = get_spi_inst(port); + spi_write_read_blocking(spi, tx, rx, (size_t)len); +} diff --git a/drivers/0x0b_spi/spi.h b/drivers/0x0b_spi/spi.h new file mode 100644 index 0000000..2ff3bba --- /dev/null +++ b/drivers/0x0b_spi/spi.h @@ -0,0 +1,82 @@ +/** + * @file spi.h + * @brief Header for SPI bus driver (full-duplex transfer with CS control) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SPI_H +#define SPI_H + +#include +#include + +/** + * @brief Initialize an SPI peripheral in master mode + * + * Configures the SPI instance at the requested baud rate, assigns GPIO + * alternate functions for MOSI, MISO, and SCK, and configures the chip + * select pin as a GPIO output deasserted (high) by default. + * + * @param port SPI port number (0 for spi0, 1 for spi1) + * @param mosi GPIO pin number for MOSI (controller TX) + * @param miso GPIO pin number for MISO (controller RX) + * @param sck GPIO pin number for SCK (clock) + * @param cs GPIO pin number for chip select (active-low) + * @param baud_hz Desired SPI clock frequency in Hz (e.g. 1000000 for 1 MHz) + */ +void spi_driver_init(uint8_t port, uint32_t mosi, uint32_t miso, + uint32_t sck, uint32_t cs, uint32_t baud_hz); + +/** + * @brief Assert the chip-select line (drive CS low) + * + * @param cs GPIO pin number of the chip-select output + */ +void spi_driver_cs_select(uint32_t cs); + +/** + * @brief Deassert the chip-select line (drive CS high) + * + * @param cs GPIO pin number of the chip-select output + */ +void spi_driver_cs_deselect(uint32_t cs); + +/** + * @brief Perform a full-duplex SPI transfer + * + * Sends @p len bytes from @p tx while simultaneously receiving @p len bytes + * into @p rx. Both buffers must hold at least @p len bytes. The caller is + * responsible for asserting and deasserting CS around this call. + * + * @param port SPI port number (0 for spi0, 1 for spi1) + * @param tx Pointer to the transmit buffer (must be @p len bytes) + * @param rx Pointer to the receive buffer (must be @p len bytes) + * @param len Number of bytes to transfer + */ +void spi_driver_transfer(uint8_t port, const uint8_t *tx, uint8_t *rx, + uint32_t len); + +#endif // SPI_H diff --git a/drivers/0x0b_spi_cbm/.vscode/c_cpp_properties.json b/drivers/0x0b_spi_cbm/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..60ea1fe --- /dev/null +++ b/drivers/0x0b_spi_cbm/.vscode/c_cpp_properties.json @@ -0,0 +1,45 @@ +{ + "configurations": [ + { + "name": "ARM GCC", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + }, + { + "name": "ARM GCC (Windows)", + "includePath": [ + "${workspaceFolder}/Inc/**" + ], + "defines": [ + "__GNUC__", + "__ARM_ARCH_8M_MAIN__", + "__ARMCC_VERSION" + ], + "compilerPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-arm", + "compilerArgs": [ + "-mcpu=cortex-m33", + "-mthumb" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/drivers/0x0b_spi_cbm/.vscode/extensions.json b/drivers/0x0b_spi_cbm/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0b_spi_cbm/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0b_spi_cbm/.vscode/launch.json b/drivers/0x0b_spi_cbm/.vscode/launch.json new file mode 100644 index 0000000..51ee8fd --- /dev/null +++ b/drivers/0x0b_spi_cbm/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RP2350 (OpenOCD)", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/spi.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "searchDspi": [ + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb", + "device": "RP2350", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/rp2350.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd", + "overrideLaunchCommands": [ + "set arch armv8-m.main", + "set output-radix 16", + "monitor reset init", + "load", + "monitor reset halt" + ], + "openOCDPreConfigLaunchCommands": [ + "set USE_CORE { cm0 cm1 }" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ], + "preLaunchTask": "Compile Project", + "showDevDebugOutput": "raw", + "windows": { + "serverpath": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "searchDspi": [ + "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/scripts" + ], + "gdbPath": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gdb.exe", + "svdFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0/src/rp2350/hardware_regs/RP2350.svd" + } + } + ] +} \ No newline at end of file diff --git a/drivers/0x0b_spi_cbm/.vscode/settings.json b/drivers/0x0b_spi_cbm/.vscode/settings.json new file mode 100644 index 0000000..05d7bce --- /dev/null +++ b/drivers/0x0b_spi_cbm/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} \ No newline at end of file diff --git a/drivers/0x0b_spi_cbm/.vscode/tasks.json b/drivers/0x0b_spi_cbm/.vscode/tasks.json new file mode 100644 index 0000000..cb1589b --- /dev/null +++ b/drivers/0x0b_spi_cbm/.vscode/tasks.json @@ -0,0 +1,128 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "shell", + "command": "make", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": ".\\build.bat" + } + }, + { + "label": "Clean Project", + "type": "shell", + "command": "make clean", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": ".\\clean.bat" + } + }, + { + "label": "Run Project", + "type": "shell", + "command": "${userHome}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "build/spi.uf2", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "shell", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; program build/spi.elf verify reset exit" + ], + "problemMatcher": [], + "dependsOn": [ + "Compile Project" + ], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe" + } + } + ] +} +], +"problemMatcher": [] +} +] +} \ No newline at end of file diff --git a/drivers/0x0b_spi_cbm/Inc/rp2350.h b/drivers/0x0b_spi_cbm/Inc/rp2350.h new file mode 100644 index 0000000..50f528b --- /dev/null +++ b/drivers/0x0b_spi_cbm/Inc/rp2350.h @@ -0,0 +1,263 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define SPI0_BASE 0x40080000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[12]; // GPOUT0..GPOUT3 registers Address offset: 0x00-0x2C + __IO uint32_t CLK_REF_CTRL; // Reference clock control Address offset: 0x30 + __IO uint32_t RESERVED1[5]; // CLK_REF_DIV..CLK_SYS_SELECTED Address offset: 0x34-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief SPI (PrimeCell SSP) + */ +typedef struct +{ + __IO uint32_t SSPCR0; // Control register 0 Address offset: 0x000 + __IO uint32_t SSPCR1; // Control register 1 Address offset: 0x004 + __IO uint32_t SSPDR; // Data register Address offset: 0x008 + __IO uint32_t SSPSR; // Status register Address offset: 0x00C + __IO uint32_t SSPCPSR; // Clock prescale register Address offset: 0x010 +} SPI_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SPI0 ((SPI_TypeDef *) SPI0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLK_REF bit definitions + */ +#define CLK_REF_CTRL_SRC_XOSC 2U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_MASK (0x07U << CLK_PERI_CTRL_AUXSRC_SHIFT) +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_PADS_BANK0_SHIFT 9U +#define RESETS_RESET_SPI0_SHIFT 18U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_SPI 0x01U +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U +#define IO_BANK0_CTRL_FUNCSEL_NULL 0x1FU + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U +#define PADS_BANK0_PUE_SHIFT 3U +#define PADS_BANK0_PDE_SHIFT 2U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief SPI0 SSPCR0 bit definitions + */ +#define SPI_SSPCR0_DSS_SHIFT 0U +#define SPI_SSPCR0_DSS_8BIT 0x07U +#define SPI_SSPCR0_FRF_SHIFT 4U +#define SPI_SSPCR0_SCR_SHIFT 8U + +/** + * @brief SPI0 SSPCR1 bit definitions + */ +#define SPI_SSPCR1_LBM_SHIFT 0U +#define SPI_SSPCR1_SSE_SHIFT 1U + +/** + * @brief SPI0 SSPSR bit definitions + */ +#define SPI_SSPSR_TFE_MASK (1U << 0) +#define SPI_SSPSR_TNF_MASK (1U << 1) +#define SPI_SSPSR_RNE_MASK (1U << 2) +#define SPI_SSPSR_BSY_MASK (1U << 4) + +/** + * @brief SPI0 pin definitions (SPI0 alternate function on GPIO16-19) + */ +#define SPI_MISO_PIN 16U +#define SPI_CS_PIN 17U +#define SPI_SCK_PIN 18U +#define SPI_MOSI_PIN 19U + +/** + * @brief SPI0 clock configuration for 1 MHz baud at 12 MHz clk_peri. + * + * Baud = clk_peri / (CPSDVSR * (1 + SCR)) + * = 12 MHz / (12 * (1 + 0)) = 1 MHz + */ +#define SPI_CPSDVSR_1MHZ 12U +#define SPI_SCR_1MHZ 0U + +#endif /* __RP2350_H */ diff --git a/drivers/0x0b_spi_cbm/Inc/rp2350_delay.h b/drivers/0x0b_spi_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..79db58f --- /dev/null +++ b/drivers/0x0b_spi_cbm/Inc/rp2350_delay.h @@ -0,0 +1,51 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond and microsecond busy-wait delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +/** + * @brief Delay for the specified number of microseconds. + * @param us number of microseconds to delay + * @retval None + */ +void delay_us(uint32_t us); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x0b_spi_cbm/Inc/rp2350_reset.h b/drivers/0x0b_spi_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x0b_spi_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x0b_spi_cbm/Inc/rp2350_reset_handler.h b/drivers/0x0b_spi_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x0b_spi_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x0b_spi_cbm/Inc/rp2350_spi.h b/drivers/0x0b_spi_cbm/Inc/rp2350_spi.h new file mode 100644 index 0000000..4d06215 --- /dev/null +++ b/drivers/0x0b_spi_cbm/Inc/rp2350_spi.h @@ -0,0 +1,80 @@ +/** + * @file rp2350_spi.h + * @brief SPI0 master driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides full-duplex SPI0 master mode on GPIO16-19 with + * software-controlled chip select. Configures Motorola SPI + * frame format, 8-bit data, CPOL=0, CPHA=0, 1 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_SPI_H +#define __RP2350_SPI_H + +#include "rp2350.h" + +/** + * @brief Release SPI0 from reset in the RESETS controller. + * @retval None + */ +void spi_release_reset(void); + +/** + * @brief Initialise SPI0 in master mode at 1 MHz, 8-bit, CPOL=0/CPHA=0. + * + * Configures SSPCR0, SSPCPSR, and SSPCR1 then enables the port. + * Also configures GPIO16-19 pads and IO funcsel for SPI. + * + * @retval None + */ +void spi_init(void); + +/** + * @brief Assert the chip-select line (drive CS low). + * @retval None + */ +void spi_cs_select(void); + +/** + * @brief Deassert the chip-select line (drive CS high). + * @retval None + */ +void spi_cs_deselect(void); + +/** + * @brief Perform a full-duplex SPI transfer. + * + * Sends @p len bytes from @p tx while simultaneously receiving + * @p len bytes into @p rx. The caller is responsible for + * asserting and deasserting CS around this call. + * + * @param tx pointer to transmit buffer (must be @p len bytes) + * @param rx pointer to receive buffer (must be @p len bytes) + * @param len number of bytes to transfer + * @retval None + */ +void spi_transfer(const uint8_t *tx, uint8_t *rx, uint32_t len); + +#endif /* __RP2350_SPI_H */ diff --git a/drivers/0x0b_spi_cbm/Inc/rp2350_stack.h b/drivers/0x0b_spi_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x0b_spi_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x0b_spi_cbm/Inc/rp2350_uart.h b/drivers/0x0b_spi_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x0b_spi_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x0b_spi_cbm/Inc/rp2350_xosc.h b/drivers/0x0b_spi_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..80fd75d --- /dev/null +++ b/drivers/0x0b_spi_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,55 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +/** + * @brief Switch CLK_REF source to XOSC for a stable 12 MHz clk_sys. + * @retval None + */ +void xosc_set_clk_ref(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x0b_spi_cbm/Makefile b/drivers/0x0b_spi_cbm/Makefile new file mode 100644 index 0000000..bd7673d --- /dev/null +++ b/drivers/0x0b_spi_cbm/Makefile @@ -0,0 +1,84 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C SPI loopback driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = spi + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_spi.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x0b_spi_cbm/Src/image_def.c b/drivers/0x0b_spi_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x0b_spi_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x0b_spi_cbm/Src/main.c b/drivers/0x0b_spi_cbm/Src/main.c new file mode 100644 index 0000000..db4f44a --- /dev/null +++ b/drivers/0x0b_spi_cbm/Src/main.c @@ -0,0 +1,138 @@ +/** + * @file main.c + * @brief SPI loopback demonstration. + * @author Kevin Thomas + * @date 2026 + * + * Performs a full-duplex SPI0 transfer in master mode with + * MOSI wired to MISO for loopback verification. Prints TX + * and RX data over UART every second. + * + * Wiring (loopback test): + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * GPIO19 (MOSI) -> GPIO16 (MISO) + * GPIO18 (SCK) -> logic analyzer (optional) + * GPIO17 (CS) -> active-low slave (optional) + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_spi.h" +#include "rp2350_uart.h" +#include "rp2350_delay.h" +#include "rp2350_xosc.h" + +/** + * @brief Hex digit lookup table for byte-to-hex conversion. + */ +static const char hex_lut[16] = "0123456789ABCDEF"; + +/** + * @brief Print a byte as a two-digit hex string over UART. + * @param value byte to print + * @retval None + */ +static void print_hex(uint8_t value) +{ + char buf[3]; + buf[0] = hex_lut[value >> 4]; + buf[1] = hex_lut[value & 0x0FU]; + buf[2] = '\0'; + uart_puts(buf); +} + +/** + * @brief Print a buffer as hex bytes separated by spaces over UART. + * @param label text label to print before the data + * @param buf byte buffer to print + * @param len number of bytes in buffer + * @retval None + */ +static void print_buffer(const char *label, const uint8_t *buf, uint32_t len) +{ + uart_puts(label); + for (uint32_t i = 0; i < len; i++) + { + print_hex(buf[i]); + if (i + 1 < len) + uart_putchar(' '); + } + uart_puts("\r\n"); +} + +/** + * @brief Perform one SPI loopback transfer and print TX/RX over UART. + * @param tx_buf transmit buffer + * @param rx_buf receive buffer (cleared after printing) + * @param len number of bytes to transfer + * @retval None + */ +static void loopback_transfer(const uint8_t *tx_buf, uint8_t *rx_buf, uint32_t len) +{ + spi_cs_select(); + spi_transfer(tx_buf, rx_buf, len); + spi_cs_deselect(); + print_buffer("TX: ", tx_buf, len); + print_buffer("RX: ", rx_buf, len); + uart_puts("\r\n"); +} + +/** + * @brief Clear a byte buffer to zero. + * @param buf pointer to buffer + * @param len number of bytes to clear + * @retval None + */ +static void clear_buffer(uint8_t *buf, uint32_t len) +{ + for (uint32_t i = 0; i < len; i++) + buf[i] = 0; +} + +/** + * @brief Initialize clocks, SPI peripheral, and announce over UART. + * @retval None + */ +static void spi_setup(void) +{ + xosc_set_clk_ref(); + spi_release_reset(); + spi_init(); + uart_puts("SPI loopback initialized (MOSI->MISO on GPIO19->GPIO16)\r\n"); +} + +int main(void) +{ + /** @brief Transmit test pattern for SPI loopback. */ + static const uint8_t tx[] = {0xDE, 0xAD, 0xBE, 0xEF}; + /** @brief Buffer length for SPI loopback transfer. */ + static const uint32_t len = sizeof(tx); + uint8_t rx[sizeof(tx)] = {0}; + spi_setup(); + while (1) + { + loopback_transfer(tx, rx, len); + clear_buffer(rx, len); + delay_ms(1000); + } +} diff --git a/drivers/0x0b_spi_cbm/Src/rp2350_delay.c b/drivers/0x0b_spi_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..65b0821 --- /dev/null +++ b/drivers/0x0b_spi_cbm/Src/rp2350_delay.c @@ -0,0 +1,64 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond and microsecond delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} + +void delay_us(uint32_t us) +{ + if (us == 0) + return; + __asm__ volatile ( + "mov r4, #4\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (us) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x0b_spi_cbm/Src/rp2350_reset.c b/drivers/0x0b_spi_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x0b_spi_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x0b_spi_cbm/Src/rp2350_reset_handler.c b/drivers/0x0b_spi_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..a736fba --- /dev/null +++ b/drivers/0x0b_spi_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,82 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, SPI, UART, then + * branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_spi.h" +#include "rp2350_uart.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl spi_release_reset\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x0b_spi_cbm/Src/rp2350_spi.c b/drivers/0x0b_spi_cbm/Src/rp2350_spi.c new file mode 100644 index 0000000..2c9f18d --- /dev/null +++ b/drivers/0x0b_spi_cbm/Src/rp2350_spi.c @@ -0,0 +1,230 @@ +/** + * @file rp2350_spi.c + * @brief SPI0 master driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Full-duplex SPI0 master on GPIO16 (MISO), GPIO17 (CS), + * GPIO18 (SCK), GPIO19 (MOSI). Motorola SPI frame format, + * 8-bit data, CPOL=0, CPHA=0, 1 MHz clock from 12 MHz + * clk_peri. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_spi.h" + +/** + * @brief Bit mask for the chip-select pin. + */ +#define CS_PIN_MASK (1U << SPI_CS_PIN) + +/** + * @brief Clear the SPI0 reset bit in the reset controller. + * @retval None + */ +static void spi_clear_reset(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_SPI0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until SPI0 is out of reset. + * @retval None + */ +static void spi_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_SPI0_SHIFT)) == 0) {} +} + +/** + * @brief Configure a GPIO pad for SPI: input enabled, no pull, no isolation. + * @param pin GPIO pin number to configure + * @retval None + */ +static void configure_spi_pad(uint8_t pin) +{ + uint32_t value; + value = PADS_BANK0->GPIO[pin]; + value &= ~(1U << PADS_BANK0_OD_SHIFT); + value |= (1U << PADS_BANK0_IE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + PADS_BANK0->GPIO[pin] = value; +} + +/** + * @brief Configure a GPIO pad for CS: output enabled, pull-up, no isolation. + * @param pin GPIO pin number to configure + * @retval None + */ +static void configure_cs_pad(uint8_t pin) +{ + uint32_t value; + value = PADS_BANK0->GPIO[pin]; + value &= ~(1U << PADS_BANK0_OD_SHIFT); + value |= (1U << PADS_BANK0_IE_SHIFT); + value |= (1U << PADS_BANK0_PUE_SHIFT); + value &= ~(1U << PADS_BANK0_ISO_SHIFT); + PADS_BANK0->GPIO[pin] = value; +} + +/** + * @brief Set a GPIO funcsel to SPI alternate function. + * @param pin GPIO pin number + * @retval None + */ +static void set_funcsel_spi(uint8_t pin) +{ + IO_BANK0->GPIO[pin].CTRL = IO_BANK0_CTRL_FUNCSEL_SPI; +} + +/** + * @brief Set a GPIO funcsel to SIO for software control (CS pin). + * @param pin GPIO pin number + * @retval None + */ +static void set_funcsel_sio(uint8_t pin) +{ + IO_BANK0->GPIO[pin].CTRL = IO_BANK0_CTRL_FUNCSEL_SIO; +} + +/** + * @brief Configure CS pin as output, initially deasserted (high). + * @retval None + */ +static void cs_init(void) +{ + SIO[SIO_GPIO_OUT_SET_OFFSET] = CS_PIN_MASK; + SIO[SIO_GPIO_OE_SET_OFFSET] = CS_PIN_MASK; +} + +/** + * @brief Configure SSPCR0 for 8-bit Motorola SPI, SCR=0. + * @retval None + */ +static void configure_cr0(void) +{ + uint32_t value = 0; + value |= (SPI_SSPCR0_DSS_8BIT << SPI_SSPCR0_DSS_SHIFT); + value |= (SPI_SCR_1MHZ << SPI_SSPCR0_SCR_SHIFT); + SPI0->SSPCR0 = value; +} + +/** + * @brief Configure SSPCPSR clock prescaler for 1 MHz. + * @retval None + */ +static void configure_prescaler(void) +{ + SPI0->SSPCPSR = SPI_CPSDVSR_1MHZ; +} + +/** + * @brief Enable the SPI0 peripheral (SSE=1 in SSPCR1). + * @retval None + */ +static void enable_spi(void) +{ + SPI0->SSPCR1 = (1U << SPI_SSPCR1_SSE_SHIFT); +} + +/** + * @brief Wait until the SPI transmit FIFO has space. + * @retval None + */ +static void wait_tx_not_full(void) +{ + while ((SPI0->SSPSR & SPI_SSPSR_TNF_MASK) == 0) {} +} + +/** + * @brief Wait until the SPI receive FIFO has data. + * @retval None + */ +static void wait_rx_not_empty(void) +{ + while ((SPI0->SSPSR & SPI_SSPSR_RNE_MASK) == 0) {} +} + +void spi_release_reset(void) +{ + spi_clear_reset(); + spi_wait_reset_done(); +} + +/** + * @brief Configure all SPI and CS GPIO pads. + * @retval None + */ +static void configure_all_pads(void) +{ + configure_spi_pad(SPI_MOSI_PIN); + configure_spi_pad(SPI_MISO_PIN); + configure_spi_pad(SPI_SCK_PIN); + configure_cs_pad(SPI_CS_PIN); +} + +/** + * @brief Assign SPI and SIO alternate functions to all GPIO pins. + * @retval None + */ +static void configure_all_funcsel(void) +{ + set_funcsel_spi(SPI_MOSI_PIN); + set_funcsel_spi(SPI_MISO_PIN); + set_funcsel_spi(SPI_SCK_PIN); + set_funcsel_sio(SPI_CS_PIN); +} + +void spi_init(void) +{ + configure_all_pads(); + configure_all_funcsel(); + cs_init(); + configure_cr0(); + configure_prescaler(); + enable_spi(); +} + +void spi_cs_select(void) +{ + SIO[SIO_GPIO_OUT_CLR_OFFSET] = CS_PIN_MASK; +} + +void spi_cs_deselect(void) +{ + SIO[SIO_GPIO_OUT_SET_OFFSET] = CS_PIN_MASK; +} + +void spi_transfer(const uint8_t *tx, uint8_t *rx, uint32_t len) +{ + for (uint32_t i = 0; i < len; i++) + { + wait_tx_not_full(); + SPI0->SSPDR = tx[i]; + wait_rx_not_empty(); + rx[i] = (uint8_t)SPI0->SSPDR; + } +} diff --git a/drivers/0x0b_spi_cbm/Src/rp2350_stack.c b/drivers/0x0b_spi_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x0b_spi_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x0b_spi_cbm/Src/rp2350_uart.c b/drivers/0x0b_spi_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x0b_spi_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x0b_spi_cbm/Src/rp2350_xosc.c b/drivers/0x0b_spi_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..b33a71d --- /dev/null +++ b/drivers/0x0b_spi_cbm/Src/rp2350_xosc.c @@ -0,0 +1,54 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value &= ~CLK_PERI_CTRL_AUXSRC_MASK; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} + +void xosc_set_clk_ref(void) +{ + CLOCKS->CLK_REF_CTRL = CLK_REF_CTRL_SRC_XOSC; +} diff --git a/drivers/0x0b_spi_cbm/Src/vector_table.c b/drivers/0x0b_spi_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x0b_spi_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x0b_spi_cbm/build.log b/drivers/0x0b_spi_cbm/build.log new file mode 100644 index 0000000..271ea0d Binary files /dev/null and b/drivers/0x0b_spi_cbm/build.log differ diff --git a/drivers/0x0b_spi_cbm/linker.ld b/drivers/0x0b_spi_cbm/linker.ld new file mode 100644 index 0000000..0ad5c05 --- /dev/null +++ b/drivers/0x0b_spi_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x0b_spi_rust/.cargo/config.toml b/drivers/0x0b_spi_rust/.cargo/config.toml new file mode 100644 index 0000000..63db92a --- /dev/null +++ b/drivers/0x0b_spi_rust/.cargo/config.toml @@ -0,0 +1,32 @@ +[build] +target = "thumbv8m.main-none-eabihf" + +[target.thumbv6m-none-eabi] +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.thumbv8m.main-none-eabihf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.riscv32imac-unknown-none-elf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x0b_spi_rust/.pico-rs b/drivers/0x0b_spi_rust/.pico-rs new file mode 100644 index 0000000..be06904 --- /dev/null +++ b/drivers/0x0b_spi_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 diff --git a/drivers/0x0b_spi_rust/.vscode/extensions.json b/drivers/0x0b_spi_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x0b_spi_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0b_spi_rust/.vscode/launch.json b/drivers/0x0b_spi_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x0b_spi_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x0b_spi_rust/.vscode/settings.json b/drivers/0x0b_spi_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x0b_spi_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x0b_spi_rust/.vscode/tasks.json b/drivers/0x0b_spi_rust/.vscode/tasks.json new file mode 100644 index 0000000..d288f27 --- /dev/null +++ b/drivers/0x0b_spi_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} diff --git a/drivers/0x0b_spi_rust/Cargo.lock b/drivers/0x0b_spi_rust/Cargo.lock new file mode 100644 index 0000000..a414cf4 --- /dev/null +++ b/drivers/0x0b_spi_rust/Cargo.lock @@ -0,0 +1,750 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "crc-any" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "frunk" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28aef0f9aa070bce60767c12ba9cb41efeaf1a2bc6427f87b7d83f11239a16d7" +dependencies = [ + "frunk_core", + "frunk_derives", +] + +[[package]] +name = "frunk_core" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476eeaa382e3462b84da5d6ba3da97b5786823c2d0d3a0d04ef088d073da225c" + +[[package]] +name = "frunk_derives" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b4095fc99e1d858e5b8c7125d2638372ec85aa0fe6c807105cf10b0265ca6c" +dependencies = [ + "frunk_proc_macro_helpers", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "frunk_proc_macro_helpers" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1952b802269f2db12ab7c0bd328d0ae8feaabf19f352a7b0af7bb0c5693abfce" +dependencies = [ + "frunk_core", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fugit" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "panic-halt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" + +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "riscv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", +] + +[[package]] +name = "riscv-rt" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d35e32cf1383183e8885d8a9aa4402a087fd094dc34c2cb6df6687d0229dfe" +dependencies = [ + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "riscv-rt-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp-binary-info" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f582261945fa215d40e2988b4df595d11c0908c0fff97a0fe23df766d117b790" + +[[package]] +name = "rp-hal-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288358786b1458fb2caac8c4b40fb529ef4200d6c46467e2695b7a8ba573ae8" +dependencies = [ + "fugit", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rp2040-hal" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb79a4590775204387f334672e6f79c0d734d0a159da23d60677b3c10fa1245" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "itertools 0.10.5", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "rp-binary-info", + "rp-hal-common", + "rp2040-hal-macros", + "rp2040-pac", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp2040-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e" +dependencies = [ + "cortex-m-rt", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rp2040-pac" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cbcd3f7a0ca7bbe61dc4eb7e202842bee4e27b769a7bf3a4a72fa399d6e404" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rp235x-hal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2939c82776b0b4ae110168b4298b5adf831e6cff249b057bf2a2187453b959c" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "cortex-m-rt", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "gcd", + "itertools 0.13.0", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "riscv", + "riscv-rt", + "rp-binary-info", + "rp-hal-common", + "rp235x-hal-macros", + "rp235x-pac", + "sha2-const-stable", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp235x-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74edd7a5979e9763bbb98e9746e711bac7464ee3397af7288e6c288ff0d3c764" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp235x-pac" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffcb6931deee4242886b5a1df62db5e2555b0eb6ae1e8be101f3ea3e58e65c6" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "spi" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "embedded-hal 1.0.0", + "fugit", + "panic-halt", + "panic-probe", + "regex", + "rp2040-boot2", + "rp2040-hal", + "rp235x-hal", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless", + "portable-atomic", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/drivers/0x0b_spi_rust/Cargo.toml b/drivers/0x0b_spi_rust/Cargo.toml new file mode 100644 index 0000000..f2bce21 --- /dev/null +++ b/drivers/0x0b_spi_rust/Cargo.toml @@ -0,0 +1,40 @@ +[package] +edition = "2024" +name = "spi" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "spi_lib" +path = "src/lib.rs" + +[[bin]] +name = "spi" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" +embedded-hal = "1.0" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x0b_spi_rust/build.rs b/drivers/0x0b_spi_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x0b_spi_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x0b_spi_rust/rp2040.x b/drivers/0x0b_spi_rust/rp2040.x new file mode 100644 index 0000000..e66f718 --- /dev/null +++ b/drivers/0x0b_spi_rust/rp2040.x @@ -0,0 +1,44 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x0b_spi_rust/rp2350.x b/drivers/0x0b_spi_rust/rp2350.x new file mode 100644 index 0000000..570f72c --- /dev/null +++ b/drivers/0x0b_spi_rust/rp2350.x @@ -0,0 +1,47 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); diff --git a/drivers/0x0b_spi_rust/rp2350_riscv.x b/drivers/0x0b_spi_rust/rp2350_riscv.x new file mode 100644 index 0000000..2c9b445 --- /dev/null +++ b/drivers/0x0b_spi_rust/rp2350_riscv.x @@ -0,0 +1,58 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +PROVIDE(__pre_init = default_pre_init); +PROVIDE(_setup_interrupts = default_setup_interrupts); +PROVIDE(_mp_hook = default_mp_hook); +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + . = ABSOLUTE(_stext); + } > FLASH +} diff --git a/drivers/0x0b_spi_rust/src/board.rs b/drivers/0x0b_spi_rust/src/board.rs new file mode 100644 index 0000000..6f32a47 --- /dev/null +++ b/drivers/0x0b_spi_rust/src/board.rs @@ -0,0 +1,346 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// SPI pure-logic helpers and constants +use spi_lib::spi; +// Digital output trait for software-controlled chip select +use embedded_hal::digital::OutputPin; +// SPI bus trait for full-duplex transfer +use embedded_hal::spi::SpiBus; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionSio, FunctionSpi, FunctionUart, Pin, PullDown, PullNone}; +// SPI peripheral type +use hal::spi::Spi; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Delay between loopback transfers in milliseconds. +pub(crate) const POLL_MS: u32 = 1_000; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the configured SPI RX pin (GPIO16). +pub(crate) type MisoPin = Pin; + +/// Type alias for the configured SPI chip-select pin (GPIO17). +pub(crate) type CsPin = Pin, PullNone>; + +/// Type alias for the configured SPI clock pin (GPIO18). +pub(crate) type SckPin = Pin; + +/// Type alias for the configured SPI TX pin (GPIO19). +pub(crate) type MosiPin = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Type alias for the SPI0 peripheral configured in 8-bit master mode. +pub(crate) type EnabledSpi = Spi; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Executes the reconfigure miso, sck, and mosi pins from default to spi operation. +fn reconfigure_data_pins( + miso: hal::gpio::Pin, + sck: hal::gpio::Pin, + mosi: hal::gpio::Pin, +) -> (MosiPin, MisoPin, SckPin) { + ( + mosi.reconfigure::(), + miso.reconfigure::(), + sck.reconfigure::(), + ) +} + +/// Reconfigure a pin as digital output and de-assert chip select (drive high). +/// +/// # Arguments +/// +/// * `cs` - The `cs` parameter. +/// * `FunctionNull` - The `FunctionNull` parameter. +/// * `PullDown>` - The `PullDown>` parameter. +/// +/// # Returns +/// +/// A value of type `CsPin`. +/// +/// # Arguments +/// +/// * `cs` - The `cs` parameter. +/// * `FunctionNull` - The `FunctionNull` parameter. +/// * `PullDown>` - The `PullDown>` parameter. +/// +/// # Returns +/// +/// A value of type `CsPin`. +fn init_cs_pin(cs: hal::gpio::Pin) -> CsPin { + let mut cs = cs.reconfigure::, PullNone>(); + let _ = cs.set_high(); + cs +} + +/// Configure SPI0 and the software-controlled chip-select pin. +pub(crate) fn init_spi( + spi0: hal::pac::SPI0, + miso: hal::gpio::Pin, + cs: hal::gpio::Pin, + sck: hal::gpio::Pin, + mosi: hal::gpio::Pin, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> (EnabledSpi, CsPin) { + let (mosi, miso, sck) = reconfigure_data_pins(miso, sck, mosi); + let cs = init_cs_pin(cs); + let spi = Spi::<_, _, _, 8>::new(spi0, (mosi, miso, sck)).init( + resets, + clocks.peripheral_clock.freq(), + spi::SPI_BAUD_HZ.Hz(), + embedded_hal::spi::MODE_0, + ); + (spi, cs) +} + +/// Drive chip select active (low). +/// +/// # Arguments +/// +/// * `cs` - The `cs` parameter. +/// +/// # Arguments +/// +/// * `cs` - The `cs` parameter. +fn cs_select(cs: &mut CsPin) { + let _ = cs.set_low(); +} + +/// Drive chip select inactive (high). +/// +/// # Arguments +/// +/// * `cs` - The `cs` parameter. +/// +/// # Arguments +/// +/// * `cs` - The `cs` parameter. +fn cs_deselect(cs: &mut CsPin) { + let _ = cs.set_high(); +} + +/// Execute a full-duplex SPI transfer with chip-select framing. +/// +/// # Arguments +/// +/// * `spi_dev` - The `spi_dev` parameter. +/// * `cs` - The `cs` parameter. +/// * `rx` - The `rx` parameter. +/// +/// # Arguments +/// +/// * `spi_dev` - The `spi_dev` parameter. +/// * `cs` - The `cs` parameter. +/// * `rx` - The `rx` parameter. +fn execute_transfer(spi_dev: &mut EnabledSpi, cs: &mut CsPin, rx: &mut [u8]) { + cs_select(cs); + let _ = spi_dev.transfer(rx, spi::TX_MESSAGE); + cs_deselect(cs); +} + +/// Format and print the TX and RX lines over UART. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `rx` - The `rx` parameter. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `rx` - The `rx` parameter. +fn print_transfer_result(uart: &EnabledUart, rx: &[u8]) { + let mut line_buf = [0u8; 24]; + let tx_len = spi::format_tx_line(&mut line_buf, spi::TX_MESSAGE); + uart.write_full_blocking(&line_buf[..tx_len]); + let rx_len = spi::format_rx_line(&mut line_buf, rx); + uart.write_full_blocking(&line_buf[..rx_len]); +} + +/// Perform one SPI loopback transfer and print TX/RX text over UART. +pub(crate) fn loopback_transfer( + spi_dev: &mut EnabledSpi, + cs: &mut CsPin, + uart: &EnabledUart, + delay: &mut cortex_m::delay::Delay, +) { + let mut rx = [0u8; spi::TX_MESSAGE.len()]; + execute_transfer(spi_dev, cs, &mut rx); + print_transfer_result(uart, &rx); + spi::clear_rx_buffer(&mut rx); + delay.delay_ms(POLL_MS); +} + +/// Initialise all peripherals and run the SPI loopback demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + let (mut spi, mut cs) = init_spi( + pac.SPI0, + p.gpio16, + p.gpio17, + p.gpio18, + p.gpio19, + &mut pac.RESETS, + &clocks, + ); + uart.write_full_blocking(b"SPI driver initialized on SPI0 at 1000000 Hz\r\n"); + loop { + loopback_transfer(&mut spi, &mut cs, &uart, &mut delay); + } +} + diff --git a/drivers/0x0b_spi_rust/src/lib.rs b/drivers/0x0b_spi_rust/src/lib.rs new file mode 100644 index 0000000..40befff --- /dev/null +++ b/drivers/0x0b_spi_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod spi; diff --git a/drivers/0x0b_spi_rust/src/main.rs b/drivers/0x0b_spi_rust/src/main.rs new file mode 100644 index 0000000..a29e875 --- /dev/null +++ b/drivers/0x0b_spi_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// SPI driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the SPI loopback demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"SPI Loopback Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x0b_spi_rust/src/spi.rs b/drivers/0x0b_spi_rust/src/spi.rs new file mode 100644 index 0000000..10b6e0d --- /dev/null +++ b/drivers/0x0b_spi_rust/src/spi.rs @@ -0,0 +1,310 @@ +//! Implementation module +//! +//! **File:** `spi.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// SPI port number used by the loopback demo. +pub const SPI_PORT: u8 = 0; + +/// SPI clock frequency used by the loopback demo. +pub const SPI_BAUD_HZ: u32 = 1_000_000; + +/// GPIO pin number for MISO. +pub const PIN_MISO: u8 = 16; + +/// GPIO pin number for chip select. +pub const PIN_CS: u8 = 17; + +/// GPIO pin number for SCK. +pub const PIN_SCK: u8 = 18; + +/// GPIO pin number for MOSI. +pub const PIN_MOSI: u8 = 19; + +/// Fixed transmit message used by the SPI loopback demo. +pub const TX_MESSAGE: &[u8; 16] = b"SPI loopback OK\0"; + +/// Return true when the selected SPI port is supported by the driver. +/// +/// # Arguments +/// +/// * `port` - The `port` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +/// +/// # Arguments +/// +/// * `port` - The `port` parameter. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +pub fn is_valid_port(port: u8) -> bool { + port <= 1 +} + +/// Clear a receive buffer to all zeros. +/// +/// # Arguments +/// +/// * `rx` - The `rx` parameter. +/// +/// # Arguments +/// +/// * `rx` - The `rx` parameter. +pub fn clear_rx_buffer(rx: &mut [u8]) { + rx.fill(0); +} + +/// Copy `src` into `dst` up to the minimum length of both slices. +/// +/// # Arguments +/// +/// * `src` - The `src` parameter. +/// * `dst` - The `dst` parameter. +/// +/// # Arguments +/// +/// * `src` - The `src` parameter. +/// * `dst` - The `dst` parameter. +pub fn copy_transfer_result(src: &[u8], dst: &mut [u8]) { + let len = core::cmp::min(src.len(), dst.len()); + dst[..len].copy_from_slice(&src[..len]); +} + +/// Format the `TX:` line with trailing CRLF. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `tx` - The `tx` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `tx` - The `tx` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_tx_line(buf: &mut [u8], tx: &[u8]) -> usize { + format_line(buf, b"TX: ", tx, false) +} + +/// Format the `RX:` line with an extra blank line after it. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `rx` - The `rx` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `rx` - The `rx` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_rx_line(buf: &mut [u8], rx: &[u8]) -> usize { + format_line(buf, b"RX: ", rx, true) +} + +/// Format a single ASCII line for UART output. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `prefix` - The `prefix` parameter. +/// * `text` - The `text` parameter. +/// * `extra_blank_line` - The `extra_blank_line` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `prefix` - The `prefix` parameter. +/// * `text` - The `text` parameter. +/// * `extra_blank_line` - The `extra_blank_line` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_line(buf: &mut [u8], prefix: &[u8], text: &[u8], extra_blank_line: bool) -> usize { + let mut pos = 0; + buf[pos..pos + prefix.len()].copy_from_slice(prefix); + pos += prefix.len(); + pos += copy_c_string(&mut buf[pos..], text); + pos += append_crlf(&mut buf[pos..], extra_blank_line); + pos +} + +/// Copy bytes from `text` up to the first NUL into `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `text` - The `text` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `text` - The `text` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn copy_c_string(buf: &mut [u8], text: &[u8]) -> usize { + let len = c_string_len(text); + buf[..len].copy_from_slice(&text[..len]); + len +} + +/// Append CRLF (and optionally a second blank CRLF) to `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `extra` - The `extra` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `extra` - The `extra` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn append_crlf(buf: &mut [u8], extra: bool) -> usize { + buf[0] = b'\r'; + buf[1] = b'\n'; + if extra { + buf[2] = b'\r'; + buf[3] = b'\n'; + 4 + } else { + 2 + } +} + +/// Return the length up to the first NUL byte or full slice length. +/// +/// # Arguments +/// +/// * `text` - The `text` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `text` - The `text` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn c_string_len(text: &[u8]) -> usize { + let mut len = 0usize; + while len < text.len() && text[len] != 0 { + len += 1; + } + len +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the valid ports are zero and one operation. + #[test] + fn valid_ports_are_zero_and_one() { + assert!(is_valid_port(0)); + assert!(is_valid_port(1)); + assert!(!is_valid_port(2)); + } + + /// Executes the clear rx buffer zeroes all bytes operation. + #[test] + fn clear_rx_buffer_zeroes_all_bytes() { + let mut rx = [1u8, 2, 3, 4]; + clear_rx_buffer(&mut rx); + assert_eq!(rx, [0u8; 4]); + } + + /// Executes the copy transfer result copies common prefix operation. + #[test] + fn copy_transfer_result_copies_common_prefix() { + let src = b"abcd"; + let mut dst = [0u8; 3]; + copy_transfer_result(src, &mut dst); + assert_eq!(&dst, b"abc"); + } + + /// Executes the format tx line omits trailing nul operation. + #[test] + fn format_tx_line_omits_trailing_nul() { + let mut buf = [0u8; 32]; + let n = format_tx_line(&mut buf, TX_MESSAGE); + assert_eq!(&buf[..n], b"TX: SPI loopback OK\r\n"); + } + + /// Executes the format rx line adds blank line operation. + #[test] + fn format_rx_line_adds_blank_line() { + let mut buf = [0u8; 32]; + let n = format_rx_line(&mut buf, TX_MESSAGE); + assert_eq!(&buf[..n], b"RX: SPI loopback OK\r\n\r\n"); + } + + /// Executes the c string len stops at nul operation. + #[test] + fn c_string_len_stops_at_nul() { + assert_eq!(c_string_len(b"abc\0xyz"), 3); + } +} diff --git a/drivers/0x0c_multicore/.vscode/.vscode/c_cpp_properties.json b/drivers/0x0c_multicore/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0c_multicore/.vscode/.vscode/cmake-kits.json b/drivers/0x0c_multicore/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0c_multicore/.vscode/.vscode/extensions.json b/drivers/0x0c_multicore/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0c_multicore/.vscode/.vscode/launch.json b/drivers/0x0c_multicore/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0c_multicore/.vscode/.vscode/settings.json b/drivers/0x0c_multicore/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0c_multicore/.vscode/.vscode/tasks.json b/drivers/0x0c_multicore/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0c_multicore/.vscode/c_cpp_properties.json b/drivers/0x0c_multicore/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0c_multicore/.vscode/cmake-kits.json b/drivers/0x0c_multicore/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0c_multicore/.vscode/extensions.json b/drivers/0x0c_multicore/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0c_multicore/.vscode/launch.json b/drivers/0x0c_multicore/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0c_multicore/.vscode/settings.json b/drivers/0x0c_multicore/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0c_multicore/.vscode/tasks.json b/drivers/0x0c_multicore/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0c_multicore/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0c_multicore/0x0c_multicore.c b/drivers/0x0c_multicore/0x0c_multicore.c new file mode 100644 index 0000000..fefa95c --- /dev/null +++ b/drivers/0x0c_multicore/0x0c_multicore.c @@ -0,0 +1,77 @@ +/** + * @file 0x0c_multicore.c + * @brief Multicore FIFO messaging demo using multicore.c/multicore.h + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates dual-core operation using the multicore driver + * (multicore.h / multicore.c). Core 0 sends an incrementing counter to + * core 1 via the FIFO; core 1 returns the value plus one. Both values + * are printed over UART every second. + * + * Wiring: + * No external wiring required (dual-core communication is on-chip) + */ + +#include +#include "pico/stdlib.h" +#include "multicore.h" + +/** + * @brief Core 1 entry point: receive a value and return it incremented + * + * Blocks on the FIFO, adds one to each received value, and pushes + * the result back to core 0. + */ +static void core1_main(void) { + while (true) { + uint32_t value = multicore_driver_pop(); + multicore_driver_push(value + 1u); + } +} + +/** + * @brief Send the counter to core 1 and print the round-trip result + * + * @param counter Pointer to the running counter (post-incremented) + */ +static void send_and_print(uint32_t *counter) { + multicore_driver_push(*counter); + uint32_t response = multicore_driver_pop(); + printf("core0 sent: %lu, core1 returned: %lu\r\n", + (unsigned long)*counter, (unsigned long)response); + (*counter)++; + sleep_ms(1000); +} + +int main(void) { + stdio_init_all(); + multicore_driver_launch(core1_main); + uint32_t counter = 0; + while (true) + send_and_print(&counter); +} diff --git a/drivers/0x0c_multicore/CMakeLists.txt b/drivers/0x0c_multicore/CMakeLists.txt new file mode 100644 index 0000000..75b2ad2 --- /dev/null +++ b/drivers/0x0c_multicore/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0c_multicore C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0c_multicore 0x0c_multicore.c multicore.c) + +pico_set_program_name(0x0c_multicore "0x0c_multicore") +pico_set_program_version(0x0c_multicore "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0c_multicore 1) +pico_enable_stdio_usb(0x0c_multicore 0) + +# Add the standard library to the build +target_link_libraries(0x0c_multicore + pico_stdlib + pico_multicore) + +# Add the standard include files to the build +target_include_directories(0x0c_multicore PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0c_multicore) diff --git a/drivers/0x0c_multicore/multicore.c b/drivers/0x0c_multicore/multicore.c new file mode 100644 index 0000000..d0a93f9 --- /dev/null +++ b/drivers/0x0c_multicore/multicore.c @@ -0,0 +1,43 @@ +/** + * @file multicore.c + * @brief Implementation of multicore FIFO driver for RP2350 + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "multicore.h" +#include "pico/multicore.h" + +void multicore_driver_launch(void (*core1_entry)(void)) { + multicore_launch_core1(core1_entry); +} + +void multicore_driver_push(uint32_t data) { + multicore_fifo_push_blocking(data); +} + +uint32_t multicore_driver_pop(void) { + return multicore_fifo_pop_blocking(); +} diff --git a/drivers/0x0c_multicore/multicore.h b/drivers/0x0c_multicore/multicore.h new file mode 100644 index 0000000..2618038 --- /dev/null +++ b/drivers/0x0c_multicore/multicore.h @@ -0,0 +1,66 @@ +/** + * @file multicore.h + * @brief Header for dual-core (multicore) driver using the FIFO mailbox + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MULTICORE_H +#define MULTICORE_H + +#include + +/** + * @brief Start the provided function on core 1 + * + * Launches the given entry function on the second RP2350 core using the + * Pico SDK multicore_launch_core1() API. The function will run + * concurrently with core 0 from the moment this call returns. + * + * @param core1_entry Pointer to the void(void) function to run on core 1 + */ +void multicore_driver_launch(void (*core1_entry)(void)); + +/** + * @brief Push a 32-bit value into the inter-core FIFO (blocking) + * + * Writes @p data to the FIFO that core 1 reads from. Blocks until + * there is space in the hardware FIFO. + * + * @param data 32-bit value to send to core 1 + */ +void multicore_driver_push(uint32_t data); + +/** + * @brief Pop a 32-bit value from the inter-core FIFO (blocking) + * + * Reads one entry from the FIFO that core 1 writes into. Blocks until + * a value is available. + * + * @return uint32_t Value received from core 1 + */ +uint32_t multicore_driver_pop(void); + +#endif // MULTICORE_H diff --git a/drivers/0x0c_multicore/pico_sdk_import.cmake b/drivers/0x0c_multicore/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x0c_multicore/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x0c_multicore_cbm/Inc/rp2350.h b/drivers/0x0c_multicore_cbm/Inc/rp2350.h new file mode 100644 index 0000000..fbc6597 --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Inc/rp2350.h @@ -0,0 +1,244 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define PSM_BASE 0x40018000UL +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL + +/*!< Atomic register alias offsets */ +#define ATOMIC_SET_OFFSET 0x2000UL +#define ATOMIC_CLR_OFFSET 0x3000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[12]; // GPOUT0..GPOUT3 registers Address offset: 0x00-0x2C + __IO uint32_t CLK_REF_CTRL; // Reference clock control Address offset: 0x30 + __IO uint32_t RESERVED1[5]; // CLK_REF_DIV..CLK_SYS_SELECTED Address offset: 0x34-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief PSM (Power-on State Machine) + */ +typedef struct +{ + __IO uint32_t FRCE_ON; // Force block out of reset Address offset: 0x00 + __IO uint32_t FRCE_OFF; // Force block into reset Address offset: 0x04 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x08 + __IO uint32_t DONE; // Subsystem ready status Address offset: 0x0C +} PSM_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define PSM ((PSM_TypeDef *) PSM_BASE) +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLK_REF bit definitions + */ +#define CLK_REF_CTRL_SRC_XOSC 2U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_MASK (0x07U << CLK_PERI_CTRL_AUXSRC_SHIFT) +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_PADS_BANK0_SHIFT 9U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief PSM bit definitions + */ +#define PSM_FRCE_OFF_PROC1_SHIFT 24U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U +#define IO_BANK0_CTRL_FUNCSEL_NULL 0x1FU + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U +#define PADS_BANK0_PUE_SHIFT 3U +#define PADS_BANK0_PDE_SHIFT 2U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief SIO FIFO register offsets (word indices from SIO_BASE) + */ +#define SIO_CPUID_OFFSET (0x000U / 4U) +#define SIO_FIFO_ST_OFFSET (0x050U / 4U) +#define SIO_FIFO_WR_OFFSET (0x054U / 4U) +#define SIO_FIFO_RD_OFFSET (0x058U / 4U) + +/** + * @brief SIO FIFO status register bit definitions + */ +#define SIO_FIFO_ST_VLD_MASK (1U << 0) +#define SIO_FIFO_ST_RDY_MASK (1U << 1) + +#endif /* __RP2350_H */ diff --git a/drivers/0x0c_multicore_cbm/Inc/rp2350_delay.h b/drivers/0x0c_multicore_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..79db58f --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Inc/rp2350_delay.h @@ -0,0 +1,51 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond and microsecond busy-wait delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +/** + * @brief Delay for the specified number of microseconds. + * @param us number of microseconds to delay + * @retval None + */ +void delay_us(uint32_t us); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x0c_multicore_cbm/Inc/rp2350_multicore.h b/drivers/0x0c_multicore_cbm/Inc/rp2350_multicore.h new file mode 100644 index 0000000..beb037e --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Inc/rp2350_multicore.h @@ -0,0 +1,71 @@ +/** + * @file rp2350_multicore.h + * @brief Multicore driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal dual-core driver using the SIO inter-processor + * FIFOs. Provides core 1 launch, blocking push, and blocking + * pop operations for 32-bit mailbox messaging. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_MULTICORE_H +#define __RP2350_MULTICORE_H + +#include "rp2350.h" + +/** + * @brief Launch a function on processor core 1. + * + * Resets core 1 via the PSM, then performs the FIFO handshake + * protocol described in RP2350 datasheet Section 5.3 to pass + * the vector table, stack pointer, and entry point. + * + * @param entry pointer to the void(void) function to run on core 1 + * @retval None + */ +void multicore_launch(void (*entry)(void)); + +/** + * @brief Push a 32-bit value into the inter-core FIFO (blocking). + * + * Blocks until there is space in the TX FIFO, then writes the + * value and signals the other core with SEV. + * + * @param data 32-bit value to send + * @retval None + */ +void multicore_fifo_push(uint32_t data); + +/** + * @brief Pop a 32-bit value from the inter-core FIFO (blocking). + * + * Blocks with WFE until a value is available in the RX FIFO, + * then returns it. + * + * @retval uint32_t value received from the other core + */ +uint32_t multicore_fifo_pop(void); + +#endif /* __RP2350_MULTICORE_H */ diff --git a/drivers/0x0c_multicore_cbm/Inc/rp2350_reset.h b/drivers/0x0c_multicore_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x0c_multicore_cbm/Inc/rp2350_reset_handler.h b/drivers/0x0c_multicore_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x0c_multicore_cbm/Inc/rp2350_stack.h b/drivers/0x0c_multicore_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x0c_multicore_cbm/Inc/rp2350_uart.h b/drivers/0x0c_multicore_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x0c_multicore_cbm/Inc/rp2350_xosc.h b/drivers/0x0c_multicore_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..80fd75d --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,55 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +/** + * @brief Switch CLK_REF source to XOSC for a stable 12 MHz clk_sys. + * @retval None + */ +void xosc_set_clk_ref(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x0c_multicore_cbm/Makefile b/drivers/0x0c_multicore_cbm/Makefile new file mode 100644 index 0000000..1ade403 --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Makefile @@ -0,0 +1,85 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C multicore FIFO driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = multicore + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_multicore.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x0c_multicore_cbm/Src/image_def.c b/drivers/0x0c_multicore_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x0c_multicore_cbm/Src/main.c b/drivers/0x0c_multicore_cbm/Src/main.c new file mode 100644 index 0000000..27500eb --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Src/main.c @@ -0,0 +1,151 @@ +/** + * @file main.c + * @brief Multicore FIFO messaging demonstration. + * @author Kevin Thomas + * @date 2026 + * + * Core 0 launches core 1, then sends an incrementing counter + * through the SIO inter-processor FIFO. Core 1 returns the + * value plus one. Both values are printed over UART every + * second. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * No external wiring required (dual-core on-chip) + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_multicore.h" +#include "rp2350_uart.h" +#include "rp2350_delay.h" + +/** + * @brief Extract decimal digits from a value in reverse order. + * @param value unsigned integer to convert + * @param tmp output buffer for reversed digits (min 11 bytes) + * @retval int number of digits written + */ +static int extract_digits(uint32_t value, char *tmp) +{ + int i = 0; + if (value == 0) + tmp[i++] = '0'; + while (value > 0) + { + tmp[i++] = '0' + (char)(value % 10); + value /= 10; + } + return i; +} + +/** + * @brief Reverse-copy a digit buffer into a null-terminated string. + * @param tmp source buffer with reversed digits + * @param len number of digits in source + * @param buf destination buffer (must hold len + 1 bytes) + * @retval None + */ +static void reverse_copy(const char *tmp, int len, char *buf) +{ + int j = 0; + while (len > 0) + buf[j++] = tmp[--len]; + buf[j] = '\0'; +} + +/** + * @brief Convert a uint32_t to a decimal null-terminated string. + * @param value unsigned integer to convert + * @param buf output buffer (min 12 bytes) + * @retval None + */ +static void uint_to_str(uint32_t value, char *buf) +{ + char tmp[11]; + int len = extract_digits(value, tmp); + reverse_copy(tmp, len, buf); +} + +/** + * @brief Print a labelled decimal value over UART. + * @param label text label to print before the value + * @param value unsigned integer to print + * @retval None + */ +static void print_labeled_value(const char *label, uint32_t value) +{ + char buf[12]; + uart_puts(label); + uint_to_str(value, buf); + uart_puts(buf); +} + +/** + * @brief Print a full sent/returned line over UART. + * @param sent value sent by core 0 + * @param received value returned by core 1 + * @retval None + */ +static void print_counter_line(uint32_t sent, uint32_t received) +{ + print_labeled_value("core0 sent: ", sent); + print_labeled_value(", core1 returned: ", received); + uart_puts("\r\n"); +} + +/** + * @brief Core 1 entry point: receive a value and return it plus one. + * @retval None (does not return) + */ +static void core1_main(void) +{ + while (1) + { + uint32_t value = multicore_fifo_pop(); + multicore_fifo_push(value + 1); + } +} + +/** + * @brief Send the counter to core 1, print the round-trip, delay. + * @param counter pointer to the running counter (post-incremented) + * @retval None + */ +static void send_and_print(uint32_t *counter) +{ + multicore_fifo_push(*counter); + uint32_t response = multicore_fifo_pop(); + print_counter_line(*counter, response); + (*counter)++; + delay_ms(1000); +} + +int main(void) +{ + uint32_t counter = 0; + multicore_launch(core1_main); + uart_puts("Multicore FIFO demo initialized\r\n"); + while (1) + send_and_print(&counter); +} diff --git a/drivers/0x0c_multicore_cbm/Src/rp2350_delay.c b/drivers/0x0c_multicore_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..65b0821 --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Src/rp2350_delay.c @@ -0,0 +1,64 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond and microsecond delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} + +void delay_us(uint32_t us) +{ + if (us == 0) + return; + __asm__ volatile ( + "mov r4, #4\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (us) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x0c_multicore_cbm/Src/rp2350_multicore.c b/drivers/0x0c_multicore_cbm/Src/rp2350_multicore.c new file mode 100644 index 0000000..e5e45d1 --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Src/rp2350_multicore.c @@ -0,0 +1,155 @@ +/** + * @file rp2350_multicore.c + * @brief Multicore driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Implements bare-metal core 1 launch via the PSM reset and + * SIO FIFO handshake protocol (RP2350 datasheet Section 5.3). + * Provides blocking push/pop for inter-core 32-bit messaging. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_multicore.h" + +/** + * @brief Number of 32-bit words in the core 1 stack (4096 bytes). + */ +#define CORE1_STACK_WORDS 1024U + +/** + * @brief Core 1 stack array allocated in BSS. + */ +static uint32_t core1_stack[CORE1_STACK_WORDS]; + +/** + * @brief Drain all pending values from the RX FIFO. + * @retval None + */ +static void fifo_drain(void) +{ + while (SIO[SIO_FIFO_ST_OFFSET] & SIO_FIFO_ST_VLD_MASK) + (void)SIO[SIO_FIFO_RD_OFFSET]; +} + +/** + * @brief Push one 32-bit word to the TX FIFO, blocking until ready. + * @param data value to write + * @retval None + */ +static void fifo_push_blocking(uint32_t data) +{ + while (!(SIO[SIO_FIFO_ST_OFFSET] & SIO_FIFO_ST_RDY_MASK)) {} + SIO[SIO_FIFO_WR_OFFSET] = data; + __asm__ volatile ("sev"); +} + +/** + * @brief Pop one 32-bit word from the RX FIFO, blocking until valid. + * @retval uint32_t value read from the FIFO + */ +static uint32_t fifo_pop_blocking(void) +{ + while (!(SIO[SIO_FIFO_ST_OFFSET] & SIO_FIFO_ST_VLD_MASK)) {} + return SIO[SIO_FIFO_RD_OFFSET]; +} + +/** + * @brief Force core 1 into reset via PSM atomic set alias. + * @retval None + */ +static void set_frce_off_proc1(void) +{ + volatile uint32_t *set = (volatile uint32_t *)((uintptr_t)&PSM->FRCE_OFF + ATOMIC_SET_OFFSET); + *set = (1U << PSM_FRCE_OFF_PROC1_SHIFT); + while (!(PSM->FRCE_OFF & (1U << PSM_FRCE_OFF_PROC1_SHIFT))) {} +} + +/** + * @brief Release core 1 from reset via PSM atomic clear alias. + * @retval None + */ +static void clr_frce_off_proc1(void) +{ + volatile uint32_t *clr = (volatile uint32_t *)((uintptr_t)&PSM->FRCE_OFF + ATOMIC_CLR_OFFSET); + *clr = (1U << PSM_FRCE_OFF_PROC1_SHIFT); +} + +/** + * @brief Reset core 1 and wait for its boot FIFO acknowledgement. + * @retval None + */ +static void reset_core1(void) +{ + set_frce_off_proc1(); + clr_frce_off_proc1(); + (void)fifo_pop_blocking(); +} + +/** + * @brief Send one handshake word, draining the FIFO first for zeroes. + * @param cmd the command word to send + * @retval None + */ +static void send_handshake_word(uint32_t cmd) +{ + if (!cmd) + { + fifo_drain(); + __asm__ volatile ("sev"); + } + fifo_push_blocking(cmd); +} + +/** + * @brief Perform the six-word FIFO handshake to launch core 1. + * @param entry pointer to the core 1 entry function + * @retval None + */ +static void launch_handshake(void (*entry)(void)) +{ + extern uint32_t __Vectors; + uint32_t *sp = &core1_stack[CORE1_STACK_WORDS]; + const uint32_t seq[] = {0, 0, 1, (uintptr_t)&__Vectors, (uintptr_t)sp, (uintptr_t)entry}; + uint32_t idx = 0; + do { + send_handshake_word(seq[idx]); + idx = (fifo_pop_blocking() == seq[idx]) ? idx + 1 : 0; + } while (idx < 6); +} + +void multicore_launch(void (*entry)(void)) +{ + reset_core1(); + launch_handshake(entry); +} + +void multicore_fifo_push(uint32_t data) +{ + fifo_push_blocking(data); +} + +uint32_t multicore_fifo_pop(void) +{ + return fifo_pop_blocking(); +} diff --git a/drivers/0x0c_multicore_cbm/Src/rp2350_reset.c b/drivers/0x0c_multicore_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x0c_multicore_cbm/Src/rp2350_reset_handler.c b/drivers/0x0c_multicore_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..62b58d2 --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,80 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, then branches + * to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x0c_multicore_cbm/Src/rp2350_stack.c b/drivers/0x0c_multicore_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x0c_multicore_cbm/Src/rp2350_uart.c b/drivers/0x0c_multicore_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x0c_multicore_cbm/Src/rp2350_xosc.c b/drivers/0x0c_multicore_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..b33a71d --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Src/rp2350_xosc.c @@ -0,0 +1,54 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value &= ~CLK_PERI_CTRL_AUXSRC_MASK; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} + +void xosc_set_clk_ref(void) +{ + CLOCKS->CLK_REF_CTRL = CLK_REF_CTRL_SRC_XOSC; +} diff --git a/drivers/0x0c_multicore_cbm/Src/vector_table.c b/drivers/0x0c_multicore_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x0c_multicore_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x0c_multicore_cbm/build.log b/drivers/0x0c_multicore_cbm/build.log new file mode 100644 index 0000000..31c61d1 Binary files /dev/null and b/drivers/0x0c_multicore_cbm/build.log differ diff --git a/drivers/0x0c_multicore_cbm/linker.ld b/drivers/0x0c_multicore_cbm/linker.ld new file mode 100644 index 0000000..56ac361 --- /dev/null +++ b/drivers/0x0c_multicore_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x0c_multicore_rust/.cargo/config.toml b/drivers/0x0c_multicore_rust/.cargo/config.toml new file mode 100644 index 0000000..18ef831 --- /dev/null +++ b/drivers/0x0c_multicore_rust/.cargo/config.toml @@ -0,0 +1,20 @@ +[build] +target = "thumbv8m.main-none-eabihf" + +[target.thumbv6m-none-eabi] +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.thumbv8m.main-none-eabihf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] diff --git a/drivers/0x0c_multicore_rust/.pico-rs b/drivers/0x0c_multicore_rust/.pico-rs new file mode 100644 index 0000000..be06904 --- /dev/null +++ b/drivers/0x0c_multicore_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 diff --git a/drivers/0x0c_multicore_rust/.vscode/extensions.json b/drivers/0x0c_multicore_rust/.vscode/extensions.json new file mode 100644 index 0000000..5f2fd0c --- /dev/null +++ b/drivers/0x0c_multicore_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0c_multicore_rust/.vscode/launch.json b/drivers/0x0c_multicore_rust/.vscode/launch.json new file mode 100644 index 0000000..0bc38c3 --- /dev/null +++ b/drivers/0x0c_multicore_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} \ No newline at end of file diff --git a/drivers/0x0c_multicore_rust/.vscode/settings.json b/drivers/0x0c_multicore_rust/.vscode/settings.json new file mode 100644 index 0000000..b03cd57 --- /dev/null +++ b/drivers/0x0c_multicore_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} \ No newline at end of file diff --git a/drivers/0x0c_multicore_rust/.vscode/tasks.json b/drivers/0x0c_multicore_rust/.vscode/tasks.json new file mode 100644 index 0000000..d288f27 --- /dev/null +++ b/drivers/0x0c_multicore_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} diff --git a/drivers/0x0c_multicore_rust/Cargo.lock b/drivers/0x0c_multicore_rust/Cargo.lock new file mode 100644 index 0000000..55856a2 --- /dev/null +++ b/drivers/0x0c_multicore_rust/Cargo.lock @@ -0,0 +1,750 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "crc-any" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "frunk" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28aef0f9aa070bce60767c12ba9cb41efeaf1a2bc6427f87b7d83f11239a16d7" +dependencies = [ + "frunk_core", + "frunk_derives", +] + +[[package]] +name = "frunk_core" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476eeaa382e3462b84da5d6ba3da97b5786823c2d0d3a0d04ef088d073da225c" + +[[package]] +name = "frunk_derives" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b4095fc99e1d858e5b8c7125d2638372ec85aa0fe6c807105cf10b0265ca6c" +dependencies = [ + "frunk_proc_macro_helpers", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "frunk_proc_macro_helpers" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1952b802269f2db12ab7c0bd328d0ae8feaabf19f352a7b0af7bb0c5693abfce" +dependencies = [ + "frunk_core", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fugit" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "multicore" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "embedded-hal 1.0.0", + "fugit", + "panic-halt", + "panic-probe", + "regex", + "rp2040-boot2", + "rp2040-hal", + "rp235x-hal", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "panic-halt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" + +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "riscv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", +] + +[[package]] +name = "riscv-rt" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d35e32cf1383183e8885d8a9aa4402a087fd094dc34c2cb6df6687d0229dfe" +dependencies = [ + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "riscv-rt-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp-binary-info" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f582261945fa215d40e2988b4df595d11c0908c0fff97a0fe23df766d117b790" + +[[package]] +name = "rp-hal-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288358786b1458fb2caac8c4b40fb529ef4200d6c46467e2695b7a8ba573ae8" +dependencies = [ + "fugit", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rp2040-hal" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb79a4590775204387f334672e6f79c0d734d0a159da23d60677b3c10fa1245" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "itertools 0.10.5", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "rp-binary-info", + "rp-hal-common", + "rp2040-hal-macros", + "rp2040-pac", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp2040-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e" +dependencies = [ + "cortex-m-rt", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rp2040-pac" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cbcd3f7a0ca7bbe61dc4eb7e202842bee4e27b769a7bf3a4a72fa399d6e404" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rp235x-hal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2939c82776b0b4ae110168b4298b5adf831e6cff249b057bf2a2187453b959c" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "cortex-m-rt", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "gcd", + "itertools 0.13.0", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "riscv", + "riscv-rt", + "rp-binary-info", + "rp-hal-common", + "rp235x-hal-macros", + "rp235x-pac", + "sha2-const-stable", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp235x-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74edd7a5979e9763bbb98e9746e711bac7464ee3397af7288e6c288ff0d3c764" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp235x-pac" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffcb6931deee4242886b5a1df62db5e2555b0eb6ae1e8be101f3ea3e58e65c6" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless", + "portable-atomic", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/drivers/0x0c_multicore_rust/Cargo.toml b/drivers/0x0c_multicore_rust/Cargo.toml new file mode 100644 index 0000000..db46c60 --- /dev/null +++ b/drivers/0x0c_multicore_rust/Cargo.toml @@ -0,0 +1,40 @@ +[package] +edition = "2024" +name = "multicore" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "multicore_lib" +path = "src/lib.rs" + +[[bin]] +name = "multicore" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" +embedded-hal = "1.0" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x0c_multicore_rust/build.rs b/drivers/0x0c_multicore_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x0c_multicore_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x0c_multicore_rust/rp2040.x b/drivers/0x0c_multicore_rust/rp2040.x new file mode 100644 index 0000000..e66f718 --- /dev/null +++ b/drivers/0x0c_multicore_rust/rp2040.x @@ -0,0 +1,44 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; diff --git a/drivers/0x0c_multicore_rust/rp2350.x b/drivers/0x0c_multicore_rust/rp2350.x new file mode 100644 index 0000000..570f72c --- /dev/null +++ b/drivers/0x0c_multicore_rust/rp2350.x @@ -0,0 +1,47 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); diff --git a/drivers/0x0c_multicore_rust/rp2350_riscv.x b/drivers/0x0c_multicore_rust/rp2350_riscv.x new file mode 100644 index 0000000..2c9b445 --- /dev/null +++ b/drivers/0x0c_multicore_rust/rp2350_riscv.x @@ -0,0 +1,58 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +PROVIDE(__pre_init = default_pre_init); +PROVIDE(_setup_interrupts = default_setup_interrupts); +PROVIDE(_mp_hook = default_mp_hook); +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + . = ABSOLUTE(_stext); + } > FLASH +} diff --git a/drivers/0x0c_multicore_rust/src/board.rs b/drivers/0x0c_multicore_rust/src/board.rs new file mode 100644 index 0000000..af1cea7 --- /dev/null +++ b/drivers/0x0c_multicore_rust/src/board.rs @@ -0,0 +1,245 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Multicore pure-logic helpers and constants +use multicore_lib::multicore; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +// Multicore execution management and stack type for core 1 +use hal::multicore::{Multicore, Stack}; +// SIO type for inter-core FIFO and GPIO bank ownership +use hal::sio::{Sio, SioFifo}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Delay between FIFO round-trip messages in milliseconds. +pub(crate) const POLL_MS: u32 = 1_000; + +/// Maximum buffer size for formatting a round-trip message. +pub(crate) const MSG_BUF_LEN: usize = 52; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX pins. +pub(crate) type EnabledUart = UartPeripheral; + +// Stack allocation for core 1 (4096 words) +/// The core1 stack static variable. +static CORE1_STACK: Stack<4096> = Stack::new(); + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set along with the SIO FIFO. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> (hal::gpio::Pins, SioFifo) { + let sio = Sio::new(sio); + let pins = hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets); + (pins, sio.fifo) +} + +/// Initialise UART0 for serial output. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Core 1 entry point: receive values via FIFO, increment, and return. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +fn core1_entry() -> ! { + let pac = unsafe { hal::pac::Peripherals::steal() }; + let sio = Sio::new(pac.SIO); + let mut fifo = sio.fifo; + loop { + let value = fifo.read_blocking(); + fifo.write_blocking(multicore::increment_value(value)); + } +} + +/// Launch core 1 with its FIFO echo task. +/// +/// # Arguments +/// +/// * `psm` - The `psm` parameter. +/// * `ppb` - The `ppb` parameter. +/// * `fifo` - The `fifo` parameter. +/// +/// # Arguments +/// +/// * `psm` - The `psm` parameter. +/// * `ppb` - The `ppb` parameter. +/// * `fifo` - The `fifo` parameter. +pub(crate) fn spawn_core1(psm: &mut hal::pac::PSM, ppb: &mut hal::pac::PPB, fifo: &mut SioFifo) { + let mut mc = Multicore::new(psm, ppb, fifo); + let cores = mc.cores(); + let core1 = &mut cores[1]; + let _ = core1.spawn(CORE1_STACK.take().unwrap(), move || core1_entry()); +} + +/// Send the counter to core 1 via FIFO and print the round-trip result. +pub(crate) fn send_and_print( + fifo: &mut SioFifo, + uart: &EnabledUart, + counter: &mut u32, + delay: &mut cortex_m::delay::Delay, +) { + fifo.write_blocking(*counter); + let response = fifo.read_blocking(); + let mut buf = [0u8; MSG_BUF_LEN]; + let n = multicore::format_round_trip(&mut buf, *counter, response); + uart.write_full_blocking(&buf[..n]); + *counter = counter.wrapping_add(1); + delay.delay_ms(POLL_MS); +} + +/// Initialise all peripherals and run the multicore FIFO demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let (pins, mut fifo) = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + spawn_core1(&mut pac.PSM, &mut pac.PPB, &mut fifo); + let mut counter = 0u32; + loop { + send_and_print(&mut fifo, &uart, &mut counter, &mut delay); + } +} + diff --git a/drivers/0x0c_multicore_rust/src/lib.rs b/drivers/0x0c_multicore_rust/src/lib.rs new file mode 100644 index 0000000..cf2940d --- /dev/null +++ b/drivers/0x0c_multicore_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod multicore; diff --git a/drivers/0x0c_multicore_rust/src/main.rs b/drivers/0x0c_multicore_rust/src/main.rs new file mode 100644 index 0000000..f1d78b6 --- /dev/null +++ b/drivers/0x0c_multicore_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// Multicore driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the multicore FIFO demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"Multicore FIFO Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x0c_multicore_rust/src/multicore.rs b/drivers/0x0c_multicore_rust/src/multicore.rs new file mode 100644 index 0000000..d323f55 --- /dev/null +++ b/drivers/0x0c_multicore_rust/src/multicore.rs @@ -0,0 +1,283 @@ +//! Implementation module +//! +//! **File:** `multicore.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// Increment a 32-bit value by one (core 1 processing logic). +/// +/// # Arguments +/// +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +/// +/// # Arguments +/// +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A 32-bit unsigned integer value. +pub fn increment_value(value: u32) -> u32 { + value.wrapping_add(1) +} + +/// Format a u32 value as decimal ASCII into a buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_u32(buf: &mut [u8], value: u32) -> usize { + if value == 0 { + buf[0] = b'0'; + return 1; + } + let mut tmp = [0u8; 10]; + let n = u32_to_digits_reversed(&mut tmp, value); + reverse_copy(buf, &tmp, n); + n +} + +/// Convert a u32 to reversed decimal digits in a temporary buffer. +/// +/// # Arguments +/// +/// * `tmp` - The `tmp` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `tmp` - The `tmp` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn u32_to_digits_reversed(tmp: &mut [u8; 10], mut value: u32) -> usize { + let mut n = 0usize; + while value > 0 { + tmp[n] = b'0' + (value % 10) as u8; + value /= 10; + n += 1; + } + n +} + +/// Copy digits from a reversed temporary buffer into the output buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `tmp` - The `tmp` parameter. +/// * `n` - Nibble or number. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `tmp` - The `tmp` parameter. +/// * `n` - Nibble or number. +fn reverse_copy(buf: &mut [u8], tmp: &[u8], n: usize) { + for i in 0..n { + buf[i] = tmp[n - 1 - i]; + } +} + +/// Copy a byte slice into `buf` at the given offset, returning bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `offset` - The `offset` parameter. +/// * `src` - The `src` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `offset` - The `offset` parameter. +/// * `src` - The `src` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn copy_slice(buf: &mut [u8], offset: usize, src: &[u8]) -> usize { + buf[offset..offset + src.len()].copy_from_slice(src); + src.len() +} + +/// Append a CRLF sequence at `pos` in `buf` and return the new position. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `pos` - The `pos` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn append_crlf(buf: &mut [u8], pos: usize) -> usize { + buf[pos] = b'\r'; + buf[pos + 1] = b'\n'; + pos + 2 +} + +/// Format the round-trip message for UART output. +/// +/// Produces: `core0 sent: N, core1 returned: N\r\n` +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `sent` - The `sent` parameter. +/// * `returned` - The `returned` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `sent` - The `sent` parameter. +/// * `returned` - The `returned` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_round_trip(buf: &mut [u8], sent: u32, returned: u32) -> usize { + let mut pos = copy_slice(buf, 0, b"core0 sent: "); + pos += format_u32(&mut buf[pos..], sent); + pos += copy_slice(buf, pos, b", core1 returned: "); + pos += format_u32(&mut buf[pos..], returned); + append_crlf(buf, pos) +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the increment value adds one operation. + #[test] + fn increment_value_adds_one() { + assert_eq!(increment_value(0), 1); + assert_eq!(increment_value(41), 42); + } + + /// Executes the increment value wraps at max operation. + #[test] + fn increment_value_wraps_at_max() { + assert_eq!(increment_value(u32::MAX), 0); + } + + /// Executes the format u32 zero operation. + #[test] + fn format_u32_zero() { + let mut buf = [0u8; 10]; + let n = format_u32(&mut buf, 0); + assert_eq!(&buf[..n], b"0"); + } + + /// Executes the format u32 single digit operation. + #[test] + fn format_u32_single_digit() { + let mut buf = [0u8; 10]; + let n = format_u32(&mut buf, 7); + assert_eq!(&buf[..n], b"7"); + } + + /// Executes the format u32 multi digit operation. + #[test] + fn format_u32_multi_digit() { + let mut buf = [0u8; 10]; + let n = format_u32(&mut buf, 12345); + assert_eq!(&buf[..n], b"12345"); + } + + /// Executes the format u32 max operation. + #[test] + fn format_u32_max() { + let mut buf = [0u8; 10]; + let n = format_u32(&mut buf, u32::MAX); + assert_eq!(&buf[..n], b"4294967295"); + } + + /// Executes the format round trip small values operation. + #[test] + fn format_round_trip_small_values() { + let mut buf = [0u8; 52]; + let n = format_round_trip(&mut buf, 0, 1); + assert_eq!(&buf[..n], b"core0 sent: 0, core1 returned: 1\r\n"); + } + + /// Executes the format round trip larger values operation. + #[test] + fn format_round_trip_larger_values() { + let mut buf = [0u8; 52]; + let n = format_round_trip(&mut buf, 42, 43); + assert_eq!(&buf[..n], b"core0 sent: 42, core1 returned: 43\r\n"); + } + + /// Executes the format round trip max values operation. + #[test] + fn format_round_trip_max_values() { + let mut buf = [0u8; 52]; + let n = format_round_trip(&mut buf, u32::MAX, 0); + assert_eq!(&buf[..n], b"core0 sent: 4294967295, core1 returned: 0\r\n"); + } +} diff --git a/drivers/0x0d_timer/.vscode/.vscode/c_cpp_properties.json b/drivers/0x0d_timer/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0d_timer/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0d_timer/.vscode/.vscode/cmake-kits.json b/drivers/0x0d_timer/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0d_timer/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0d_timer/.vscode/.vscode/extensions.json b/drivers/0x0d_timer/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0d_timer/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0d_timer/.vscode/.vscode/launch.json b/drivers/0x0d_timer/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0d_timer/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0d_timer/.vscode/.vscode/settings.json b/drivers/0x0d_timer/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0d_timer/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0d_timer/.vscode/.vscode/tasks.json b/drivers/0x0d_timer/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0d_timer/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0d_timer/.vscode/c_cpp_properties.json b/drivers/0x0d_timer/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0d_timer/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0d_timer/.vscode/cmake-kits.json b/drivers/0x0d_timer/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0d_timer/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0d_timer/.vscode/extensions.json b/drivers/0x0d_timer/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0d_timer/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0d_timer/.vscode/launch.json b/drivers/0x0d_timer/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0d_timer/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0d_timer/.vscode/settings.json b/drivers/0x0d_timer/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0d_timer/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0d_timer/.vscode/tasks.json b/drivers/0x0d_timer/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0d_timer/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0d_timer/0x0d_timer.c b/drivers/0x0d_timer/0x0d_timer.c new file mode 100644 index 0000000..cee6dba --- /dev/null +++ b/drivers/0x0d_timer/0x0d_timer.c @@ -0,0 +1,58 @@ +/** + * @file 0x0d_timer.c + * @brief Repeating timer demo using timer.c/timer.h + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates repeating timer callbacks using the timer driver + * (timer.h / timer.c). A one-second heartbeat timer prints a message + * over UART to confirm the timer is firing. + * + * Wiring: + * No external wiring required + */ + +#include +#include "pico/stdlib.h" +#include "timer.h" + +/** + * @brief Heartbeat timer callback that prints a message over UART + * + * @return bool true to keep the repeating timer active + */ +static bool heartbeat_callback(void) { + printf("Timer heartbeat\r\n"); + return true; +} + +int main(void) { + stdio_init_all(); + timer_driver_start(1000, heartbeat_callback); + while (true) + tight_loop_contents(); +} diff --git a/drivers/0x0d_timer/CMakeLists.txt b/drivers/0x0d_timer/CMakeLists.txt new file mode 100644 index 0000000..3306ced --- /dev/null +++ b/drivers/0x0d_timer/CMakeLists.txt @@ -0,0 +1,56 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0d_timer C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0d_timer 0x0d_timer.c timer.c) + +pico_set_program_name(0x0d_timer "0x0d_timer") +pico_set_program_version(0x0d_timer "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0d_timer 1) +pico_enable_stdio_usb(0x0d_timer 0) + +# Add the standard library to the build +target_link_libraries(0x0d_timer + pico_stdlib) + +# Add the standard include files to the build +target_include_directories(0x0d_timer PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0d_timer) diff --git a/drivers/0x0d_timer/pico_sdk_import.cmake b/drivers/0x0d_timer/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x0d_timer/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x0d_timer/timer.c b/drivers/0x0d_timer/timer.c new file mode 100644 index 0000000..f3f0733 --- /dev/null +++ b/drivers/0x0d_timer/timer.c @@ -0,0 +1,67 @@ +/** + * @file timer.c + * @brief Implementation of repeating timer driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "timer.h" +#include "pico/time.h" + +/** @brief Pico SDK repeating timer handle */ +static repeating_timer_t g_timer; +/** @brief Flag indicating whether the timer is running */ +static bool g_timer_active = false; +/** @brief User-provided timer callback function */ +static timer_driver_callback_t g_user_callback = NULL; + +/** + * @brief Internal repeating-timer callback that forwards to the user callback + * + * @param rt Unused repeating-timer handle provided by the SDK + * @return bool true to keep the timer running, false to cancel + */ +static bool timer_shim(repeating_timer_t *rt) { + (void)rt; + if (g_user_callback) + return g_user_callback(); + return false; +} + +void timer_driver_start(int32_t period_ms, timer_driver_callback_t callback) { + if (g_timer_active) { + cancel_repeating_timer(&g_timer); + g_timer_active = false; + } + g_user_callback = callback; + g_timer_active = add_repeating_timer_ms(period_ms, timer_shim, NULL, &g_timer); +} + +void timer_driver_cancel(void) { + if (!g_timer_active) + return; + cancel_repeating_timer(&g_timer); + g_timer_active = false; +} diff --git a/drivers/0x0d_timer/timer.h b/drivers/0x0d_timer/timer.h new file mode 100644 index 0000000..2141a62 --- /dev/null +++ b/drivers/0x0d_timer/timer.h @@ -0,0 +1,64 @@ +/** + * @file timer.h + * @brief Header for repeating hardware timer driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TIMER_H +#define TIMER_H + +#include +#include + +/** + * @brief Timer callback function signature + * + * Must return true to keep the timer repeating, or false to stop. + */ +typedef bool (*timer_driver_callback_t)(void); + +/** + * @brief Start a repeating hardware timer that fires the given callback + * + * Schedules @p callback to be invoked every @p period_ms milliseconds using + * the Pico SDK add_repeating_timer_ms() API. The timer continues firing + * until timer_driver_cancel() is called. + * + * @param period_ms Interval between callbacks in milliseconds (positive value) + * @param callback Function to call on each timer expiry; must return true to + * continue repeating, false to stop + */ +void timer_driver_start(int32_t period_ms, timer_driver_callback_t callback); + +/** + * @brief Cancel the active repeating timer + * + * Stops the timer started by timer_driver_start(). Safe to call even if the + * timer has already fired and self-cancelled. + */ +void timer_driver_cancel(void); + +#endif // TIMER_H diff --git a/drivers/0x0d_timer_cbm/Inc/rp2350.h b/drivers/0x0d_timer_cbm/Inc/rp2350.h new file mode 100644 index 0000000..3f858fb --- /dev/null +++ b/drivers/0x0d_timer_cbm/Inc/rp2350.h @@ -0,0 +1,275 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define TICKS_BASE 0x40108000UL +#define TIMER0_BASE 0x400B0000UL + +/*!< Atomic register alias offsets */ +#define ATOMIC_SET_OFFSET 0x2000UL +#define ATOMIC_CLR_OFFSET 0x3000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[12]; // GPOUT0..GPOUT3 registers Address offset: 0x00-0x2C + __IO uint32_t CLK_REF_CTRL; // Reference clock control Address offset: 0x30 + __IO uint32_t RESERVED1[5]; // CLK_REF_DIV..CLK_SYS_SELECTED Address offset: 0x34-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief TIMER0 (System Timer) + */ +typedef struct +{ + __IO uint32_t TIMEHW; // Write to bits 63:32 of time (no side effects) Address offset: 0x00 + __IO uint32_t TIMELW; // Write to bits 31:0 of time (triggers latch) Address offset: 0x04 + __IO uint32_t TIMEHR; // Read bits 63:32 of time (latched on TIMELR) Address offset: 0x08 + __IO uint32_t TIMELR; // Read bits 31:0 of time (latches TIMEHR) Address offset: 0x0C + __IO uint32_t ALARM0; // Alarm 0 target (arms on write) Address offset: 0x10 + __IO uint32_t ALARM1; // Alarm 1 target (arms on write) Address offset: 0x14 + __IO uint32_t ALARM2; // Alarm 2 target (arms on write) Address offset: 0x18 + __IO uint32_t ALARM3; // Alarm 3 target (arms on write) Address offset: 0x1C + __IO uint32_t ARMED; // Armed status / disarm by writing 1 Address offset: 0x20 + __IO uint32_t TIMERAWH; // Raw read bits 63:32 (no latch) Address offset: 0x24 + __IO uint32_t TIMERAWL; // Raw read bits 31:0 (no latch) Address offset: 0x28 + __IO uint32_t DBGPAUSE; // Debug pause control Address offset: 0x2C + __IO uint32_t PAUSE; // Pause timer Address offset: 0x30 + __IO uint32_t LOCKED; // Lock writes to timer Address offset: 0x34 + __IO uint32_t SOURCE; // Clock source select Address offset: 0x38 + __IO uint32_t INTR; // Raw interrupts Address offset: 0x3C + __IO uint32_t INTE; // Interrupt enable Address offset: 0x40 + __IO uint32_t INTF; // Interrupt force Address offset: 0x44 + __IO uint32_t INTS; // Interrupt status after masking & forcing Address offset: 0x48 +} TIMER_TypeDef; + +/** + * @brief TICKS (Tick Generator entry, one per source) + */ +typedef struct +{ + __IO uint32_t CTRL; // Enable tick generator (bit 0) Address offset: 0x00 + __IO uint32_t CYCLES; // Number of CLK_REF cycles per tick (bits 8:0) Address offset: 0x04 + __IO uint32_t COUNT; // Running counter of ticks (read-only) Address offset: 0x08 +} TICKS_Entry_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) +#define TIMER0 ((TIMER_TypeDef *) TIMER0_BASE) + +/** + * @brief TICKS entries (indexed from TICKS_BASE, 12-byte stride) + */ +#define TICKS_TIMER0 ((TICKS_Entry_TypeDef *) (TICKS_BASE + 0x18UL)) + +/** + * @brief NVIC registers + */ +#define NVIC_ISER0 ((volatile uint32_t *) (PPB_BASE + 0x0E100UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLK_REF bit definitions + */ +#define CLK_REF_CTRL_SRC_XOSC 2U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_MASK (0x07U << CLK_PERI_CTRL_AUXSRC_SHIFT) +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_PADS_BANK0_SHIFT 9U +#define RESETS_RESET_UART0_SHIFT 26U +#define RESETS_RESET_TIMER0_SHIFT 23U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U +#define IO_BANK0_CTRL_FUNCSEL_NULL 0x1FU + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U +#define PADS_BANK0_PUE_SHIFT 3U +#define PADS_BANK0_PDE_SHIFT 2U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief TIMER0 bit definitions + */ +#define TIMER0_ALARM0_IRQ 0U +#define TIMER_INTE_ALARM0_SHIFT 0U +#define TIMER_INTR_ALARM0_MASK (1U << 0) + +/** + * @brief TICKS bit definitions + */ +#define TICKS_CTRL_ENABLE_SHIFT 0U +#define TICKS_TIMER0_CYCLES_12MHZ 12U + +#endif /* __RP2350_H */ diff --git a/drivers/0x0d_timer_cbm/Inc/rp2350_delay.h b/drivers/0x0d_timer_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..79db58f --- /dev/null +++ b/drivers/0x0d_timer_cbm/Inc/rp2350_delay.h @@ -0,0 +1,51 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond and microsecond busy-wait delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +/** + * @brief Delay for the specified number of microseconds. + * @param us number of microseconds to delay + * @retval None + */ +void delay_us(uint32_t us); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x0d_timer_cbm/Inc/rp2350_reset.h b/drivers/0x0d_timer_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x0d_timer_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x0d_timer_cbm/Inc/rp2350_reset_handler.h b/drivers/0x0d_timer_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x0d_timer_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x0d_timer_cbm/Inc/rp2350_stack.h b/drivers/0x0d_timer_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x0d_timer_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x0d_timer_cbm/Inc/rp2350_timer.h b/drivers/0x0d_timer_cbm/Inc/rp2350_timer.h new file mode 100644 index 0000000..c920575 --- /dev/null +++ b/drivers/0x0d_timer_cbm/Inc/rp2350_timer.h @@ -0,0 +1,63 @@ +/** + * @file rp2350_timer.h + * @brief TIMER0 alarm driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal TIMER0 driver providing a repeating alarm + * interrupt on alarm 0. The tick generator is configured + * for 1 us resolution from the 12 MHz CLK_REF. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_TIMER_H +#define __RP2350_TIMER_H + +#include "rp2350.h" + +/** + * @brief Callback type for repeating timer alarm. + */ +typedef void (*timer_callback_t)(void); + +/** + * @brief Release TIMER0 from reset and wait until ready. + * @retval None + */ +void timer_release_reset(void); + +/** + * @brief Start the TIMER0 tick generator at 1 us resolution. + * @retval None + */ +void timer_tick_init(void); + +/** + * @brief Start a repeating alarm that fires every period_ms milliseconds. + * @param period_ms interval in milliseconds between callbacks + * @param cb function to call on each alarm + * @retval None + */ +void timer_alarm_start(uint32_t period_ms, timer_callback_t cb); + +#endif /* __RP2350_TIMER_H */ diff --git a/drivers/0x0d_timer_cbm/Inc/rp2350_uart.h b/drivers/0x0d_timer_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x0d_timer_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x0d_timer_cbm/Inc/rp2350_xosc.h b/drivers/0x0d_timer_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..80fd75d --- /dev/null +++ b/drivers/0x0d_timer_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,55 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +/** + * @brief Switch CLK_REF source to XOSC for a stable 12 MHz clk_sys. + * @retval None + */ +void xosc_set_clk_ref(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x0d_timer_cbm/Makefile b/drivers/0x0d_timer_cbm/Makefile new file mode 100644 index 0000000..1a276c4 --- /dev/null +++ b/drivers/0x0d_timer_cbm/Makefile @@ -0,0 +1,85 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C timer alarm driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = timer + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/rp2350_timer.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x0d_timer_cbm/Src/image_def.c b/drivers/0x0d_timer_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x0d_timer_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x0d_timer_cbm/Src/main.c b/drivers/0x0d_timer_cbm/Src/main.c new file mode 100644 index 0000000..529bddc --- /dev/null +++ b/drivers/0x0d_timer_cbm/Src/main.c @@ -0,0 +1,56 @@ +/** + * @file main.c + * @brief Repeating timer alarm demonstration. + * @author Kevin Thomas + * @date 2026 + * + * Configures TIMER0 alarm 0 to fire a callback every 1 second. + * The callback prints "Timer heartbeat" over UART. The main + * loop idles with WFI. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * No external components required + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_timer.h" +#include "rp2350_uart.h" + +/** + * @brief Heartbeat callback invoked by TIMER0 alarm 0 IRQ. + * @retval None + */ +static void heartbeat(void) +{ + uart_puts("Timer heartbeat\r\n"); +} + +int main(void) +{ + uart_puts("Timer alarm demo initialized\r\n"); + timer_alarm_start(1000, heartbeat); + while (1) + __asm__ volatile ("wfi"); +} diff --git a/drivers/0x0d_timer_cbm/Src/rp2350_delay.c b/drivers/0x0d_timer_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..65b0821 --- /dev/null +++ b/drivers/0x0d_timer_cbm/Src/rp2350_delay.c @@ -0,0 +1,64 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond and microsecond delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} + +void delay_us(uint32_t us) +{ + if (us == 0) + return; + __asm__ volatile ( + "mov r4, #4\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (us) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x0d_timer_cbm/Src/rp2350_reset.c b/drivers/0x0d_timer_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x0d_timer_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x0d_timer_cbm/Src/rp2350_reset_handler.c b/drivers/0x0d_timer_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..1e40e17 --- /dev/null +++ b/drivers/0x0d_timer_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,88 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, TIMER0 tick + * generator, then branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" +#include "rp2350_timer.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void _late_init(void) +{ + timer_release_reset(); + timer_tick_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "bl _late_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x0d_timer_cbm/Src/rp2350_stack.c b/drivers/0x0d_timer_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x0d_timer_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x0d_timer_cbm/Src/rp2350_timer.c b/drivers/0x0d_timer_cbm/Src/rp2350_timer.c new file mode 100644 index 0000000..9846e1d --- /dev/null +++ b/drivers/0x0d_timer_cbm/Src/rp2350_timer.c @@ -0,0 +1,140 @@ +/** + * @file rp2350_timer.c + * @brief TIMER0 alarm driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the TIMER0 tick generator for 1 us resolution + * from the 12 MHz CLK_REF, then uses alarm 0 with NVIC IRQ + * to implement a repeating callback at a configurable period. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_timer.h" + +/** + * @brief User callback and period stored for ISR re-arm. + */ +static timer_callback_t user_callback; + +/** + * @brief Alarm period in microseconds for re-arming. + */ +static uint32_t alarm_period_us; + +/** + * @brief Clear the TIMER0 reset bit in the reset controller. + * @retval None + */ +static void timer_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_TIMER0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the TIMER0 block is out of reset. + * @retval None + */ +static void timer_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_TIMER0_SHIFT)) == 0) {} +} + +/** + * @brief Set the TIMER0 tick generator cycle count to 12. + * @retval None + */ +static void timer_set_tick_cycles(void) +{ + TICKS_TIMER0->CYCLES = TICKS_TIMER0_CYCLES_12MHZ; +} + +/** + * @brief Enable the TIMER0 tick generator. + * @retval None + */ +static void timer_enable_tick(void) +{ + TICKS_TIMER0->CTRL = (1U << TICKS_CTRL_ENABLE_SHIFT); +} + +/** + * @brief Enable the alarm 0 interrupt in TIMER0 INTE register. + * @retval None + */ +static void timer_enable_alarm_irq(void) +{ + TIMER0->INTE = (1U << TIMER_INTE_ALARM0_SHIFT); +} + +/** + * @brief Enable TIMER0_IRQ_0 in the NVIC. + * @retval None + */ +static void timer_enable_nvic(void) +{ + *NVIC_ISER0 = (1U << TIMER0_ALARM0_IRQ); +} + +/** + * @brief Arm alarm 0 with the next target time. + * @retval None + */ +static void timer_arm_alarm(void) +{ + uint32_t target; + target = TIMER0->TIMERAWL + alarm_period_us; + TIMER0->ALARM0 = target; +} + +void timer_release_reset(void) +{ + timer_clear_reset_bit(); + timer_wait_reset_done(); +} + +void timer_tick_init(void) +{ + timer_set_tick_cycles(); + timer_enable_tick(); +} + +void timer_alarm_start(uint32_t period_ms, timer_callback_t cb) +{ + user_callback = cb; + alarm_period_us = period_ms * 1000U; + timer_enable_alarm_irq(); + timer_enable_nvic(); + timer_arm_alarm(); +} + +void TIMER0_IRQ_0_Handler(void) +{ + TIMER0->INTR = TIMER_INTR_ALARM0_MASK; + timer_arm_alarm(); + if (user_callback) + user_callback(); +} diff --git a/drivers/0x0d_timer_cbm/Src/rp2350_uart.c b/drivers/0x0d_timer_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x0d_timer_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x0d_timer_cbm/Src/rp2350_xosc.c b/drivers/0x0d_timer_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..b33a71d --- /dev/null +++ b/drivers/0x0d_timer_cbm/Src/rp2350_xosc.c @@ -0,0 +1,54 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value &= ~CLK_PERI_CTRL_AUXSRC_MASK; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} + +void xosc_set_clk_ref(void) +{ + CLOCKS->CLK_REF_CTRL = CLK_REF_CTRL_SRC_XOSC; +} diff --git a/drivers/0x0d_timer_cbm/Src/vector_table.c b/drivers/0x0d_timer_cbm/Src/vector_table.c new file mode 100644 index 0000000..954ff28 --- /dev/null +++ b/drivers/0x0d_timer_cbm/Src/vector_table.c @@ -0,0 +1,71 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer, reset handler, and + * @author Kevin Thomas + * @date 2026 + * + * TIMER0 alarm 0 IRQ handler. + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); +extern void TIMER0_IRQ_0_Handler(void); + +/** + * @brief Default handler for unused exceptions (infinite loop). + * @retval None + */ +static void default_handler(void) +{ + while (1) {} +} + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[17] = { + &_stack_top, // 0: Initial stack pointer + Reset_Handler, // 1: Reset + default_handler, // 2: NMI + default_handler, // 3: HardFault + default_handler, // 4: MemManage + default_handler, // 5: BusFault + default_handler, // 6: UsageFault + default_handler, // 7: SecureFault + 0, // 8: Reserved + 0, // 9: Reserved + 0, // 10: Reserved + default_handler, // 11: SVCall + default_handler, // 12: DebugMon + 0, // 13: Reserved + default_handler, // 14: PendSV + default_handler, // 15: SysTick + TIMER0_IRQ_0_Handler, // 16: IRQ 0 — TIMER0_IRQ_0 +}; diff --git a/drivers/0x0d_timer_cbm/build.log b/drivers/0x0d_timer_cbm/build.log new file mode 100644 index 0000000..1321343 Binary files /dev/null and b/drivers/0x0d_timer_cbm/build.log differ diff --git a/drivers/0x0d_timer_cbm/linker.ld b/drivers/0x0d_timer_cbm/linker.ld new file mode 100644 index 0000000..56ac361 --- /dev/null +++ b/drivers/0x0d_timer_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x0d_timer_rust/.cargo/config.toml b/drivers/0x0d_timer_rust/.cargo/config.toml new file mode 100644 index 0000000..70fb5fa --- /dev/null +++ b/drivers/0x0d_timer_rust/.cargo/config.toml @@ -0,0 +1,32 @@ +[build] +target = "thumbv8m.main-none-eabihf" + +[target.thumbv6m-none-eabi] +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.thumbv8m.main-none-eabihf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.riscv32imac-unknown-none-elf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x0d_timer_rust/.pico-rs b/drivers/0x0d_timer_rust/.pico-rs new file mode 100644 index 0000000..be06904 --- /dev/null +++ b/drivers/0x0d_timer_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 diff --git a/drivers/0x0d_timer_rust/.vscode/extensions.json b/drivers/0x0d_timer_rust/.vscode/extensions.json new file mode 100644 index 0000000..107cc34 --- /dev/null +++ b/drivers/0x0d_timer_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} diff --git a/drivers/0x0d_timer_rust/.vscode/launch.json b/drivers/0x0d_timer_rust/.vscode/launch.json new file mode 100644 index 0000000..34833c2 --- /dev/null +++ b/drivers/0x0d_timer_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} diff --git a/drivers/0x0d_timer_rust/.vscode/settings.json b/drivers/0x0d_timer_rust/.vscode/settings.json new file mode 100644 index 0000000..3744e1f --- /dev/null +++ b/drivers/0x0d_timer_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} diff --git a/drivers/0x0d_timer_rust/.vscode/tasks.json b/drivers/0x0d_timer_rust/.vscode/tasks.json new file mode 100644 index 0000000..d288f27 --- /dev/null +++ b/drivers/0x0d_timer_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} diff --git a/drivers/0x0d_timer_rust/Cargo.lock b/drivers/0x0d_timer_rust/Cargo.lock new file mode 100644 index 0000000..f2bef09 --- /dev/null +++ b/drivers/0x0d_timer_rust/Cargo.lock @@ -0,0 +1,749 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "crc-any" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "frunk" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28aef0f9aa070bce60767c12ba9cb41efeaf1a2bc6427f87b7d83f11239a16d7" +dependencies = [ + "frunk_core", + "frunk_derives", +] + +[[package]] +name = "frunk_core" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476eeaa382e3462b84da5d6ba3da97b5786823c2d0d3a0d04ef088d073da225c" + +[[package]] +name = "frunk_derives" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b4095fc99e1d858e5b8c7125d2638372ec85aa0fe6c807105cf10b0265ca6c" +dependencies = [ + "frunk_proc_macro_helpers", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "frunk_proc_macro_helpers" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1952b802269f2db12ab7c0bd328d0ae8feaabf19f352a7b0af7bb0c5693abfce" +dependencies = [ + "frunk_core", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fugit" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "panic-halt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" + +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "riscv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", +] + +[[package]] +name = "riscv-rt" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d35e32cf1383183e8885d8a9aa4402a087fd094dc34c2cb6df6687d0229dfe" +dependencies = [ + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "riscv-rt-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp-binary-info" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f582261945fa215d40e2988b4df595d11c0908c0fff97a0fe23df766d117b790" + +[[package]] +name = "rp-hal-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288358786b1458fb2caac8c4b40fb529ef4200d6c46467e2695b7a8ba573ae8" +dependencies = [ + "fugit", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rp2040-hal" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb79a4590775204387f334672e6f79c0d734d0a159da23d60677b3c10fa1245" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "itertools 0.10.5", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "rp-binary-info", + "rp-hal-common", + "rp2040-hal-macros", + "rp2040-pac", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp2040-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e" +dependencies = [ + "cortex-m-rt", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rp2040-pac" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cbcd3f7a0ca7bbe61dc4eb7e202842bee4e27b769a7bf3a4a72fa399d6e404" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rp235x-hal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2939c82776b0b4ae110168b4298b5adf831e6cff249b057bf2a2187453b959c" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "cortex-m-rt", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "gcd", + "itertools 0.13.0", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "riscv", + "riscv-rt", + "rp-binary-info", + "rp-hal-common", + "rp235x-hal-macros", + "rp235x-pac", + "sha2-const-stable", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp235x-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74edd7a5979e9763bbb98e9746e711bac7464ee3397af7288e6c288ff0d3c764" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp235x-pac" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffcb6931deee4242886b5a1df62db5e2555b0eb6ae1e8be101f3ea3e58e65c6" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "timer" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "fugit", + "panic-halt", + "panic-probe", + "regex", + "rp2040-boot2", + "rp2040-hal", + "rp235x-hal", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless", + "portable-atomic", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/drivers/0x0d_timer_rust/Cargo.toml b/drivers/0x0d_timer_rust/Cargo.toml new file mode 100644 index 0000000..302d398 --- /dev/null +++ b/drivers/0x0d_timer_rust/Cargo.toml @@ -0,0 +1,39 @@ +[package] +edition = "2024" +name = "timer" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "timer_lib" +path = "src/lib.rs" + +[[bin]] +name = "timer" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x0d_timer_rust/build.rs b/drivers/0x0d_timer_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x0d_timer_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x0d_timer_rust/rp2040.x b/drivers/0x0d_timer_rust/rp2040.x new file mode 100644 index 0000000..6e1a654 --- /dev/null +++ b/drivers/0x0d_timer_rust/rp2040.x @@ -0,0 +1,44 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; \ No newline at end of file diff --git a/drivers/0x0d_timer_rust/rp2350.x b/drivers/0x0d_timer_rust/rp2350.x new file mode 100644 index 0000000..570f72c --- /dev/null +++ b/drivers/0x0d_timer_rust/rp2350.x @@ -0,0 +1,47 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); diff --git a/drivers/0x0d_timer_rust/rp2350_riscv.x b/drivers/0x0d_timer_rust/rp2350_riscv.x new file mode 100644 index 0000000..2c9b445 --- /dev/null +++ b/drivers/0x0d_timer_rust/rp2350_riscv.x @@ -0,0 +1,58 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +PROVIDE(__pre_init = default_pre_init); +PROVIDE(_setup_interrupts = default_setup_interrupts); +PROVIDE(_mp_hook = default_mp_hook); +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + . = ABSOLUTE(_stext); + } > FLASH +} diff --git a/drivers/0x0d_timer_rust/src/board.rs b/drivers/0x0d_timer_rust/src/board.rs new file mode 100644 index 0000000..9c66f75 --- /dev/null +++ b/drivers/0x0d_timer_rust/src/board.rs @@ -0,0 +1,331 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Timer driver pure-logic functions and constants +use timer_lib::timer; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Timer device type for the HAL timer peripheral. +#[cfg(rp2350)] +pub(crate) type HalTimer = hal::Timer; +/// Timer type alias for RP2040 (non-generic). +#[cfg(rp2040)] +pub(crate) type HalTimer = hal::Timer; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Run the repeating timer heartbeat loop. +/// +/// Polls the HAL timer, and each time the configured period elapses, +/// fires the driver state callback and prints the heartbeat message +/// over UART. This mirrors the C demo's `_heartbeat_callback` being +/// invoked by the Pico SDK repeating timer. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `timer` - Reference to the HAL timer for microsecond measurement. +/// * `delay` - Mutable reference to the blocking delay provider. +/// * `state` - Mutable reference to the timer driver state. +pub(crate) fn heartbeat_loop( + uart: &EnabledUart, + timer: &HalTimer, + delay: &mut cortex_m::delay::Delay, + state: &mut timer::TimerDriverState, +) -> ! { + let mut last_us = timer.get_counter().ticks() as u32; + let period_us = state.period_ms() as u64 * 1_000; + loop { + let (now, elapsed) = tick_elapsed(timer, last_us); + if elapsed >= period_us { + last_us = now; + fire_heartbeat(uart, state); + } + delay.delay_us(100); + } +} + +/// Compute elapsed microseconds since last checkpoint. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// * `last_us` - The `last_us` parameter. +/// +/// # Returns +/// +/// A value of type `(u32, u64)`. +/// +/// # Arguments +/// +/// * `timer` - The `timer` parameter. +/// * `last_us` - The `last_us` parameter. +/// +/// # Returns +/// +/// A value of type `(u32, u64)`. +fn tick_elapsed(timer: &HalTimer, last_us: u32) -> (u32, u64) { + let now_us = timer.get_counter().ticks() as u32; + (now_us, now_us.wrapping_sub(last_us) as u64) +} + +/// Fire the heartbeat callback and print the message over UART. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `state` - The state to set. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `state` - The state to set. +fn fire_heartbeat(uart: &EnabledUart, state: &mut timer::TimerDriverState) { + if state.on_fire() { + let mut buf = [0u8; 32]; + let n = timer::format_heartbeat(&mut buf); + uart.write_full_blocking(&buf[..n]); + } +} + +/// Initialise all peripherals and run the repeating timer demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + let mut delay = init_delay(&clocks); + #[cfg(rp2350)] + let timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks); + #[cfg(rp2040)] + let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS); + let mut state = start_timer(&uart); + heartbeat_loop(&uart, &timer, &mut delay, &mut state) +} + +/// Create the timer driver state, start it, and report over UART. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// +/// # Returns +/// +/// Initialised timer driver state. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// +/// # Returns +/// +/// A value of type `timer::TimerDriverState`. +fn start_timer(uart: &EnabledUart) -> timer::TimerDriverState { + let mut state = timer::TimerDriverState::new(); + state.start(timer::DEFAULT_PERIOD_MS); + let mut buf = [0u8; 64]; + let n = timer::format_started(&mut buf, timer::DEFAULT_PERIOD_MS); + uart.write_full_blocking(&buf[..n]); + state +} + diff --git a/drivers/0x0d_timer_rust/src/lib.rs b/drivers/0x0d_timer_rust/src/lib.rs new file mode 100644 index 0000000..5921ebd --- /dev/null +++ b/drivers/0x0d_timer_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod timer; diff --git a/drivers/0x0d_timer_rust/src/main.rs b/drivers/0x0d_timer_rust/src/main.rs new file mode 100644 index 0000000..b80c2ef --- /dev/null +++ b/drivers/0x0d_timer_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// Timer driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the repeating timer demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"Repeating Timer Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x0d_timer_rust/src/timer.rs b/drivers/0x0d_timer_rust/src/timer.rs new file mode 100644 index 0000000..908c6fc --- /dev/null +++ b/drivers/0x0d_timer_rust/src/timer.rs @@ -0,0 +1,416 @@ +//! Implementation module +//! +//! **File:** `timer.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// Default heartbeat message printed by the timer callback. +pub const HEARTBEAT_MSG: &[u8] = b"Timer heartbeat\r\n"; + +/// Default timer period in milliseconds. +pub const DEFAULT_PERIOD_MS: u32 = 1_000; + +/// Timer driver state tracking whether the repeating timer is active. +/// +/// Mirrors the C driver's `g_timer_active` / `g_user_callback` globals +/// as a structured state machine testable on the host. +pub struct TimerDriverState { + /// Whether the repeating timer is currently active. + active: bool, + /// Configured period in milliseconds. + period_ms: u32, + /// Number of times the callback has fired. + fire_count: u32, +} + +impl TimerDriverState { + /// Create a new idle timer driver state. + /// + /// # Returns + /// + /// A `TimerDriverState` in the inactive state with zero fire count. + /// + /// # Returns + /// + /// A new instance of the struct. + pub fn new() -> Self { + Self { + active: false, + period_ms: 0, + fire_count: 0, + } + } + + /// Start the repeating timer with the given period. + /// + /// If the timer is already active it is first cancelled, matching + /// the C driver's `timer_driver_start()` behaviour. + /// + /// # Arguments + /// + /// * `period_ms` - Interval between callbacks in milliseconds. + /// + /// # Arguments + /// + /// * `period_ms` - The `period_ms` parameter. + pub fn start(&mut self, period_ms: u32) { + if self.active { + self.cancel(); + } + self.period_ms = period_ms; + self.active = true; + } + + /// Cancel the active repeating timer. + /// + /// Safe to call even if the timer is already inactive, matching + /// the C driver's `timer_driver_cancel()` behaviour. + pub fn cancel(&mut self) { + self.active = false; + } + + /// Return whether the timer is currently active. + /// + /// # Returns + /// + /// `true` if a repeating timer is running, `false` otherwise. + /// + /// # Returns + /// + /// `true` if successful or set, `false` otherwise. + pub fn is_active(&self) -> bool { + self.active + } + + /// Return the configured timer period in milliseconds. + /// + /// # Returns + /// + /// The period set by the most recent `start()` call. + /// + /// # Returns + /// + /// A 32-bit unsigned integer value. + pub fn period_ms(&self) -> u32 { + self.period_ms + } + + /// Return the total number of times the callback has fired. + /// + /// # Returns + /// + /// Cumulative fire count since construction. + /// + /// # Returns + /// + /// A 32-bit unsigned integer value. + pub fn fire_count(&self) -> u32 { + self.fire_count + } + + /// Record that the callback has fired once. + /// + /// Called by the board-level shim each time the hardware alarm + /// triggers. Returns `true` to keep the timer repeating (matching + /// the C `_heartbeat_callback` return value). + /// + /// # Returns + /// + /// `true` if the timer should continue repeating. + /// + /// # Returns + /// + /// `true` if successful or set, `false` otherwise. + pub fn on_fire(&mut self) -> bool { + if !self.active { + return false; + } + self.fire_count += 1; + true + } +} + +/// Format the heartbeat message into a caller-supplied buffer. +/// +/// Writes the fixed `HEARTBEAT_MSG` bytes and returns the number +/// of bytes written. +/// +/// # Arguments +/// +/// * `buf` - Destination buffer (must be at least `HEARTBEAT_MSG.len()` bytes). +/// +/// # Returns +/// +/// Number of bytes written to `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_heartbeat(buf: &mut [u8]) -> usize { + let len = HEARTBEAT_MSG.len(); + buf[..len].copy_from_slice(HEARTBEAT_MSG); + len +} + +/// Format a timer-started banner message. +/// +/// Writes "Repeating timer started (XXXX ms)\r\n" into `buf` and +/// returns the number of bytes written. +/// +/// # Arguments +/// +/// * `buf` - Destination buffer (must be at least 40 bytes). +/// * `period_ms` - Timer period to include in the message. +/// +/// # Returns +/// +/// Number of bytes written to `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `period_ms` - The `period_ms` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_started(buf: &mut [u8], period_ms: u32) -> usize { + let prefix = b"Repeating timer started ("; + let mut pos = prefix.len(); + buf[..pos].copy_from_slice(prefix); + pos += format_u32(&mut buf[pos..], period_ms); + let suffix = b" ms)\r\n"; + buf[pos..pos + suffix.len()].copy_from_slice(suffix); + pos + suffix.len() +} + +/// Format an unsigned 32-bit integer as decimal ASCII. +/// +/// # Arguments +/// +/// * `buf` - Destination buffer. +/// * `value` - Value to format. +/// +/// # Returns +/// +/// Number of ASCII digits written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_u32(buf: &mut [u8], value: u32) -> usize { + if value == 0 { + buf[0] = b'0'; + return 1; + } + let mut tmp = [0u8; 10]; + let n = u32_to_digits_reversed(&mut tmp, value); + reverse_copy(buf, &tmp, n); + n +} + +/// Convert a u32 to reversed decimal digits in a temporary buffer. +/// +/// # Arguments +/// +/// * `tmp` - The `tmp` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `tmp` - The `tmp` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn u32_to_digits_reversed(tmp: &mut [u8; 10], mut value: u32) -> usize { + let mut n = 0usize; + while value > 0 { + tmp[n] = b'0' + (value % 10) as u8; + value /= 10; + n += 1; + } + n +} + +/// Copy digits from a reversed temporary buffer into the output buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `tmp` - The `tmp` parameter. +/// * `n` - Nibble or number. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `tmp` - The `tmp` parameter. +/// * `n` - Nibble or number. +fn reverse_copy(buf: &mut [u8], tmp: &[u8], n: usize) { + for i in 0..n { + buf[i] = tmp[n - 1 - i]; + } +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the new state is inactive operation. + #[test] + fn new_state_is_inactive() { + let state = TimerDriverState::new(); + assert!(!state.is_active()); + assert_eq!(state.period_ms(), 0); + assert_eq!(state.fire_count(), 0); + } + + /// Executes the start activates timer operation. + #[test] + fn start_activates_timer() { + let mut state = TimerDriverState::new(); + state.start(1_000); + assert!(state.is_active()); + assert_eq!(state.period_ms(), 1_000); + } + + /// Executes the cancel deactivates timer operation. + #[test] + fn cancel_deactivates_timer() { + let mut state = TimerDriverState::new(); + state.start(1_000); + state.cancel(); + assert!(!state.is_active()); + } + + /// Executes the cancel when inactive is safe operation. + #[test] + fn cancel_when_inactive_is_safe() { + let mut state = TimerDriverState::new(); + state.cancel(); + assert!(!state.is_active()); + } + + /// Executes the start while active cancels and restarts operation. + #[test] + fn start_while_active_cancels_and_restarts() { + let mut state = TimerDriverState::new(); + state.start(500); + state.start(2_000); + assert!(state.is_active()); + assert_eq!(state.period_ms(), 2_000); + } + + /// Executes the on fire increments count operation. + #[test] + fn on_fire_increments_count() { + let mut state = TimerDriverState::new(); + state.start(1_000); + assert!(state.on_fire()); + assert_eq!(state.fire_count(), 1); + assert!(state.on_fire()); + assert_eq!(state.fire_count(), 2); + } + + /// Executes the on fire returns false when inactive operation. + #[test] + fn on_fire_returns_false_when_inactive() { + let mut state = TimerDriverState::new(); + assert!(!state.on_fire()); + assert_eq!(state.fire_count(), 0); + } + + /// Executes the on fire after cancel returns false operation. + #[test] + fn on_fire_after_cancel_returns_false() { + let mut state = TimerDriverState::new(); + state.start(1_000); + assert!(state.on_fire()); + state.cancel(); + assert!(!state.on_fire()); + assert_eq!(state.fire_count(), 1); + } + + /// Executes the format heartbeat matches c output operation. + #[test] + fn format_heartbeat_matches_c_output() { + let mut buf = [0u8; 32]; + let n = format_heartbeat(&mut buf); + assert_eq!(&buf[..n], b"Timer heartbeat\r\n"); + } + + /// Executes the format started 1000ms operation. + #[test] + fn format_started_1000ms() { + let mut buf = [0u8; 48]; + let n = format_started(&mut buf, 1_000); + assert_eq!(&buf[..n], b"Repeating timer started (1000 ms)\r\n"); + } + + /// Executes the format started single digit operation. + #[test] + fn format_started_single_digit() { + let mut buf = [0u8; 48]; + let n = format_started(&mut buf, 5); + assert_eq!(&buf[..n], b"Repeating timer started (5 ms)\r\n"); + } + + /// Executes the format u32 zero operation. + #[test] + fn format_u32_zero() { + let mut buf = [0u8; 10]; + let n = format_u32(&mut buf, 0); + assert_eq!(&buf[..n], b"0"); + } + + /// Executes the format u32 large value operation. + #[test] + fn format_u32_large_value() { + let mut buf = [0u8; 10]; + let n = format_u32(&mut buf, 123456); + assert_eq!(&buf[..n], b"123456"); + } + + /// Executes the default period matches c demo operation. + #[test] + fn default_period_matches_c_demo() { + assert_eq!(DEFAULT_PERIOD_MS, 1_000); + } +} diff --git a/drivers/0x0e_watchdog/.vscode/.vscode/c_cpp_properties.json b/drivers/0x0e_watchdog/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0e_watchdog/.vscode/.vscode/cmake-kits.json b/drivers/0x0e_watchdog/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0e_watchdog/.vscode/.vscode/extensions.json b/drivers/0x0e_watchdog/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0e_watchdog/.vscode/.vscode/launch.json b/drivers/0x0e_watchdog/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0e_watchdog/.vscode/.vscode/settings.json b/drivers/0x0e_watchdog/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0e_watchdog/.vscode/.vscode/tasks.json b/drivers/0x0e_watchdog/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0e_watchdog/.vscode/c_cpp_properties.json b/drivers/0x0e_watchdog/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0e_watchdog/.vscode/cmake-kits.json b/drivers/0x0e_watchdog/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0e_watchdog/.vscode/extensions.json b/drivers/0x0e_watchdog/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0e_watchdog/.vscode/launch.json b/drivers/0x0e_watchdog/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0e_watchdog/.vscode/settings.json b/drivers/0x0e_watchdog/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0e_watchdog/.vscode/tasks.json b/drivers/0x0e_watchdog/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0e_watchdog/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0e_watchdog/0x0e_watchdog.c b/drivers/0x0e_watchdog/0x0e_watchdog.c new file mode 100644 index 0000000..982df2c --- /dev/null +++ b/drivers/0x0e_watchdog/0x0e_watchdog.c @@ -0,0 +1,70 @@ +/** + * @file 0x0e_watchdog.c + * @brief Watchdog feed demo using watchdog.c/watchdog.h + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates the hardware watchdog using the watchdog driver + * (watchdog.h / watchdog.c). The watchdog is enabled with a 3-second + * timeout and fed every second. If the feed loop were removed, the + * chip would automatically reboot after 3 seconds. + * + * Wiring: + * No external wiring required + */ + +#include +#include "pico/stdlib.h" +#include "watchdog.h" + +/** + * @brief Print whether the system booted normally or from a watchdog reset + */ +static void print_reset_reason(void) { + if (watchdog_driver_caused_reboot()) + printf("System rebooted by watchdog timeout\r\n"); + else + printf("Normal power-on reset\r\n"); +} + +/** + * @brief Feed the watchdog and log over UART, then wait 1 second + */ +static void feed_and_report(void) { + watchdog_driver_feed(); + printf("Watchdog fed\r\n"); + sleep_ms(1000); +} + +int main(void) { + stdio_init_all(); + print_reset_reason(); + watchdog_driver_enable(3000); + printf("Watchdog enabled (3s timeout). Feeding every 1s...\r\n"); + while (true) + feed_and_report(); +} diff --git a/drivers/0x0e_watchdog/CMakeLists.txt b/drivers/0x0e_watchdog/CMakeLists.txt new file mode 100644 index 0000000..a4a5159 --- /dev/null +++ b/drivers/0x0e_watchdog/CMakeLists.txt @@ -0,0 +1,57 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0e_watchdog C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0e_watchdog 0x0e_watchdog.c watchdog.c) + +pico_set_program_name(0x0e_watchdog "0x0e_watchdog") +pico_set_program_version(0x0e_watchdog "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0e_watchdog 1) +pico_enable_stdio_usb(0x0e_watchdog 0) + +# Add the standard library to the build +target_link_libraries(0x0e_watchdog + pico_stdlib + hardware_watchdog) + +# Add the standard include files to the build +target_include_directories(0x0e_watchdog PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0e_watchdog) diff --git a/drivers/0x0e_watchdog/pico_sdk_import.cmake b/drivers/0x0e_watchdog/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x0e_watchdog/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x0e_watchdog/watchdog.c b/drivers/0x0e_watchdog/watchdog.c new file mode 100644 index 0000000..fb677de --- /dev/null +++ b/drivers/0x0e_watchdog/watchdog.c @@ -0,0 +1,43 @@ +/** + * @file watchdog.c + * @brief Implementation of hardware watchdog driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "watchdog.h" +#include "hardware/watchdog.h" + +void watchdog_driver_enable(uint32_t timeout_ms) { + watchdog_enable(timeout_ms, true); +} + +void watchdog_driver_feed(void) { + watchdog_update(); +} + +bool watchdog_driver_caused_reboot(void) { + return watchdog_caused_reboot(); +} diff --git a/drivers/0x0e_watchdog/watchdog.h b/drivers/0x0e_watchdog/watchdog.h new file mode 100644 index 0000000..3ff42cf --- /dev/null +++ b/drivers/0x0e_watchdog/watchdog.h @@ -0,0 +1,65 @@ +/** + * @file watchdog.h + * @brief Header for hardware watchdog timer driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WATCHDOG_H +#define WATCHDOG_H + +#include +#include + +/** + * @brief Enable the hardware watchdog with the specified timeout + * + * Starts the watchdog timer. If watchdog_driver_feed() is not called within + * @p timeout_ms milliseconds, the RP2350 will perform a hard reset. + * The maximum supported timeout is 8388 ms. + * + * @param timeout_ms Watchdog timeout in milliseconds (1 - 8388) + */ +void watchdog_driver_enable(uint32_t timeout_ms); + +/** + * @brief Reset ("feed") the watchdog timer to prevent a reboot + * + * Must be called periodically within the timeout window configured by + * watchdog_driver_enable(). Each call restarts the countdown. + */ +void watchdog_driver_feed(void); + +/** + * @brief Check whether the last reset was caused by the watchdog + * + * Reads the reset reason register to determine if the watchdog timer + * expired and forced the most recent reboot. + * + * @return bool true if the watchdog triggered the last reset, false otherwise + */ +bool watchdog_driver_caused_reboot(void); + +#endif // WATCHDOG_H diff --git a/drivers/0x0e_watchdog_cbm/Inc/rp2350.h b/drivers/0x0e_watchdog_cbm/Inc/rp2350.h new file mode 100644 index 0000000..bdba3e7 --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Inc/rp2350.h @@ -0,0 +1,288 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define PSM_BASE 0x40018000UL +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define WATCHDOG_BASE 0x400D8000UL +#define TICKS_BASE 0x40108000UL + +/*!< Atomic register alias offsets */ +#define ATOMIC_SET_OFFSET 0x2000UL +#define ATOMIC_CLR_OFFSET 0x3000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[12]; // GPOUT0..GPOUT3 registers Address offset: 0x00-0x2C + __IO uint32_t CLK_REF_CTRL; // Reference clock control Address offset: 0x30 + __IO uint32_t RESERVED1[5]; // CLK_REF_DIV..CLK_SYS_SELECTED Address offset: 0x34-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief PSM (Power-on State Machine) + */ +typedef struct +{ + __IO uint32_t FRCE_ON; // Force block out of reset Address offset: 0x00 + __IO uint32_t FRCE_OFF; // Force block into reset Address offset: 0x04 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x08 + __IO uint32_t DONE; // Subsystem ready status Address offset: 0x0C +} PSM_TypeDef; + +/** + * @brief WATCHDOG + */ +typedef struct +{ + __IO uint32_t CTRL; // Watchdog control Address offset: 0x00 + __IO uint32_t LOAD; // Load the watchdog timer (bits 23:0) Address offset: 0x04 + __IO uint32_t REASON; // Reason for last reset Address offset: 0x08 + __IO uint32_t SCRATCH[8]; // Scratch registers 0-7 Address offset: 0x0C-0x28 +} WATCHDOG_TypeDef; + +/** + * @brief TICKS (Tick Generator entry, one per source) + */ +typedef struct +{ + __IO uint32_t CTRL; // Enable tick generator (bit 0) Address offset: 0x00 + __IO uint32_t CYCLES; // Number of CLK_REF cycles per tick (bits 8:0) Address offset: 0x04 + __IO uint32_t COUNT; // Running counter of ticks (read-only) Address offset: 0x08 +} TICKS_Entry_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define PSM ((PSM_TypeDef *) PSM_BASE) +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) +#define WATCHDOG ((WATCHDOG_TypeDef *) WATCHDOG_BASE) + +/** + * @brief TICKS entries (indexed from TICKS_BASE, 12-byte stride) + */ +#define TICKS_WATCHDOG ((TICKS_Entry_TypeDef *) (TICKS_BASE + 0x00UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLK_REF bit definitions + */ +#define CLK_REF_CTRL_SRC_XOSC 2U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_MASK (0x07U << CLK_PERI_CTRL_AUXSRC_SHIFT) +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_PADS_BANK0_SHIFT 9U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U +#define IO_BANK0_CTRL_FUNCSEL_NULL 0x1FU + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U +#define PADS_BANK0_PUE_SHIFT 3U +#define PADS_BANK0_PDE_SHIFT 2U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief PSM bit definitions + */ +#define PSM_WDSEL_ALL_MASK 0x01FFFFFFUL +#define PSM_WDSEL_ROSC_SHIFT 2U +#define PSM_WDSEL_XOSC_SHIFT 3U + +/** + * @brief WATCHDOG CTRL bit definitions + */ +#define WATCHDOG_CTRL_TRIGGER_SHIFT 31U +#define WATCHDOG_CTRL_ENABLE_SHIFT 30U +#define WATCHDOG_CTRL_PAUSE_DBG1_SHIFT 26U +#define WATCHDOG_CTRL_PAUSE_DBG0_SHIFT 25U +#define WATCHDOG_CTRL_PAUSE_JTAG_SHIFT 24U +#define WATCHDOG_CTRL_TIME_MASK 0x00FFFFFFUL + +/** + * @brief WATCHDOG LOAD bit definitions + */ +#define WATCHDOG_LOAD_MAX 0x00FFFFFFUL + +/** + * @brief WATCHDOG REASON bit definitions + */ +#define WATCHDOG_REASON_FORCE_SHIFT 1U +#define WATCHDOG_REASON_TIMER_SHIFT 0U + +/** + * @brief TICKS bit definitions + */ +#define TICKS_CTRL_ENABLE_SHIFT 0U +#define TICKS_CYCLES_12MHZ 12U + +#endif /* __RP2350_H */ diff --git a/drivers/0x0e_watchdog_cbm/Inc/rp2350_delay.h b/drivers/0x0e_watchdog_cbm/Inc/rp2350_delay.h new file mode 100644 index 0000000..79db58f --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Inc/rp2350_delay.h @@ -0,0 +1,51 @@ +/** + * @file rp2350_delay.h + * @brief Delay driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Millisecond and microsecond busy-wait delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_DELAY_H +#define __RP2350_DELAY_H + +#include "rp2350.h" + +/** + * @brief Delay for the specified number of milliseconds. + * @param ms number of milliseconds to delay + * @retval None + */ +void delay_ms(uint32_t ms); + +/** + * @brief Delay for the specified number of microseconds. + * @param us number of microseconds to delay + * @retval None + */ +void delay_us(uint32_t us); + +#endif /* __RP2350_DELAY_H */ diff --git a/drivers/0x0e_watchdog_cbm/Inc/rp2350_reset.h b/drivers/0x0e_watchdog_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x0e_watchdog_cbm/Inc/rp2350_reset_handler.h b/drivers/0x0e_watchdog_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x0e_watchdog_cbm/Inc/rp2350_stack.h b/drivers/0x0e_watchdog_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x0e_watchdog_cbm/Inc/rp2350_uart.h b/drivers/0x0e_watchdog_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x0e_watchdog_cbm/Inc/rp2350_watchdog.h b/drivers/0x0e_watchdog_cbm/Inc/rp2350_watchdog.h new file mode 100644 index 0000000..ece89b9 --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Inc/rp2350_watchdog.h @@ -0,0 +1,63 @@ +/** + * @file rp2350_watchdog.h + * @brief Watchdog timer driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal watchdog driver providing enable, feed, and + * reboot-cause detection. The watchdog tick generator is + * configured for 1 us resolution from the 12 MHz CLK_REF. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_WATCHDOG_H +#define __RP2350_WATCHDOG_H + +#include "rp2350.h" + +/** + * @brief Start the watchdog tick generator at 1 us resolution. + * @retval None + */ +void watchdog_tick_init(void); + +/** + * @brief Enable the watchdog with the specified timeout. + * @param timeout_ms watchdog timeout in milliseconds (1-16777) + * @retval None + */ +void watchdog_enable(uint32_t timeout_ms); + +/** + * @brief Feed the watchdog to prevent a reset. + * @retval None + */ +void watchdog_feed(void); + +/** + * @brief Check whether the last reset was caused by the watchdog. + * @retval bool true if the watchdog triggered the last reset + */ +bool watchdog_caused_reboot(void); + +#endif /* __RP2350_WATCHDOG_H */ diff --git a/drivers/0x0e_watchdog_cbm/Inc/rp2350_xosc.h b/drivers/0x0e_watchdog_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..80fd75d --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,55 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +/** + * @brief Switch CLK_REF source to XOSC for a stable 12 MHz clk_sys. + * @retval None + */ +void xosc_set_clk_ref(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x0e_watchdog_cbm/Makefile b/drivers/0x0e_watchdog_cbm/Makefile new file mode 100644 index 0000000..dc822c7 --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Makefile @@ -0,0 +1,85 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C watchdog driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = watchdog + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_delay.c \ + $(SRC_DIR)/rp2350_watchdog.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x0e_watchdog_cbm/Src/image_def.c b/drivers/0x0e_watchdog_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x0e_watchdog_cbm/Src/main.c b/drivers/0x0e_watchdog_cbm/Src/main.c new file mode 100644 index 0000000..3ba9e9a --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Src/main.c @@ -0,0 +1,72 @@ +/** + * @file main.c + * @brief Watchdog feed demonstration. + * @author Kevin Thomas + * @date 2026 + * + * Enables the watchdog with a 3-second timeout and feeds it + * every 1 second. Reports whether the system booted from a + * watchdog reset or a normal power-on. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * No external components required + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_watchdog.h" +#include "rp2350_uart.h" +#include "rp2350_delay.h" + +/** + * @brief Print the reset reason over UART. + * @retval None + */ +static void print_reset_reason(void) +{ + if (watchdog_caused_reboot()) + uart_puts("System rebooted by watchdog timeout\r\n"); + else + uart_puts("Normal power-on reset\r\n"); +} + +/** + * @brief Feed the watchdog, report over UART, and delay 1 second. + * @retval None + */ +static void feed_and_report(void) +{ + watchdog_feed(); + uart_puts("Watchdog fed\r\n"); + delay_ms(1000); +} + +int main(void) +{ + print_reset_reason(); + watchdog_enable(3000); + uart_puts("Watchdog enabled (3s timeout). Feeding every 1s...\r\n"); + while (1) + feed_and_report(); +} diff --git a/drivers/0x0e_watchdog_cbm/Src/rp2350_delay.c b/drivers/0x0e_watchdog_cbm/Src/rp2350_delay.c new file mode 100644 index 0000000..65b0821 --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Src/rp2350_delay.c @@ -0,0 +1,64 @@ +/** + * @file rp2350_delay.c + * @brief Delay driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Busy-wait millisecond and microsecond delays calibrated for + * a 12 MHz clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_delay.h" + +void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + __asm__ volatile ( + "mov r4, #3600\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (ms) + : "r4", "r5", "cc" + ); +} + +void delay_us(uint32_t us) +{ + if (us == 0) + return; + __asm__ volatile ( + "mov r4, #4\n\t" + "mul r5, %0, r4\n\t" + "1:\n\t" + "subs r5, #1\n\t" + "bne 1b\n\t" + : + : "r" (us) + : "r4", "r5", "cc" + ); +} diff --git a/drivers/0x0e_watchdog_cbm/Src/rp2350_reset.c b/drivers/0x0e_watchdog_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x0e_watchdog_cbm/Src/rp2350_reset_handler.c b/drivers/0x0e_watchdog_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..6c08527 --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,82 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Initializes the + * stack, XOSC, subsystem resets, UART, watchdog tick + * generator, then branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" +#include "rp2350_watchdog.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "bl watchdog_tick_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x0e_watchdog_cbm/Src/rp2350_stack.c b/drivers/0x0e_watchdog_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x0e_watchdog_cbm/Src/rp2350_uart.c b/drivers/0x0e_watchdog_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x0e_watchdog_cbm/Src/rp2350_watchdog.c b/drivers/0x0e_watchdog_cbm/Src/rp2350_watchdog.c new file mode 100644 index 0000000..6bca077 --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Src/rp2350_watchdog.c @@ -0,0 +1,126 @@ +/** + * @file rp2350_watchdog.c + * @brief Watchdog timer driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the watchdog tick generator for 1 us resolution + * from the 12 MHz CLK_REF, enables the watchdog countdown, + * and provides feed and reboot-cause detection. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_watchdog.h" + +/** + * @brief Saved load value for feeding the watchdog. + */ +static uint32_t load_value; + +/** + * @brief Set the watchdog tick generator cycle count to 12. + * @retval None + */ +static void watchdog_set_tick_cycles(void) +{ + TICKS_WATCHDOG->CYCLES = TICKS_CYCLES_12MHZ; +} + +/** + * @brief Enable the watchdog tick generator. + * @retval None + */ +static void watchdog_enable_tick(void) +{ + TICKS_WATCHDOG->CTRL = (1U << TICKS_CTRL_ENABLE_SHIFT); +} + +/** + * @brief Disable the watchdog before reconfiguring. + * @retval None + */ +static void watchdog_disable(void) +{ + WATCHDOG->CTRL &= ~(1U << WATCHDOG_CTRL_ENABLE_SHIFT); +} + +/** + * @brief Configure PSM WDSEL to reset everything except oscillators. + * @retval None + */ +static void watchdog_set_psm_wdsel(void) +{ + uint32_t mask; + mask = PSM_WDSEL_ALL_MASK; + mask &= ~(1U << PSM_WDSEL_ROSC_SHIFT); + mask &= ~(1U << PSM_WDSEL_XOSC_SHIFT); + PSM->WDSEL = mask; +} + +/** + * @brief Set pause-on-debug bits in CTRL so debugger halts the timer. + * @retval None + */ +static void watchdog_set_pause_debug(void) +{ + WATCHDOG->CTRL |= (1U << WATCHDOG_CTRL_PAUSE_DBG0_SHIFT); + WATCHDOG->CTRL |= (1U << WATCHDOG_CTRL_PAUSE_DBG1_SHIFT); + WATCHDOG->CTRL |= (1U << WATCHDOG_CTRL_PAUSE_JTAG_SHIFT); +} + +/** + * @brief Load and enable the watchdog countdown. + * @retval None + */ +static void watchdog_load_and_enable(void) +{ + WATCHDOG->LOAD = load_value; + WATCHDOG->CTRL |= (1U << WATCHDOG_CTRL_ENABLE_SHIFT); +} + +void watchdog_tick_init(void) +{ + watchdog_set_tick_cycles(); + watchdog_enable_tick(); +} + +void watchdog_enable(uint32_t timeout_ms) +{ + load_value = timeout_ms * 1000U; + if (load_value > WATCHDOG_LOAD_MAX) + load_value = WATCHDOG_LOAD_MAX; + watchdog_disable(); + watchdog_set_psm_wdsel(); + watchdog_set_pause_debug(); + watchdog_load_and_enable(); +} + +void watchdog_feed(void) +{ + WATCHDOG->LOAD = load_value; +} + +bool watchdog_caused_reboot(void) +{ + return (WATCHDOG->REASON & ((1U << WATCHDOG_REASON_TIMER_SHIFT) | (1U << WATCHDOG_REASON_FORCE_SHIFT))) != 0; +} diff --git a/drivers/0x0e_watchdog_cbm/Src/rp2350_xosc.c b/drivers/0x0e_watchdog_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..b33a71d --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Src/rp2350_xosc.c @@ -0,0 +1,54 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value &= ~CLK_PERI_CTRL_AUXSRC_MASK; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} + +void xosc_set_clk_ref(void) +{ + CLOCKS->CLK_REF_CTRL = CLK_REF_CTRL_SRC_XOSC; +} diff --git a/drivers/0x0e_watchdog_cbm/Src/vector_table.c b/drivers/0x0e_watchdog_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x0e_watchdog_cbm/build.log b/drivers/0x0e_watchdog_cbm/build.log new file mode 100644 index 0000000..e70d06e Binary files /dev/null and b/drivers/0x0e_watchdog_cbm/build.log differ diff --git a/drivers/0x0e_watchdog_cbm/linker.ld b/drivers/0x0e_watchdog_cbm/linker.ld new file mode 100644 index 0000000..56ac361 --- /dev/null +++ b/drivers/0x0e_watchdog_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x0e_watchdog_rust/.cargo/config.toml b/drivers/0x0e_watchdog_rust/.cargo/config.toml new file mode 100644 index 0000000..70fb5fa --- /dev/null +++ b/drivers/0x0e_watchdog_rust/.cargo/config.toml @@ -0,0 +1,32 @@ +[build] +target = "thumbv8m.main-none-eabihf" + +[target.thumbv6m-none-eabi] +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.thumbv8m.main-none-eabihf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.riscv32imac-unknown-none-elf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x0e_watchdog_rust/.pico-rs b/drivers/0x0e_watchdog_rust/.pico-rs new file mode 100644 index 0000000..be06904 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 diff --git a/drivers/0x0e_watchdog_rust/.vscode/extensions.json b/drivers/0x0e_watchdog_rust/.vscode/extensions.json new file mode 100644 index 0000000..107cc34 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} diff --git a/drivers/0x0e_watchdog_rust/.vscode/launch.json b/drivers/0x0e_watchdog_rust/.vscode/launch.json new file mode 100644 index 0000000..34833c2 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} diff --git a/drivers/0x0e_watchdog_rust/.vscode/settings.json b/drivers/0x0e_watchdog_rust/.vscode/settings.json new file mode 100644 index 0000000..3744e1f --- /dev/null +++ b/drivers/0x0e_watchdog_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} diff --git a/drivers/0x0e_watchdog_rust/.vscode/tasks.json b/drivers/0x0e_watchdog_rust/.vscode/tasks.json new file mode 100644 index 0000000..d288f27 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} diff --git a/drivers/0x0e_watchdog_rust/Cargo.lock b/drivers/0x0e_watchdog_rust/Cargo.lock new file mode 100644 index 0000000..9081e98 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/Cargo.lock @@ -0,0 +1,749 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "crc-any" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "frunk" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28aef0f9aa070bce60767c12ba9cb41efeaf1a2bc6427f87b7d83f11239a16d7" +dependencies = [ + "frunk_core", + "frunk_derives", +] + +[[package]] +name = "frunk_core" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476eeaa382e3462b84da5d6ba3da97b5786823c2d0d3a0d04ef088d073da225c" + +[[package]] +name = "frunk_derives" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b4095fc99e1d858e5b8c7125d2638372ec85aa0fe6c807105cf10b0265ca6c" +dependencies = [ + "frunk_proc_macro_helpers", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "frunk_proc_macro_helpers" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1952b802269f2db12ab7c0bd328d0ae8feaabf19f352a7b0af7bb0c5693abfce" +dependencies = [ + "frunk_core", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fugit" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "panic-halt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" + +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "riscv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", +] + +[[package]] +name = "riscv-rt" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d35e32cf1383183e8885d8a9aa4402a087fd094dc34c2cb6df6687d0229dfe" +dependencies = [ + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "riscv-rt-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp-binary-info" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f582261945fa215d40e2988b4df595d11c0908c0fff97a0fe23df766d117b790" + +[[package]] +name = "rp-hal-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288358786b1458fb2caac8c4b40fb529ef4200d6c46467e2695b7a8ba573ae8" +dependencies = [ + "fugit", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rp2040-hal" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb79a4590775204387f334672e6f79c0d734d0a159da23d60677b3c10fa1245" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "itertools 0.10.5", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "rp-binary-info", + "rp-hal-common", + "rp2040-hal-macros", + "rp2040-pac", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp2040-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e" +dependencies = [ + "cortex-m-rt", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rp2040-pac" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cbcd3f7a0ca7bbe61dc4eb7e202842bee4e27b769a7bf3a4a72fa399d6e404" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rp235x-hal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2939c82776b0b4ae110168b4298b5adf831e6cff249b057bf2a2187453b959c" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "cortex-m-rt", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "gcd", + "itertools 0.13.0", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "riscv", + "riscv-rt", + "rp-binary-info", + "rp-hal-common", + "rp235x-hal-macros", + "rp235x-pac", + "sha2-const-stable", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp235x-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74edd7a5979e9763bbb98e9746e711bac7464ee3397af7288e6c288ff0d3c764" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp235x-pac" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffcb6931deee4242886b5a1df62db5e2555b0eb6ae1e8be101f3ea3e58e65c6" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless", + "portable-atomic", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] + +[[package]] +name = "watchdog" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "fugit", + "panic-halt", + "panic-probe", + "regex", + "rp2040-boot2", + "rp2040-hal", + "rp235x-hal", +] diff --git a/drivers/0x0e_watchdog_rust/Cargo.toml b/drivers/0x0e_watchdog_rust/Cargo.toml new file mode 100644 index 0000000..6787512 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/Cargo.toml @@ -0,0 +1,39 @@ +[package] +edition = "2024" +name = "watchdog" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "watchdog_lib" +path = "src/lib.rs" + +[[bin]] +name = "watchdog" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x0e_watchdog_rust/build.rs b/drivers/0x0e_watchdog_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x0e_watchdog_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x0e_watchdog_rust/rp2040.x b/drivers/0x0e_watchdog_rust/rp2040.x new file mode 100644 index 0000000..6e1a654 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/rp2040.x @@ -0,0 +1,44 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; \ No newline at end of file diff --git a/drivers/0x0e_watchdog_rust/rp2350.x b/drivers/0x0e_watchdog_rust/rp2350.x new file mode 100644 index 0000000..570f72c --- /dev/null +++ b/drivers/0x0e_watchdog_rust/rp2350.x @@ -0,0 +1,47 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); diff --git a/drivers/0x0e_watchdog_rust/rp2350_riscv.x b/drivers/0x0e_watchdog_rust/rp2350_riscv.x new file mode 100644 index 0000000..2c9b445 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/rp2350_riscv.x @@ -0,0 +1,58 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +PROVIDE(__pre_init = default_pre_init); +PROVIDE(_setup_interrupts = default_setup_interrupts); +PROVIDE(_mp_hook = default_mp_hook); +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + . = ABSOLUTE(_stext); + } > FLASH +} diff --git a/drivers/0x0e_watchdog_rust/src/board.rs b/drivers/0x0e_watchdog_rust/src/board.rs new file mode 100644 index 0000000..a747b36 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/src/board.rs @@ -0,0 +1,349 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Watchdog driver pure-logic functions and constants +use watchdog_lib::watchdog; +// Microsecond duration type for watchdog timeout +use fugit::ExtU32; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +/// +/// # Arguments +/// +/// * `clocks` - The `clocks` parameter. +/// +/// # Returns +/// +/// A value of type `cortex_m::delay::Delay`. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Check whether the last reset was caused by the watchdog. +/// +/// Reads the WATCHDOG REASON register directly from the PAC. Returns +/// `true` if either the timer or force bits are set, matching the +/// C SDK `watchdog_caused_reboot()` behaviour. +/// +/// # Returns +/// +/// `true` if the watchdog triggered the last reset. +/// +/// # Returns +/// +/// `true` if successful or set, `false` otherwise. +pub(crate) fn watchdog_caused_reboot() -> bool { + let watchdog = unsafe { &*hal::pac::WATCHDOG::ptr() }; + let reason = watchdog.reason().read(); + reason.timer().bit_is_set() || reason.force().bit_is_set() +} + +/// Enable the hardware watchdog with the specified timeout. +/// +/// Wraps `hal::Watchdog::start()` converting the timeout from +/// milliseconds to microseconds as required by the HAL. +/// +/// # Arguments +/// +/// * `watchdog` - Mutable reference to the HAL watchdog. +/// * `timeout_ms` - Timeout in milliseconds (1–8388). +/// +/// # Arguments +/// +/// * `watchdog` - The `watchdog` parameter. +/// * `timeout_ms` - The `timeout_ms` parameter. +pub(crate) fn watchdog_enable(watchdog: &mut hal::Watchdog, timeout_ms: u32) { + let timeout_us = timeout_ms * 1_000; + watchdog.start(timeout_us.micros()); +} + +/// Feed the hardware watchdog to prevent a reboot. +/// +/// Wraps `hal::Watchdog::feed()`. +/// +/// # Arguments +/// +/// * `watchdog` - Reference to the HAL watchdog. +/// +/// # Arguments +/// +/// * `watchdog` - The `watchdog` parameter. +pub(crate) fn watchdog_feed(watchdog: &hal::Watchdog) { + watchdog.feed(); +} + +/// Run the watchdog feed-and-report loop. +/// +/// Feeds the watchdog every 1 second and prints `"Watchdog fed\r\n"` +/// over UART, matching the C demo's `_feed_and_report()` function. +/// This function never returns. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `watchdog` - Reference to the HAL watchdog. +/// * `delay` - Mutable reference to the blocking delay provider. +/// * `state` - Mutable reference to the watchdog driver state. +pub(crate) fn feed_loop( + uart: &EnabledUart, + watchdog: &hal::Watchdog, + delay: &mut cortex_m::delay::Delay, + state: &mut watchdog::WatchdogDriverState, +) -> ! { + loop { + watchdog_feed(watchdog); + state.feed(); + let mut buf = [0u8; 32]; + let n = watchdog::format_fed(&mut buf); + uart.write_full_blocking(&buf[..n]); + delay.delay_ms(watchdog::FEED_INTERVAL_MS); + } +} + +/// Initialise all peripherals and run the watchdog feed demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let mut wd = hal::Watchdog::new(pac.WATCHDOG); + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut wd, + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + report_reset_reason(&uart); + let mut state = start_watchdog(&uart, &mut wd); + feed_loop(&uart, &wd, &mut init_delay(&clocks), &mut state) +} + +/// Print whether the last reset was caused by the watchdog. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +fn report_reset_reason(uart: &EnabledUart) { + let mut buf = [0u8; 64]; + let caused = watchdog_caused_reboot(); + let n = watchdog::format_reset_reason(&mut buf, caused); + uart.write_full_blocking(&buf[..n]); +} + +/// Create the driver state, enable the hardware watchdog, and report. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `wd` - Mutable reference to the HAL watchdog. +/// +/// # Returns +/// +/// Initialised watchdog driver state. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `wd` - The `wd` parameter. +/// +/// # Returns +/// +/// A value of type `watchdog::WatchdogDriverState`. +fn start_watchdog(uart: &EnabledUart, wd: &mut hal::Watchdog) -> watchdog::WatchdogDriverState { + let mut state = watchdog::WatchdogDriverState::new(); + state.enable(watchdog::DEFAULT_TIMEOUT_MS); + watchdog_enable(wd, watchdog::DEFAULT_TIMEOUT_MS); + let mut buf = [0u8; 64]; + let n = watchdog::format_enabled(&mut buf, watchdog::DEFAULT_TIMEOUT_MS); + uart.write_full_blocking(&buf[..n]); + state +} + diff --git a/drivers/0x0e_watchdog_rust/src/lib.rs b/drivers/0x0e_watchdog_rust/src/lib.rs new file mode 100644 index 0000000..7191961 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod watchdog; diff --git a/drivers/0x0e_watchdog_rust/src/main.rs b/drivers/0x0e_watchdog_rust/src/main.rs new file mode 100644 index 0000000..c422575 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// Watchdog driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the watchdog demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"Watchdog Feed Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/0x0e_watchdog_rust/src/watchdog.rs b/drivers/0x0e_watchdog_rust/src/watchdog.rs new file mode 100644 index 0000000..7201c43 --- /dev/null +++ b/drivers/0x0e_watchdog_rust/src/watchdog.rs @@ -0,0 +1,510 @@ +//! Implementation module +//! +//! **File:** `watchdog.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// Default watchdog timeout used in the C demo (3000 ms). +pub const DEFAULT_TIMEOUT_MS: u32 = 3_000; + +/// Feed interval used in the C demo (1000 ms). +pub const FEED_INTERVAL_MS: u32 = 1_000; + +/// Maximum hardware watchdog timeout in milliseconds (8388 ms). +pub const MAX_TIMEOUT_MS: u32 = 8_388; + +/// UART message for "Watchdog fed\r\n" matching the C demo output. +pub const WATCHDOG_FED_MSG: &[u8] = b"Watchdog fed\r\n"; + +/// UART message for watchdog reboot detection. +pub const REBOOT_MSG: &[u8] = b"System rebooted by watchdog timeout\r\n"; + +/// UART message for normal power-on reset. +pub const NORMAL_RESET_MSG: &[u8] = b"Normal power-on reset\r\n"; + +/// Pure-logic watchdog driver state. +/// +/// Tracks whether the watchdog has been enabled, its configured timeout, +/// and the cumulative number of feeds. The actual hardware watchdog +/// enable / feed / reboot-check calls live in `board.rs`. +pub struct WatchdogDriverState { + /// Whether the watchdog has been enabled. + enabled: bool, + /// Configured timeout in milliseconds. + timeout_ms: u32, + /// Number of times the watchdog has been fed. + feed_count: u32, +} + +impl WatchdogDriverState { + /// Create a new watchdog driver state (disabled, zero timeout). + /// + /// # Returns + /// + /// A `WatchdogDriverState` with the watchdog disabled. + /// + /// # Returns + /// + /// A new instance of the struct. + pub fn new() -> Self { + Self { + enabled: false, + timeout_ms: 0, + feed_count: 0, + } + } + + /// Enable the watchdog with the given timeout. + /// + /// If the watchdog is already enabled, this updates the timeout and + /// resets the feed count. + /// + /// # Arguments + /// + /// * `timeout_ms` - Timeout in milliseconds (1–8388). + /// + /// # Arguments + /// + /// * `timeout_ms` - The `timeout_ms` parameter. + pub fn enable(&mut self, timeout_ms: u32) { + self.enabled = true; + self.timeout_ms = timeout_ms; + self.feed_count = 0; + } + + /// Feed the watchdog, incrementing the feed counter. + /// + /// Returns `true` if the watchdog is enabled and the feed was + /// accepted, `false` if the watchdog is not enabled. + /// + /// # Returns + /// + /// `true` if the watchdog was fed, `false` otherwise. + /// + /// # Returns + /// + /// `true` if successful or set, `false` otherwise. + pub fn feed(&mut self) -> bool { + if self.enabled { + self.feed_count += 1; + true + } else { + false + } + } + + /// Check whether the watchdog is currently enabled. + /// + /// # Returns + /// + /// `true` if the watchdog has been enabled. + /// + /// # Returns + /// + /// `true` if successful or set, `false` otherwise. + pub fn is_enabled(&self) -> bool { + self.enabled + } + + /// Return the configured timeout in milliseconds. + /// + /// # Returns + /// + /// The timeout value passed to `enable()`, or 0 if never enabled. + /// + /// # Returns + /// + /// A 32-bit unsigned integer value. + pub fn timeout_ms(&self) -> u32 { + self.timeout_ms + } + + /// Return the total number of times the watchdog has been fed. + /// + /// # Returns + /// + /// Cumulative feed count since the last `enable()`. + /// + /// # Returns + /// + /// A 32-bit unsigned integer value. + pub fn feed_count(&self) -> u32 { + self.feed_count + } +} + +/// Format the "Watchdog fed\r\n" message into `buf`. +/// +/// # Arguments +/// +/// * `buf` - Output buffer (must be >= 14 bytes). +/// +/// # Returns +/// +/// Number of bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_fed(buf: &mut [u8]) -> usize { + let msg = WATCHDOG_FED_MSG; + let n = msg.len().min(buf.len()); + buf[..n].copy_from_slice(&msg[..n]); + n +} + +/// Format the reset-reason message into `buf`. +/// +/// If `caused_reboot` is `true`, writes `REBOOT_MSG`; otherwise writes +/// `NORMAL_RESET_MSG`. +/// +/// # Arguments +/// +/// * `buf` - Output buffer. +/// * `caused_reboot` - Whether the watchdog triggered the last reset. +/// +/// # Returns +/// +/// Number of bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `caused_reboot` - The `caused_reboot` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_reset_reason(buf: &mut [u8], caused_reboot: bool) -> usize { + let msg = if caused_reboot { + REBOOT_MSG + } else { + NORMAL_RESET_MSG + }; + let n = msg.len().min(buf.len()); + buf[..n].copy_from_slice(&msg[..n]); + n +} + +/// Format the "Watchdog enabled (XXXXs timeout). Feeding every 1s...\r\n" +/// banner message into `buf`. +/// +/// # Arguments +/// +/// * `buf` - Output buffer (should be >= 64 bytes). +/// * `timeout_ms` - Timeout in milliseconds. +/// +/// # Returns +/// +/// Number of bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `timeout_ms` - The `timeout_ms` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_enabled(buf: &mut [u8], timeout_ms: u32) -> usize { + let prefix = b"Watchdog enabled ("; + let suffix = b"s timeout). Feeding every 1s...\r\n"; + let mut pos = copy_slice(buf, 0, prefix); + pos += format_u32(&mut buf[pos..], timeout_ms / 1000); + pos += copy_slice(buf, pos, suffix); + pos +} + +/// Copy a byte slice into `buf` at the given offset, returning bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `offset` - The `offset` parameter. +/// * `src` - The `src` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `offset` - The `offset` parameter. +/// * `src` - The `src` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn copy_slice(buf: &mut [u8], offset: usize, src: &[u8]) -> usize { + let n = src.len().min(buf.len().saturating_sub(offset)); + buf[offset..offset + n].copy_from_slice(&src[..n]); + n +} + +/// Format zero into `buf`, returning 1 if the buffer is non-empty, else 0. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn format_zero(buf: &mut [u8]) -> usize { + if !buf.is_empty() { + buf[0] = b'0'; + 1 + } else { + 0 + } +} + +/// Format a `u32` as decimal ASCII into `buf`. +/// +/// # Arguments +/// +/// * `buf` - Output buffer. +/// * `value` - The value to format. +/// +/// # Returns +/// +/// Number of bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_u32(buf: &mut [u8], value: u32) -> usize { + if value == 0 { + return format_zero(buf); + } + let mut tmp = [0u8; 10]; + let n = u32_to_digits_reversed(&mut tmp, value); + reverse_copy(buf, &tmp, n); + n +} + +/// Convert a u32 to reversed decimal digits in a temporary buffer. +/// +/// # Arguments +/// +/// * `tmp` - The `tmp` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `tmp` - The `tmp` parameter. +/// * `value` - Value to use. +/// +/// # Returns +/// +/// A value of type `usize`. +fn u32_to_digits_reversed(tmp: &mut [u8; 10], mut value: u32) -> usize { + let mut i = 0usize; + while value > 0 { + tmp[i] = b'0' + (value % 10) as u8; + value /= 10; + i += 1; + } + i +} + +/// Copy digits from a reversed temporary buffer into the output buffer. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `tmp` - The `tmp` parameter. +/// * `n` - Nibble or number. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `tmp` - The `tmp` parameter. +/// * `n` - Nibble or number. +fn reverse_copy(buf: &mut [u8], tmp: &[u8], n: usize) { + let count = n.min(buf.len()); + for j in 0..count { + buf[j] = tmp[n - 1 - j]; + } +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the new state is disabled operation. + #[test] + fn new_state_is_disabled() { + let state = WatchdogDriverState::new(); + assert!(!state.is_enabled()); + assert_eq!(state.timeout_ms(), 0); + assert_eq!(state.feed_count(), 0); + } + + /// Executes the enable activates watchdog operation. + #[test] + fn enable_activates_watchdog() { + let mut state = WatchdogDriverState::new(); + state.enable(3000); + assert!(state.is_enabled()); + assert_eq!(state.timeout_ms(), 3000); + assert_eq!(state.feed_count(), 0); + } + + /// Executes the feed increments count operation. + #[test] + fn feed_increments_count() { + let mut state = WatchdogDriverState::new(); + state.enable(3000); + assert!(state.feed()); + assert_eq!(state.feed_count(), 1); + assert!(state.feed()); + assert_eq!(state.feed_count(), 2); + } + + /// Executes the feed returns false when disabled operation. + #[test] + fn feed_returns_false_when_disabled() { + let mut state = WatchdogDriverState::new(); + assert!(!state.feed()); + assert_eq!(state.feed_count(), 0); + } + + /// Executes the enable resets feed count operation. + #[test] + fn enable_resets_feed_count() { + let mut state = WatchdogDriverState::new(); + state.enable(3000); + state.feed(); + state.feed(); + assert_eq!(state.feed_count(), 2); + state.enable(5000); + assert_eq!(state.feed_count(), 0); + assert_eq!(state.timeout_ms(), 5000); + } + + /// Executes the default timeout matches c demo operation. + #[test] + fn default_timeout_matches_c_demo() { + assert_eq!(DEFAULT_TIMEOUT_MS, 3000); + } + + /// Executes the feed interval matches c demo operation. + #[test] + fn feed_interval_matches_c_demo() { + assert_eq!(FEED_INTERVAL_MS, 1000); + } + + /// Executes the max timeout is 8388 operation. + #[test] + fn max_timeout_is_8388() { + assert_eq!(MAX_TIMEOUT_MS, 8388); + } + + /// Executes the format fed matches c output operation. + #[test] + fn format_fed_matches_c_output() { + let mut buf = [0u8; 32]; + let n = format_fed(&mut buf); + assert_eq!(&buf[..n], b"Watchdog fed\r\n"); + } + + /// Executes the format reset reason watchdog operation. + #[test] + fn format_reset_reason_watchdog() { + let mut buf = [0u8; 64]; + let n = format_reset_reason(&mut buf, true); + assert_eq!(&buf[..n], b"System rebooted by watchdog timeout\r\n"); + } + + /// Executes the format reset reason normal operation. + #[test] + fn format_reset_reason_normal() { + let mut buf = [0u8; 64]; + let n = format_reset_reason(&mut buf, false); + assert_eq!(&buf[..n], b"Normal power-on reset\r\n"); + } + + /// Executes the format enabled 3s operation. + #[test] + fn format_enabled_3s() { + let mut buf = [0u8; 64]; + let n = format_enabled(&mut buf, 3000); + assert_eq!( + &buf[..n], + b"Watchdog enabled (3s timeout). Feeding every 1s...\r\n" + ); + } + + /// Executes the format enabled 5s operation. + #[test] + fn format_enabled_5s() { + let mut buf = [0u8; 64]; + let n = format_enabled(&mut buf, 5000); + assert_eq!( + &buf[..n], + b"Watchdog enabled (5s timeout). Feeding every 1s...\r\n" + ); + } + + /// Executes the format u32 zero operation. + #[test] + fn format_u32_zero() { + let mut buf = [0u8; 16]; + let n = format_u32(&mut buf, 0); + assert_eq!(&buf[..n], b"0"); + } + + /// Executes the format u32 large value operation. + #[test] + fn format_u32_large_value() { + let mut buf = [0u8; 16]; + let n = format_u32(&mut buf, 12345); + assert_eq!(&buf[..n], b"12345"); + } +} diff --git a/drivers/0x0f_flash/.vscode/.vscode/c_cpp_properties.json b/drivers/0x0f_flash/.vscode/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0f_flash/.vscode/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0f_flash/.vscode/.vscode/cmake-kits.json b/drivers/0x0f_flash/.vscode/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0f_flash/.vscode/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0f_flash/.vscode/.vscode/extensions.json b/drivers/0x0f_flash/.vscode/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0f_flash/.vscode/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0f_flash/.vscode/.vscode/launch.json b/drivers/0x0f_flash/.vscode/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0f_flash/.vscode/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0f_flash/.vscode/.vscode/settings.json b/drivers/0x0f_flash/.vscode/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0f_flash/.vscode/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0f_flash/.vscode/.vscode/tasks.json b/drivers/0x0f_flash/.vscode/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0f_flash/.vscode/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0f_flash/.vscode/c_cpp_properties.json b/drivers/0x0f_flash/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e80461d --- /dev/null +++ b/drivers/0x0f_flash/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h", + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc.exe", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/drivers/0x0f_flash/.vscode/cmake-kits.json b/drivers/0x0f_flash/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/drivers/0x0f_flash/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/drivers/0x0f_flash/.vscode/extensions.json b/drivers/0x0f_flash/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/drivers/0x0f_flash/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/drivers/0x0f_flash/.vscode/launch.json b/drivers/0x0f_flash/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/drivers/0x0f_flash/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/drivers/0x0f_flash/.vscode/settings.json b/drivers/0x0f_flash/.vscode/settings.json new file mode 100644 index 0000000..688748a --- /dev/null +++ b/drivers/0x0f_flash/.vscode/settings.json @@ -0,0 +1,43 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "cmake.configureOnOpen": false, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": true, + "raspberry-pi-pico.useCmakeTools": false, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", + "files.associations": { + "stdlib.h": "c" + } +} diff --git a/drivers/0x0f_flash/.vscode/tasks.json b/drivers/0x0f_flash/.vscode/tasks.json new file mode 100644 index 0000000..544949c --- /dev/null +++ b/drivers/0x0f_flash/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "dependsOn": "Configure Project", + "dependsOrder": "sequence", + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Configure Project", + "type": "process", + "command": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "args": [ + "-S", + "${workspaceFolder}", + "-B", + "${workspaceFolder}/build", + "-G", + "Ninja" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Risc-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/drivers/0x0f_flash/0x0f_flash.c b/drivers/0x0f_flash/0x0f_flash.c new file mode 100644 index 0000000..dd59ed8 --- /dev/null +++ b/drivers/0x0f_flash/0x0f_flash.c @@ -0,0 +1,78 @@ +/** + * @file 0x0f_flash.c + * @brief On-chip flash write/read demo using flash.c/flash.h + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ----------------------------------------------------------------------------- + * + * Demonstrates on-chip flash read/write using the flash driver + * (flash.h / flash.c). A string is written to the last sector of flash + * and then read back to verify. The result is printed over UART. + * + * Wiring: + * No external wiring required + */ + +#include +#include +#include "pico/stdlib.h" +#include "flash.h" + +/** @brief Byte offset from flash start for demo write target */ +#define FLASH_TARGET_OFFSET (FLASH_DRIVER_SIZE_BYTES - FLASH_DRIVER_SECTOR_SIZE) +/** @brief Number of bytes to write in the flash demo */ +#define FLASH_WRITE_LEN FLASH_DRIVER_PAGE_SIZE + +/** + * @brief Fill a write buffer with 0xFF and copy the demo string into it + * + * @param buf Destination buffer + * @param buf_size Size of the buffer in bytes + */ +static void prepare_write_buf(uint8_t *buf, size_t buf_size) { + memset(buf, 0xFF, buf_size); + const char *msg = "Embedded Hacking flash driver demo"; + memcpy(buf, msg, strlen(msg) + 1); +} + +/** + * @brief Write the demo string to flash and print the read-back result + */ +static void write_and_verify(void) { + static uint8_t write_buf[FLASH_WRITE_LEN]; + static uint8_t read_buf[FLASH_WRITE_LEN]; + prepare_write_buf(write_buf, sizeof(write_buf)); + flash_driver_write(FLASH_TARGET_OFFSET, write_buf, FLASH_WRITE_LEN); + flash_driver_read(FLASH_TARGET_OFFSET, read_buf, FLASH_WRITE_LEN); + printf("Flash readback: %s\r\n", read_buf); +} + +int main(void) { + stdio_init_all(); + write_and_verify(); + while (true) + tight_loop_contents(); +} diff --git a/drivers/0x0f_flash/CMakeLists.txt b/drivers/0x0f_flash/CMakeLists.txt new file mode 100644 index 0000000..636b971 --- /dev/null +++ b/drivers/0x0f_flash/CMakeLists.txt @@ -0,0 +1,58 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico2 CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(0x0f_flash C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(0x0f_flash 0x0f_flash.c flash.c) + +pico_set_program_name(0x0f_flash "0x0f_flash") +pico_set_program_version(0x0f_flash "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(0x0f_flash 1) +pico_enable_stdio_usb(0x0f_flash 0) + +# Add the standard library to the build +target_link_libraries(0x0f_flash + pico_stdlib + hardware_flash + hardware_sync) + +# Add the standard include files to the build +target_include_directories(0x0f_flash PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(0x0f_flash) diff --git a/drivers/0x0f_flash/flash.c b/drivers/0x0f_flash/flash.c new file mode 100644 index 0000000..48f7e46 --- /dev/null +++ b/drivers/0x0f_flash/flash.c @@ -0,0 +1,58 @@ +/** + * @file flash.c + * @brief Implementation of on-chip flash read/write driver + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "flash.h" +#include +#include "hardware/flash.h" +#include "hardware/sync.h" +#include "pico/stdlib.h" + +void flash_driver_write(uint32_t flash_offset, const uint8_t *data, uint32_t len) { + if (data == NULL || flash_offset >= FLASH_DRIVER_SIZE_BYTES) { + return; + } + if (len > FLASH_DRIVER_SIZE_BYTES - flash_offset) { + len = FLASH_DRIVER_SIZE_BYTES - flash_offset; + } + uint32_t ints = save_and_disable_interrupts(); + flash_range_erase(flash_offset, FLASH_SECTOR_SIZE); + flash_range_program(flash_offset, data, len); + restore_interrupts(ints); +} + +void flash_driver_read(uint32_t flash_offset, uint8_t *out, uint32_t len) { + if (out == NULL || flash_offset >= FLASH_DRIVER_SIZE_BYTES) { + return; + } + if (len > FLASH_DRIVER_SIZE_BYTES - flash_offset) { + len = FLASH_DRIVER_SIZE_BYTES - flash_offset; + } + const uint8_t *flash_target_contents = (const uint8_t *)(XIP_BASE + flash_offset); + memcpy(out, flash_target_contents, len); +} diff --git a/drivers/0x0f_flash/flash.h b/drivers/0x0f_flash/flash.h new file mode 100644 index 0000000..aec8385 --- /dev/null +++ b/drivers/0x0f_flash/flash.h @@ -0,0 +1,74 @@ +/** + * @file flash.h + * @brief Header for on-chip flash read/write driver (XIP memory-mapped) + * @author Kevin Thomas + * @date 2025 + * + * MIT License + * + * Copyright (c) 2025 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef FLASH_H +#define FLASH_H + +#include +#include + +/** @brief Total on-chip flash size in bytes (4 MB) */ +#define FLASH_DRIVER_SIZE_BYTES (4 * 1024 * 1024) +/** @brief Flash erase sector size in bytes */ +#define FLASH_DRIVER_SECTOR_SIZE 4096 +/** @brief Flash program page size in bytes */ +#define FLASH_DRIVER_PAGE_SIZE 256 + +/** + * @brief Erase one 4096-byte sector and write data to on-chip flash + * + * The target address must be aligned to a 4096-byte sector boundary. + * The function guards against NULL @p data and out-of-range @p flash_offset, + * returning immediately if either is invalid. If @p len would exceed the + * flash boundary it is clamped to the remaining space. Interrupts are + * disabled for the erase+program sequence and re-enabled on return. The + * write length must be a multiple of FLASH_DRIVER_PAGE_SIZE (256 bytes); + * pad with 0xFF if necessary. + * + * @param flash_offset Byte offset from the start of flash (must be sector-aligned) + * @param data Pointer to the data buffer to write (must not be NULL) + * @param len Number of bytes to write (multiple of FLASH_DRIVER_PAGE_SIZE) + */ +void flash_driver_write(uint32_t flash_offset, const uint8_t *data, uint32_t len); + +/** + * @brief Read bytes from on-chip flash via the XIP memory map + * + * Flash is memory-mapped starting at XIP_BASE (0x10000000). This function + * guards against NULL @p out and out-of-range @p flash_offset, returning + * immediately if either is invalid. If @p len would exceed the flash + * boundary it is clamped to the remaining space before the memcpy. + * + * @param flash_offset Byte offset from the start of flash + * @param out Pointer to the destination buffer (must not be NULL) + * @param len Number of bytes to read + */ +void flash_driver_read(uint32_t flash_offset, uint8_t *out, uint32_t len); + +#endif // FLASH_H diff --git a/drivers/0x0f_flash/pico_sdk_import.cmake b/drivers/0x0f_flash/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/drivers/0x0f_flash/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +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") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/drivers/0x0f_flash_cbm/Inc/rp2350.h b/drivers/0x0f_flash_cbm/Inc/rp2350.h new file mode 100644 index 0000000..66618d5 --- /dev/null +++ b/drivers/0x0f_flash_cbm/Inc/rp2350.h @@ -0,0 +1,308 @@ +/** + * @file rp2350.h + * @brief RP2350 Device Peripheral Access Layer Header File. + * @author Kevin Thomas + * @date 2026 + * + * Memory-mapped register structures and peripheral base addresses + * for the RP2350 microcontroller (Cortex-M33 dual-core). All + * register offsets verified against the RP2350 datasheet + * (RP-008373-DS-2). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_H +#define __RP2350_H + +#include +#include + +/*!< Defines 'read / write' permissions */ +#define __IO volatile + +/*!< Stack addresses */ +#define STACK_TOP 0x20082000UL +#define STACK_LIMIT 0x2007A000UL + +/*!< Memory map */ +#define XIP_BASE 0x10000000UL +#define SRAM_BASE 0x20000000UL +#define SIO_BASE 0xD0000000UL +#define PPB_BASE 0xE0000000UL + +/*!< APB peripherals */ +#define PSM_BASE 0x40018000UL +#define CLOCKS_BASE 0x40010000UL +#define RESETS_BASE 0x40020000UL +#define IO_BANK0_BASE 0x40028000UL +#define PADS_BANK0_BASE 0x40038000UL +#define XOSC_BASE 0x40048000UL +#define UART0_BASE 0x40070000UL +#define WATCHDOG_BASE 0x400D8000UL +#define TICKS_BASE 0x40108000UL + +/*!< Atomic register alias offsets */ +#define ATOMIC_SET_OFFSET 0x2000UL +#define ATOMIC_CLR_OFFSET 0x3000UL + +/** + * @brief XOSC (External Crystal Oscillator) + */ +typedef struct +{ + __IO uint32_t CTRL; // Control register Address offset: 0x00 + __IO uint32_t STATUS; // Status register Address offset: 0x04 + __IO uint32_t DORMANT; // Dormant mode Address offset: 0x08 + __IO uint32_t STARTUP; // Startup delay Address offset: 0x0C + __IO uint32_t COUNT; // Frequency count Address offset: 0x10 +} XOSC_TypeDef; + +/** + * @brief CLOCKS + */ +typedef struct +{ + __IO uint32_t RESERVED0[12]; // GPOUT0..GPOUT3 registers Address offset: 0x00-0x2C + __IO uint32_t CLK_REF_CTRL; // Reference clock control Address offset: 0x30 + __IO uint32_t RESERVED1[5]; // CLK_REF_DIV..CLK_SYS_SELECTED Address offset: 0x34-0x44 + __IO uint32_t CLK_PERI_CTRL; // Peripheral clock control Address offset: 0x48 +} CLOCKS_TypeDef; + +/** + * @brief RESETS + */ +typedef struct +{ + __IO uint32_t RESET; // Reset control Address offset: 0x00 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x04 + __IO uint32_t RESET_DONE; // Reset done status Address offset: 0x08 +} RESETS_TypeDef; + +/** + * @brief IO_BANK0 GPIO Control (one per GPIO) + */ +typedef struct +{ + __IO uint32_t STATUS; // GPIO status Address offset: 0x00 + __IO uint32_t CTRL; // GPIO control Address offset: 0x04 +} IO_BANK0_GPIO_TypeDef; + +/** + * @brief IO_BANK0 + */ +typedef struct +{ + IO_BANK0_GPIO_TypeDef GPIO[30]; // GPIO 0-29 status/ctrl pairs Address offset: 0x000-0x0E8 +} IO_BANK0_TypeDef; + +/** + * @brief PADS_BANK0 + */ +typedef struct +{ + __IO uint32_t VOLTAGE_SELECT; // Voltage select Address offset: 0x00 + __IO uint32_t GPIO[30]; // GPIO 0-29 pad control Address offset: 0x04-0x78 +} PADS_BANK0_TypeDef; + +/** + * @brief PSM (Power-on State Machine) + */ +typedef struct +{ + __IO uint32_t FRCE_ON; // Force block out of reset Address offset: 0x00 + __IO uint32_t FRCE_OFF; // Force block into reset Address offset: 0x04 + __IO uint32_t WDSEL; // Watchdog select Address offset: 0x08 + __IO uint32_t DONE; // Subsystem ready status Address offset: 0x0C +} PSM_TypeDef; + +/** + * @brief WATCHDOG + */ +typedef struct +{ + __IO uint32_t CTRL; // Watchdog control Address offset: 0x00 + __IO uint32_t LOAD; // Load the watchdog timer (bits 23:0) Address offset: 0x04 + __IO uint32_t REASON; // Reason for last reset Address offset: 0x08 + __IO uint32_t SCRATCH[8]; // Scratch registers 0-7 Address offset: 0x0C-0x28 +} WATCHDOG_TypeDef; + +/** + * @brief TICKS (Tick Generator entry, one per source) + */ +typedef struct +{ + __IO uint32_t CTRL; // Enable tick generator (bit 0) Address offset: 0x00 + __IO uint32_t CYCLES; // Number of CLK_REF cycles per tick (bits 8:0) Address offset: 0x04 + __IO uint32_t COUNT; // Running counter of ticks (read-only) Address offset: 0x08 +} TICKS_Entry_TypeDef; + +/** + * @brief Peripheral Definitions + */ +#define PSM ((PSM_TypeDef *) PSM_BASE) +#define XOSC ((XOSC_TypeDef *) XOSC_BASE) +#define CLOCKS ((CLOCKS_TypeDef *) CLOCKS_BASE) +#define RESETS ((RESETS_TypeDef *) RESETS_BASE) +#define IO_BANK0 ((IO_BANK0_TypeDef *) IO_BANK0_BASE) +#define PADS_BANK0 ((PADS_BANK0_TypeDef *) PADS_BANK0_BASE) +#define SIO ((volatile uint32_t *) SIO_BASE) +#define CPACR ((volatile uint32_t *) (PPB_BASE + 0x0ED88UL)) +#define WATCHDOG ((WATCHDOG_TypeDef *) WATCHDOG_BASE) + +/** + * @brief TICKS entries (indexed from TICKS_BASE, 12-byte stride) + */ +#define TICKS_WATCHDOG ((TICKS_Entry_TypeDef *) (TICKS_BASE + 0x00UL)) + +/** + * @brief XOSC bit definitions + */ +#define XOSC_STATUS_STABLE_SHIFT 31U + +/** + * @brief CPACR bit definitions + */ +#define CPACR_CP0_SHIFT 0U +#define CPACR_CP1_SHIFT 1U + +/** + * @brief CLK_REF bit definitions + */ +#define CLK_REF_CTRL_SRC_XOSC 2U + +/** + * @brief CLOCKS bit definitions + */ +#define CLK_PERI_CTRL_ENABLE_SHIFT 11U +#define CLK_PERI_CTRL_AUXSRC_SHIFT 5U +#define CLK_PERI_CTRL_AUXSRC_MASK (0x07U << CLK_PERI_CTRL_AUXSRC_SHIFT) +#define CLK_PERI_CTRL_AUXSRC_XOSC 4U + +/** + * @brief RESETS bit definitions + */ +#define RESETS_RESET_IO_BANK0_SHIFT 6U +#define RESETS_RESET_PADS_BANK0_SHIFT 9U +#define RESETS_RESET_UART0_SHIFT 26U + +/** + * @brief IO_BANK0 bit definitions + */ +#define IO_BANK0_CTRL_FUNCSEL_MASK 0x1FU +#define IO_BANK0_CTRL_FUNCSEL_UART 0x02U +#define IO_BANK0_CTRL_FUNCSEL_SIO 0x05U +#define IO_BANK0_CTRL_FUNCSEL_NULL 0x1FU + +/** + * @brief PADS_BANK0 bit definitions + */ +#define PADS_BANK0_OD_SHIFT 7U +#define PADS_BANK0_IE_SHIFT 6U +#define PADS_BANK0_ISO_SHIFT 8U +#define PADS_BANK0_PUE_SHIFT 3U +#define PADS_BANK0_PDE_SHIFT 2U + +/** + * @brief SIO GPIO register offsets (word indices from SIO_BASE) + */ +#define SIO_GPIO_OUT_SET_OFFSET (0x018U / 4U) +#define SIO_GPIO_OUT_CLR_OFFSET (0x020U / 4U) +#define SIO_GPIO_OE_SET_OFFSET (0x038U / 4U) + +/** + * @brief UART register offsets (word indices from UART0_BASE) + */ +#define UART_DR_OFFSET (0x000U / 4U) +#define UART_FR_OFFSET (0x018U / 4U) +#define UART_IBRD_OFFSET (0x024U / 4U) +#define UART_FBRD_OFFSET (0x028U / 4U) +#define UART_LCR_H_OFFSET (0x02CU / 4U) +#define UART_CR_OFFSET (0x030U / 4U) + +/** + * @brief UART flag register bit definitions + */ +#define UART_FR_TXFF_MASK 32U +#define UART_FR_RXFE_MASK 16U + +/** + * @brief UART line control and enable values + */ +#define UART_LCR_H_8N1_FIFO 0x70U +#define UART_CR_ENABLE ((3U << 8) | 1U) + +/** + * @brief PSM bit definitions + */ +#define PSM_WDSEL_ALL_MASK 0x01FFFFFFUL +#define PSM_WDSEL_ROSC_SHIFT 2U +#define PSM_WDSEL_XOSC_SHIFT 3U + +/** + * @brief WATCHDOG CTRL bit definitions + */ +#define WATCHDOG_CTRL_TRIGGER_SHIFT 31U +#define WATCHDOG_CTRL_ENABLE_SHIFT 30U +#define WATCHDOG_CTRL_PAUSE_DBG1_SHIFT 26U +#define WATCHDOG_CTRL_PAUSE_DBG0_SHIFT 25U +#define WATCHDOG_CTRL_PAUSE_JTAG_SHIFT 24U +#define WATCHDOG_CTRL_TIME_MASK 0x00FFFFFFUL + +/** + * @brief WATCHDOG LOAD bit definitions + */ +#define WATCHDOG_LOAD_MAX 0x00FFFFFFUL + +/** + * @brief WATCHDOG REASON bit definitions + */ +#define WATCHDOG_REASON_FORCE_SHIFT 1U +#define WATCHDOG_REASON_TIMER_SHIFT 0U + +/** + * @brief TICKS bit definitions + */ +#define TICKS_CTRL_ENABLE_SHIFT 0U +#define TICKS_CYCLES_12MHZ 12U + +/*!< Bootrom function lookup */ +#define BOOTROM_TABLE_LOOKUP_OFFSET 0x16U +#define RT_FLAG_FUNC_ARM_SEC 0x0004U +#define RT_FLAG_FUNC_ARM_NONSEC 0x0010U + +/*!< ROM function codes */ +#define ROM_FUNC_CONNECT_INTERNAL_FLASH (('I') | (('F') << 8)) +#define ROM_FUNC_FLASH_EXIT_XIP (('E') | (('X') << 8)) +#define ROM_FUNC_FLASH_RANGE_ERASE (('R') | (('E') << 8)) +#define ROM_FUNC_FLASH_RANGE_PROGRAM (('R') | (('P') << 8)) +#define ROM_FUNC_FLASH_FLUSH_CACHE (('F') | (('C') << 8)) +#define ROM_FUNC_FLASH_ENTER_CMD_XIP (('C') | (('X') << 8)) + +/*!< Flash geometry */ +#define FLASH_SIZE (4U * 1024U * 1024U) +#define FLASH_SECTOR_SIZE 4096U +#define FLASH_PAGE_SIZE 256U +#define FLASH_BLOCK_SIZE (1U << 16) +#define FLASH_BLOCK_ERASE_CMD 0xD8U + +#endif /* __RP2350_H */ diff --git a/drivers/0x0f_flash_cbm/Inc/rp2350_flash.h b/drivers/0x0f_flash_cbm/Inc/rp2350_flash.h new file mode 100644 index 0000000..a00f5ea --- /dev/null +++ b/drivers/0x0f_flash_cbm/Inc/rp2350_flash.h @@ -0,0 +1,69 @@ +/** + * @file rp2350_flash.h + * @brief On-chip flash driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal flash erase, program, and read driver using ROM + * bootrom functions. Write operations run from RAM to avoid + * XIP conflicts. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_FLASH_H +#define __RP2350_FLASH_H + +#include "rp2350.h" + +/** + * @brief Erase the containing sector(s) and program data to flash. + * + * The data buffer must reside in RAM (not flash). Guards against + * NULL @p data and out-of-range @p offset, returning immediately + * if either is invalid. If @p len would exceed the flash boundary + * it is clamped to the remaining space. Interrupts are disabled + * for the erase/program cycle. The write length must be a multiple + * of FLASH_PAGE_SIZE (256 bytes); pad with 0xFF if necessary. + * + * @param offset byte offset from the start of flash (sector-aligned) + * @param data pointer to the source buffer in RAM (must not be NULL) + * @param len number of bytes to write + * @retval None + */ +void flash_write(uint32_t offset, const uint8_t *data, uint32_t len); + +/** + * @brief Read bytes from on-chip flash via the XIP memory map. + * + * Guards against NULL @p out and out-of-range @p offset, returning + * immediately if either is invalid. If @p len would exceed the flash + * boundary it is clamped to the remaining space before the read. + * + * @param offset byte offset from the start of flash + * @param out pointer to the destination buffer (must not be NULL) + * @param len number of bytes to read + * @retval None + */ +void flash_read(uint32_t offset, uint8_t *out, uint32_t len); + +#endif /* __RP2350_FLASH_H */ diff --git a/drivers/0x0f_flash_cbm/Inc/rp2350_reset.h b/drivers/0x0f_flash_cbm/Inc/rp2350_reset.h new file mode 100644 index 0000000..733054a --- /dev/null +++ b/drivers/0x0f_flash_cbm/Inc/rp2350_reset.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset.h + * @brief Reset controller driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Provides subsystem reset release for IO_BANK0. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_H +#define __RP2350_RESET_H + +#include "rp2350.h" + +/** + * @brief Release IO_BANK0 from reset and wait until ready. + * @retval None + */ +void reset_init_subsystem(void); + +#endif /* __RP2350_RESET_H */ diff --git a/drivers/0x0f_flash_cbm/Inc/rp2350_reset_handler.h b/drivers/0x0f_flash_cbm/Inc/rp2350_reset_handler.h new file mode 100644 index 0000000..43c2d7f --- /dev/null +++ b/drivers/0x0f_flash_cbm/Inc/rp2350_reset_handler.h @@ -0,0 +1,42 @@ +/** + * @file rp2350_reset_handler.h + * @brief Reset handler header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after reset. Performs stack initialization, XOSC + * setup, subsystem reset release, UART initialization, + * and branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_RESET_HANDLER_H +#define __RP2350_RESET_HANDLER_H + +/** + * @brief Reset handler entry point (naked, noreturn). + * @retval None + */ +void Reset_Handler(void) __attribute__((noreturn)); + +#endif /* __RP2350_RESET_HANDLER_H */ diff --git a/drivers/0x0f_flash_cbm/Inc/rp2350_stack.h b/drivers/0x0f_flash_cbm/Inc/rp2350_stack.h new file mode 100644 index 0000000..4faea5c --- /dev/null +++ b/drivers/0x0f_flash_cbm/Inc/rp2350_stack.h @@ -0,0 +1,43 @@ +/** + * @file rp2350_stack.h + * @brief Stack pointer initialization header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM from the STACK_TOP and + * STACK_LIMIT values defined in rp2350.h. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_STACK_H +#define __RP2350_STACK_H + +#include "rp2350.h" + +/** + * @brief Initialize MSP, PSP, MSPLIM, and PSPLIM stack pointers. + * @retval None + */ +void stack_init(void); + +#endif /* __RP2350_STACK_H */ diff --git a/drivers/0x0f_flash_cbm/Inc/rp2350_uart.h b/drivers/0x0f_flash_cbm/Inc/rp2350_uart.h new file mode 100644 index 0000000..c72890e --- /dev/null +++ b/drivers/0x0f_flash_cbm/Inc/rp2350_uart.h @@ -0,0 +1,82 @@ +/** + * @file rp2350_uart.h + * @brief UART0 driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Bare-metal UART0 driver supporting TX/RX on GPIO 0/1 at + * 115200 baud (12 MHz XOSC clock). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_UART_H +#define __RP2350_UART_H + +#include "rp2350.h" + +/** + * @brief Release UART0 from reset and wait until ready. + * @retval None + */ +void uart_release_reset(void); + +/** + * @brief Initialize UART0 pins, baud rate, line control, and enable. + * @retval None + */ +void uart_init(void); + +/** + * @brief Check whether a received byte is waiting in the UART FIFO. + * @retval bool true if at least one byte is available + */ +bool uart_is_readable(void); + +/** + * @brief Read one character from UART0 (blocking). + * @retval char the received character + */ +char uart_getchar(void); + +/** + * @brief Transmit one character over UART0 (blocking). + * @param c character to transmit + * @retval None + */ +void uart_putchar(char c); + +/** + * @brief Transmit a null-terminated string over UART0. + * @param str pointer to the string to send + * @retval None + */ +void uart_puts(const char *str); + +/** + * @brief Convert a lowercase ASCII character to uppercase. + * @param c input character + * @retval char uppercase equivalent or original character + */ +char uart_to_upper(char c); + +#endif /* __RP2350_UART_H */ diff --git a/drivers/0x0f_flash_cbm/Inc/rp2350_xosc.h b/drivers/0x0f_flash_cbm/Inc/rp2350_xosc.h new file mode 100644 index 0000000..80fd75d --- /dev/null +++ b/drivers/0x0f_flash_cbm/Inc/rp2350_xosc.h @@ -0,0 +1,55 @@ +/** + * @file rp2350_xosc.h + * @brief XOSC driver header for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * External crystal oscillator initialization and peripheral + * clock enable using the XOSC registers. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __RP2350_XOSC_H +#define __RP2350_XOSC_H + +#include "rp2350.h" + +/** + * @brief Initialize the external crystal oscillator and wait until stable. + * @retval None + */ +void xosc_init(void); + +/** + * @brief Enable the XOSC peripheral clock via CLK_PERI_CTRL. + * @retval None + */ +void xosc_enable_peri_clk(void); + +/** + * @brief Switch CLK_REF source to XOSC for a stable 12 MHz clk_sys. + * @retval None + */ +void xosc_set_clk_ref(void); + +#endif /* __RP2350_XOSC_H */ diff --git a/drivers/0x0f_flash_cbm/Makefile b/drivers/0x0f_flash_cbm/Makefile new file mode 100644 index 0000000..b5353e3 --- /dev/null +++ b/drivers/0x0f_flash_cbm/Makefile @@ -0,0 +1,84 @@ +# ------------------------------------------------------------------------------ +# @file Makefile +# @author Kevin Thomas +# @brief Build script for RP2350 bare-metal C flash driver. +# +# Compiles, links, and generates UF2 firmware for the RP2350. +# ------------------------------------------------------------------------------ + +# OS detection +ifeq ($(OS),Windows_NT) + MKDIR = if not exist $(subst /,\\,$(BUILD_DIR)) mkdir $(subst /,\\,$(BUILD_DIR)) + RM = if exist $(subst /,\\,$(BUILD_DIR)) rmdir /s /q $(subst /,\\,$(BUILD_DIR)) +else + MKDIR = mkdir -p $(BUILD_DIR) + RM = rm -rf $(BUILD_DIR) +endif + +# Toolchain +CC = arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +SIZE = arm-none-eabi-size + +# Target +TARGET = flash + +# Directories +SRC_DIR = Src +INC_DIR = Inc +BUILD_DIR = build + +# CPU flags +CPU_FLAGS = -mcpu=cortex-m33 -mthumb + +# Compiler flags +CFLAGS = $(CPU_FLAGS) -Og -g3 -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(INC_DIR) + +# Linker flags +LDFLAGS = $(CPU_FLAGS) -T linker.ld -nostdlib -Wl,--gc-sections + +# Source files +SRCS = $(SRC_DIR)/vector_table.c \ + $(SRC_DIR)/rp2350_reset_handler.c \ + $(SRC_DIR)/rp2350_stack.c \ + $(SRC_DIR)/rp2350_xosc.c \ + $(SRC_DIR)/rp2350_reset.c \ + $(SRC_DIR)/rp2350_uart.c \ + $(SRC_DIR)/rp2350_flash.c \ + $(SRC_DIR)/main.c \ + $(SRC_DIR)/image_def.c + +# Object files +OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) + +# Rules +.PHONY: all clean flash + +all: $(BUILD_DIR)/$(TARGET).bin + @echo "===================================" + @echo "SUCCESS! Created $(TARGET).elf and $(TARGET).bin" + @echo "===================================" + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/$(TARGET).elf: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) -o $@ + $(SIZE) $@ + +$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf + $(OBJCOPY) -O binary $< $@ + + +$(BUILD_DIR): + $(MKDIR) + +clean: + $(RM) + +flash: $(BUILD_DIR)/$(TARGET).elf + openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \ + -c "adapter speed 5000" \ + -c "program $< verify reset exit" diff --git a/drivers/0x0f_flash_cbm/Src/image_def.c b/drivers/0x0f_flash_cbm/Src/image_def.c new file mode 100644 index 0000000..72148ba --- /dev/null +++ b/drivers/0x0f_flash_cbm/Src/image_def.c @@ -0,0 +1,41 @@ +/** + * @file image_def.c + * @brief RP2350 IMAGE_DEF block for boot ROM image recognition. + * @author Kevin Thomas + * @date 2026 + * + * Must appear within the first 4 KB of flash for the boot ROM + * to accept the image. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +__attribute__((section(".embedded_block"), used)) +const uint8_t picobin_block[] = { + 0xD3, 0xDE, 0xFF, 0xFF, + 0x42, 0x01, 0x21, 0x10, + 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x79, 0x35, 0x12, 0xAB +}; diff --git a/drivers/0x0f_flash_cbm/Src/main.c b/drivers/0x0f_flash_cbm/Src/main.c new file mode 100644 index 0000000..f422cdd --- /dev/null +++ b/drivers/0x0f_flash_cbm/Src/main.c @@ -0,0 +1,84 @@ +/** + * @file main.c + * @brief On-chip flash write / read demonstration. + * @author Kevin Thomas + * @date 2026 + * + * Writes "Embedded Hacking flash driver demo" to the last + * sector of flash, reads it back via XIP, and prints the + * result over UART. + * + * Wiring: + * GPIO0 -> UART TX (USB-to-UART adapter RX) + * GPIO1 -> UART RX (USB-to-UART adapter TX) + * No external components required + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_flash.h" +#include "rp2350_uart.h" + +/** + * @brief Target offset: last sector of the 4 MB flash chip. + */ +/** @brief Flash offset for the target storage sector */ +#define FLASH_TARGET_OFFSET (FLASH_SIZE - FLASH_SECTOR_SIZE) + +/** + * @brief Fill a page buffer with 0xFF and copy the demo string. + * @param buf destination buffer (FLASH_PAGE_SIZE bytes in RAM) + * @retval None + */ +static void prepare_write_buf(uint8_t *buf) +{ + uint32_t i; + const char *msg = "Embedded Hacking flash driver demo"; + for (i = 0; i < FLASH_PAGE_SIZE; i++) + buf[i] = 0xFFU; + for (i = 0; msg[i] != '\0'; i++) + buf[i] = (uint8_t)msg[i]; + buf[i] = 0x00U; +} + +/** + * @brief Write the demo string to flash and print the read-back. + * @retval None + */ +static void write_and_verify(void) +{ + uint8_t write_buf[FLASH_PAGE_SIZE]; + uint8_t read_buf[FLASH_PAGE_SIZE]; + prepare_write_buf(write_buf); + flash_write(FLASH_TARGET_OFFSET, write_buf, FLASH_PAGE_SIZE); + flash_read(FLASH_TARGET_OFFSET, read_buf, FLASH_PAGE_SIZE); + uart_puts("Flash readback: "); + uart_puts((const char *)read_buf); + uart_puts("\r\n"); +} + +int main(void) +{ + write_and_verify(); + while (1) + __asm volatile ("wfi"); +} diff --git a/drivers/0x0f_flash_cbm/Src/rp2350_flash.c b/drivers/0x0f_flash_cbm/Src/rp2350_flash.c new file mode 100644 index 0000000..1007130 --- /dev/null +++ b/drivers/0x0f_flash_cbm/Src/rp2350_flash.c @@ -0,0 +1,148 @@ +/** + * @file rp2350_flash.c + * @brief On-chip flash driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Erases and programs flash sectors using ROM bootrom + * functions. The erase/program trampoline executes from RAM + * (placed in .ram_func) because XIP is disabled while the + * flash chip is being modified. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_flash.h" + +/** + * @brief ROM table lookup function pointer type (RP2350 ARM). + */ +typedef void *(*rom_table_lookup_fn)(uint32_t code, uint32_t mask); + +/** + * @brief ROM void function pointer (no parameters, no return). + */ +typedef void (*rom_void_fn)(void); + +/** + * @brief ROM flash range erase function pointer. + */ +typedef void (*rom_flash_erase_fn)(uint32_t addr, uint32_t count, + uint32_t block_size, uint8_t block_cmd); + +/** + * @brief ROM flash range program function pointer. + */ +typedef void (*rom_flash_program_fn)(uint32_t addr, const uint8_t *data, + uint32_t count); + +/** + * @brief Collection of ROM flash function pointers. + */ +typedef struct +{ + rom_void_fn connect; /**< @brief Connect to internal flash */ + rom_void_fn exit_xip; /**< @brief Exit XIP mode */ + rom_flash_erase_fn erase; /**< @brief Erase flash range */ + rom_flash_program_fn program; /**< @brief Program flash range */ + rom_void_fn flush_cache; /**< @brief Flush XIP cache */ + rom_void_fn enter_xip; /**< @brief Re-enter XIP mode */ +} FlashRomFns; + +/** + * @brief Look up a ROM function by its two-character code. + * @param code ROM_FUNC_* code from rp2350.h + * @retval void* pointer to the ROM function + */ +static void *rom_func_lookup(uint32_t code) +{ + rom_table_lookup_fn fn = + (rom_table_lookup_fn)(uintptr_t)(*(uint16_t *)BOOTROM_TABLE_LOOKUP_OFFSET); + return fn(code, RT_FLAG_FUNC_ARM_SEC); +} + +/** + * @brief Populate all ROM flash function pointers. + * @param fns pointer to the struct to fill + * @retval None + */ +static void lookup_rom_fns(FlashRomFns *fns) +{ + fns->connect = (rom_void_fn)rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH); + fns->exit_xip = (rom_void_fn)rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP); + fns->erase = (rom_flash_erase_fn)rom_func_lookup(ROM_FUNC_FLASH_RANGE_ERASE); + fns->program = (rom_flash_program_fn)rom_func_lookup(ROM_FUNC_FLASH_RANGE_PROGRAM); + fns->flush_cache = (rom_void_fn)rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE); + fns->enter_xip = (rom_void_fn)rom_func_lookup(ROM_FUNC_FLASH_ENTER_CMD_XIP); +} + +/** + * @brief RAM-resident trampoline that erases and programs flash. + * + * Must not call any function residing in flash. All ROM + * function pointers are passed via the fns struct. + * + * @param fns pointer to ROM function pointers (in RAM/stack) + * @param offset byte offset from start of flash + * @param data pointer to source buffer (must be in RAM) + * @param len number of bytes to program + * @retval None + */ +__attribute__((section(".ram_func"), noinline)) +static void flash_erase_program_ram(const FlashRomFns *fns, uint32_t offset, + const uint8_t *data, uint32_t len) +{ + fns->connect(); + fns->exit_xip(); + uint32_t erase_addr = offset & ~(FLASH_SECTOR_SIZE - 1U); + uint32_t erase_end = (offset + len + FLASH_SECTOR_SIZE - 1U) & ~(FLASH_SECTOR_SIZE - 1U); + fns->erase(erase_addr, erase_end - erase_addr, FLASH_BLOCK_SIZE, FLASH_BLOCK_ERASE_CMD); + fns->program(offset, data, len); + fns->flush_cache(); + fns->enter_xip(); +} + +void flash_write(uint32_t offset, const uint8_t *data, uint32_t len) +{ + if (data == NULL || offset >= FLASH_SIZE) + return; + if (len > FLASH_SIZE - offset) + len = FLASH_SIZE - offset; + FlashRomFns fns; + lookup_rom_fns(&fns); + uint32_t primask; + __asm volatile ("mrs %0, primask" : "=r" (primask)); + __asm volatile ("cpsid i"); + flash_erase_program_ram(&fns, offset, data, len); + __asm volatile ("msr primask, %0" :: "r" (primask)); +} + +void flash_read(uint32_t offset, uint8_t *out, uint32_t len) +{ + if (out == NULL || offset >= FLASH_SIZE) + return; + if (len > FLASH_SIZE - offset) + len = FLASH_SIZE - offset; + const uint8_t *src = (const uint8_t *)(XIP_BASE + offset); + for (uint32_t i = 0; i < len; i++) + out[i] = src[i]; +} diff --git a/drivers/0x0f_flash_cbm/Src/rp2350_reset.c b/drivers/0x0f_flash_cbm/Src/rp2350_reset.c new file mode 100644 index 0000000..702ef53 --- /dev/null +++ b/drivers/0x0f_flash_cbm/Src/rp2350_reset.c @@ -0,0 +1,41 @@ +/** + * @file rp2350_reset.c + * @brief Reset controller driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Releases IO_BANK0 from reset and waits until the subsystem + * is ready. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset.h" + +void reset_init_subsystem(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT); + RESETS->RESET = value; + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {} +} diff --git a/drivers/0x0f_flash_cbm/Src/rp2350_reset_handler.c b/drivers/0x0f_flash_cbm/Src/rp2350_reset_handler.c new file mode 100644 index 0000000..9d6866d --- /dev/null +++ b/drivers/0x0f_flash_cbm/Src/rp2350_reset_handler.c @@ -0,0 +1,80 @@ +/** + * @file rp2350_reset_handler.c + * @brief Reset handler implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Entry point after power-on or system reset. Copies the + * .data section from flash to RAM, initializes the stack, + * XOSC, subsystem resets, UART, then branches to main(). + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_reset_handler.h" +#include "rp2350_stack.h" +#include "rp2350_xosc.h" +#include "rp2350_reset.h" +#include "rp2350_uart.h" + +extern int main(void); + +extern uint32_t __data_lma; +extern uint32_t __data_start; +extern uint32_t __data_end; +extern uint32_t __bss_start; +extern uint32_t __bss_end; + +static void _data_copy_init(void) +{ + uint32_t *src = &__data_lma; + uint32_t *dst = &__data_start; + while (dst < &__data_end) + *dst++ = *src++; +} + +static void _bss_zero_init(void) +{ + uint32_t *dst = &__bss_start; + while (dst < &__bss_end) + *dst++ = 0U; +} + +void _ram_init(void) +{ + stack_init(); + _data_copy_init(); + _bss_zero_init(); +} + +void __attribute__((naked, noreturn)) Reset_Handler(void) +{ + __asm__ volatile ( + "bl _ram_init\n\t" + "bl xosc_init\n\t" + "bl xosc_enable_peri_clk\n\t" + "bl reset_init_subsystem\n\t" + "bl uart_release_reset\n\t" + "bl uart_init\n\t" + "b main\n\t" + ); +} diff --git a/drivers/0x0f_flash_cbm/Src/rp2350_stack.c b/drivers/0x0f_flash_cbm/Src/rp2350_stack.c new file mode 100644 index 0000000..2143da3 --- /dev/null +++ b/drivers/0x0f_flash_cbm/Src/rp2350_stack.c @@ -0,0 +1,47 @@ +/** + * @file rp2350_stack.c + * @brief Stack pointer initialization for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_stack.h" + +void stack_init(void) +{ + __asm__ volatile ( + "ldr r0, =%0\n\t" + "msr PSP, r0\n\t" + "ldr r0, =%1\n\t" + "msr MSPLIM, r0\n\t" + "msr PSPLIM, r0\n\t" + "ldr r0, =%0\n\t" + "msr MSP, r0\n\t" + : + : "i" (STACK_TOP), "i" (STACK_LIMIT) + : "r0" + ); +} diff --git a/drivers/0x0f_flash_cbm/Src/rp2350_uart.c b/drivers/0x0f_flash_cbm/Src/rp2350_uart.c new file mode 100644 index 0000000..4b196d5 --- /dev/null +++ b/drivers/0x0f_flash_cbm/Src/rp2350_uart.c @@ -0,0 +1,134 @@ +/** + * @file rp2350_uart.c + * @brief UART0 driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200 + * baud using the 12 MHz XOSC clock. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_uart.h" + +/** @brief Base address pointer for UART0 peripheral registers */ +#define UART_BASE ((volatile uint32_t *) UART0_BASE) + +/** + * @brief Clear the UART0 reset bit in the reset controller. + * @retval None + */ +static void uart_clear_reset_bit(void) +{ + uint32_t value; + value = RESETS->RESET; + value &= ~(1U << RESETS_RESET_UART0_SHIFT); + RESETS->RESET = value; +} + +/** + * @brief Wait until the UART0 block is out of reset. + * @retval None + */ +static void uart_wait_reset_done(void) +{ + while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {} +} + +/** + * @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function. + * @retval None + */ +static void uart_configure_pins(void) +{ + IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART; + PADS_BANK0->GPIO[0] = 0x04; + PADS_BANK0->GPIO[1] = 0x40; +} + +/** + * @brief Set UART0 baud rate divisors for 115200 at 12 MHz. + * @retval None + */ +static void uart_set_baud(void) +{ + UART_BASE[UART_CR_OFFSET] = 0; + UART_BASE[UART_IBRD_OFFSET] = 6; + UART_BASE[UART_FBRD_OFFSET] = 33; +} + +/** + * @brief Configure line control and enable UART0. + * @retval None + */ +static void uart_enable(void) +{ + UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO; + UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE; +} + +void uart_release_reset(void) +{ + uart_clear_reset_bit(); + uart_wait_reset_done(); +} + +void uart_init(void) +{ + uart_configure_pins(); + uart_set_baud(); + uart_enable(); +} + +bool uart_is_readable(void) +{ + return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0; +} + +char uart_getchar(void) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {} + return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF); +} + +void uart_putchar(char c) +{ + while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {} + UART_BASE[UART_DR_OFFSET] = (uint32_t)c; +} + +void uart_puts(const char *str) +{ + while (*str) + { + uart_putchar(*str++); + } +} + +char uart_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 32); + return c; +} diff --git a/drivers/0x0f_flash_cbm/Src/rp2350_xosc.c b/drivers/0x0f_flash_cbm/Src/rp2350_xosc.c new file mode 100644 index 0000000..b33a71d --- /dev/null +++ b/drivers/0x0f_flash_cbm/Src/rp2350_xosc.c @@ -0,0 +1,54 @@ +/** + * @file rp2350_xosc.c + * @brief XOSC driver implementation for RP2350. + * @author Kevin Thomas + * @date 2026 + * + * Configures the external crystal oscillator and enables the + * peripheral clock sourced from XOSC. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "rp2350_xosc.h" + +void xosc_init(void) +{ + XOSC->STARTUP = 0x00C4U; + XOSC->CTRL = 0x00FABAA0U; + while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {} +} + +void xosc_enable_peri_clk(void) +{ + uint32_t value; + value = CLOCKS->CLK_PERI_CTRL; + value &= ~CLK_PERI_CTRL_AUXSRC_MASK; + value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT); + value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT); + CLOCKS->CLK_PERI_CTRL = value; +} + +void xosc_set_clk_ref(void) +{ + CLOCKS->CLK_REF_CTRL = CLK_REF_CTRL_SRC_XOSC; +} diff --git a/drivers/0x0f_flash_cbm/Src/vector_table.c b/drivers/0x0f_flash_cbm/Src/vector_table.c new file mode 100644 index 0000000..23691fb --- /dev/null +++ b/drivers/0x0f_flash_cbm/Src/vector_table.c @@ -0,0 +1,44 @@ +/** + * @file vector_table.c + * @brief Vector table with initial stack pointer and reset handler. + * @author Kevin Thomas + * @date 2026 + * + * Placed in the .vectors section at the start of flash. + * The Thumb bit (bit 0 = 1) is automatically set by the + * linker for function pointers in Thumb mode. + * + * MIT License + * + * Copyright (c) 2026 Kevin Thomas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +extern uint32_t _stack_top; +extern void Reset_Handler(void); + +typedef void (*vector_func_t)(void); + +__attribute__((section(".vectors"), used)) +const void *_vectors[2] = { + &_stack_top, + Reset_Handler +}; diff --git a/drivers/0x0f_flash_cbm/build.log b/drivers/0x0f_flash_cbm/build.log new file mode 100644 index 0000000..457ded8 Binary files /dev/null and b/drivers/0x0f_flash_cbm/build.log differ diff --git a/drivers/0x0f_flash_cbm/linker.ld b/drivers/0x0f_flash_cbm/linker.ld new file mode 100644 index 0000000..ba468d2 --- /dev/null +++ b/drivers/0x0f_flash_cbm/linker.ld @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * @file linker.ld + * @author Kevin Thomas + * @brief Minimal linker script for bare-metal RP2350 development. + * + * Defines FLASH (XIP 32 MB) and RAM (520 kB SRAM) regions. + * The vector table is placed at the start of flash (0x10000000). + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Kevin Thomas. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** + * Entry point. + */ +ENTRY(Reset_Handler) + +/** + * Define memory regions. + */ +__XIP_BASE = 0x10000000; +__XIP_SIZE = 32M; + +__SRAM_BASE = 0x20000000; +__SRAM_SIZE = 520K; +__STACK_SIZE = 32K; + +/** + * Memory layout. + */ +MEMORY +{ + RAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE + FLASH (rx) : ORIGIN = __XIP_BASE, LENGTH = __XIP_SIZE +} + +/** + * Program headers. + */ +PHDRS +{ + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); +} + +/** + * Section placement. + */ +SECTIONS +{ + . = ORIGIN(FLASH); + + /** + * Vector table MUST be first at 0x10000000. + */ + .vectors : + { + KEEP(*(.vectors)) + } > FLASH :text + + /** + * Verify vector table placement. + */ + ASSERT((ADDR(.vectors) == ORIGIN(FLASH)), + "Vector table must be at start of flash (0x10000000)") + + /** + * Text and read-only data. + */ + .text : + { + . = ALIGN(4); + *(.text*) + *(.rodata*) + KEEP(*(.init)) + KEEP(*(.fini)) + KEEP(*(.ARM.attributes)) + } > FLASH :text + + /** + * IMAGE_DEF block at end of code. + */ + .embedded_block : + { + KEEP(*(.embedded_block)) + } > FLASH :text + + /** + * Initialized data and RAM-resident code (LMA in flash, VMA in RAM). + * Startup copies from __data_lma to __data_start..__data_end. + */ + .data : + { + . = ALIGN(4); + __data_start = .; + *(.ram_func*) + *(.data*) + . = ALIGN(4); + __data_end = .; + } > RAM AT> FLASH :data + + __data_lma = LOADADDR(.data); + + /** + * Uninitialized data (BSS) in RAM. + */ + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > RAM + + /** + * Non-secure stack symbols. + */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - __STACK_SIZE; + __stack = __StackTop; + _stack_top = __StackTop; + + /** + * Stack section (no load). + */ + .stack (NOLOAD) : { . = ALIGN(8); } > RAM + + /** + * Provide vector table symbol to startup code. + */ + PROVIDE(__Vectors = ADDR(.vectors)); +} diff --git a/drivers/0x0f_flash_rust/.cargo/config.toml b/drivers/0x0f_flash_rust/.cargo/config.toml new file mode 100644 index 0000000..70fb5fa --- /dev/null +++ b/drivers/0x0f_flash_rust/.cargo/config.toml @@ -0,0 +1,32 @@ +[build] +target = "thumbv8m.main-none-eabihf" + +[target.thumbv6m-none-eabi] +linker = "flip-link" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "no-vectorize-loops", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.thumbv8m.main-none-eabihf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "target-cpu=cortex-m33", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[target.riscv32imac-unknown-none-elf] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Trp2350_riscv.x", + "-C", "link-arg=-Tdefmt.x", +] +runner = "${PICOTOOL_PATH} load -u -v -x -t elf" + +[env] +DEFMT_LOG = "debug" diff --git a/drivers/0x0f_flash_rust/.pico-rs b/drivers/0x0f_flash_rust/.pico-rs new file mode 100644 index 0000000..be06904 --- /dev/null +++ b/drivers/0x0f_flash_rust/.pico-rs @@ -0,0 +1 @@ +rp2350 diff --git a/drivers/0x0f_flash_rust/.vscode/extensions.json b/drivers/0x0f_flash_rust/.vscode/extensions.json new file mode 100644 index 0000000..107cc34 --- /dev/null +++ b/drivers/0x0f_flash_rust/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "rust-lang.rust-analyzer", + "probe-rs.probe-rs-debugger", + "raspberry-pi.raspberry-pi-pico" + ] +} diff --git a/drivers/0x0f_flash_rust/.vscode/launch.json b/drivers/0x0f_flash_rust/.vscode/launch.json new file mode 100644 index 0000000..34833c2 --- /dev/null +++ b/drivers/0x0f_flash_rust/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (probe-rs)", + "cwd": "${workspaceFolder}", + "request": "launch", + "type": "probe-rs-debug", + "connectUnderReset": false, + "speed": 5000, + "runtimeExecutable": "probe-rs", + "chip": "${command:raspberry-pi-pico.getChip}", + "runtimeArgs": [ + "dap-server" + ], + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": false + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "${command:raspberry-pi-pico.launchTargetPath}", + "rttEnabled": true, + "svdFile": "${command:raspberry-pi-pico.getSVDPath}", + "rttChannelFormats": [ + { + "channelNumber": 0, + "dataFormat": "Defmt", + "mode": "NoBlockSkip", + "showTimestamps": true + } + ] + } + ], + "preLaunchTask": "Build + Generate SBOM (debug)", + "consoleLogLevel": "Debug", + "wireProtocol": "Swd" + } + ] +} diff --git a/drivers/0x0f_flash_rust/.vscode/settings.json b/drivers/0x0f_flash_rust/.vscode/settings.json new file mode 100644 index 0000000..3744e1f --- /dev/null +++ b/drivers/0x0f_flash_rust/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.check.allTargets": false, + "editor.formatOnSave": true, + "files.exclude": { + ".pico-rs": true + } +} diff --git a/drivers/0x0f_flash_rust/.vscode/tasks.json b/drivers/0x0f_flash_rust/.vscode/tasks.json new file mode 100644 index 0000000..d288f27 --- /dev/null +++ b/drivers/0x0f_flash_rust/.vscode/tasks.json @@ -0,0 +1,124 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build", + "--release" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (release)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathRelease}" + ] + }, + "dependsOn": "Compile Project", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Compile Project (debug)", + "type": "process", + "isBuildCommand": true, + "command": "cargo", + "args": [ + "build" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$rustc", + "options": { + "env": { + "PICOTOOL_PATH": "${command:raspberry-pi-pico.getPicotoolPath}", + "CHIP": "${command:raspberry-pi-pico.getChip}" + } + } + }, + { + "label": "Build + Generate SBOM (debug)", + "type": "shell", + "command": "bash", + "args": [ + "-lc", + "cargo sbom > ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ], + "windows": { + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + "cargo sbom | Set-Content -Encoding utf8 ${command:raspberry-pi-pico.sbomTargetPathDebug}" + ] + }, + "dependsOn": "Compile Project (debug)", + "presentation": { + "reveal": "silent", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Run Project", + "type": "shell", + "dependsOn": [ + "Build + Generate SBOM (release)" + ], + "command": "${command:raspberry-pi-pico.getPicotoolPath}", + "args": [ + "load", + "-x", + "${command:raspberry-pi-pico.launchTargetPathRelease}", + "-t", + "elf" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} diff --git a/drivers/0x0f_flash_rust/Cargo.lock b/drivers/0x0f_flash_rust/Cargo.lock new file mode 100644 index 0000000..ff64116 --- /dev/null +++ b/drivers/0x0f_flash_rust/Cargo.lock @@ -0,0 +1,749 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "crc-any" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "flash" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "fugit", + "panic-halt", + "panic-probe", + "regex", + "rp2040-boot2", + "rp2040-hal", + "rp235x-hal", +] + +[[package]] +name = "frunk" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28aef0f9aa070bce60767c12ba9cb41efeaf1a2bc6427f87b7d83f11239a16d7" +dependencies = [ + "frunk_core", + "frunk_derives", +] + +[[package]] +name = "frunk_core" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476eeaa382e3462b84da5d6ba3da97b5786823c2d0d3a0d04ef088d073da225c" + +[[package]] +name = "frunk_derives" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b4095fc99e1d858e5b8c7125d2638372ec85aa0fe6c807105cf10b0265ca6c" +dependencies = [ + "frunk_proc_macro_helpers", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "frunk_proc_macro_helpers" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1952b802269f2db12ab7c0bd328d0ae8feaabf19f352a7b0af7bb0c5693abfce" +dependencies = [ + "frunk_core", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fugit" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "panic-halt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" + +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "riscv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", +] + +[[package]] +name = "riscv-rt" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d35e32cf1383183e8885d8a9aa4402a087fd094dc34c2cb6df6687d0229dfe" +dependencies = [ + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "riscv-rt-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp-binary-info" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f582261945fa215d40e2988b4df595d11c0908c0fff97a0fe23df766d117b790" + +[[package]] +name = "rp-hal-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288358786b1458fb2caac8c4b40fb529ef4200d6c46467e2695b7a8ba573ae8" +dependencies = [ + "fugit", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rp2040-hal" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb79a4590775204387f334672e6f79c0d734d0a159da23d60677b3c10fa1245" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "itertools 0.10.5", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "rp-binary-info", + "rp-hal-common", + "rp2040-hal-macros", + "rp2040-pac", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp2040-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e" +dependencies = [ + "cortex-m-rt", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rp2040-pac" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cbcd3f7a0ca7bbe61dc4eb7e202842bee4e27b769a7bf3a4a72fa399d6e404" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rp235x-hal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2939c82776b0b4ae110168b4298b5adf831e6cff249b057bf2a2187453b959c" +dependencies = [ + "bitfield 0.14.0", + "cortex-m", + "cortex-m-rt", + "critical-section", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "frunk", + "fugit", + "gcd", + "itertools 0.13.0", + "nb 1.1.0", + "paste", + "pio", + "rand_core", + "riscv", + "riscv-rt", + "rp-binary-info", + "rp-hal-common", + "rp235x-hal-macros", + "rp235x-pac", + "sha2-const-stable", + "usb-device", + "vcell", + "void", +] + +[[package]] +name = "rp235x-hal-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74edd7a5979e9763bbb98e9746e711bac7464ee3397af7288e6c288ff0d3c764" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "rp235x-pac" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffcb6931deee4242886b5a1df62db5e2555b0eb6ae1e8be101f3ea3e58e65c6" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "vcell", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless", + "portable-atomic", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/drivers/0x0f_flash_rust/Cargo.toml b/drivers/0x0f_flash_rust/Cargo.toml new file mode 100644 index 0000000..057776b --- /dev/null +++ b/drivers/0x0f_flash_rust/Cargo.toml @@ -0,0 +1,39 @@ +[package] +edition = "2024" +name = "flash" +version = "0.1.0" +license = "MIT or Apache-2.0" + +[lib] +name = "flash_lib" +path = "src/lib.rs" + +[[bin]] +name = "flash" +path = "src/main.rs" + +[build-dependencies] +regex = "1.11.0" + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +fugit = "0.3" +defmt = "1" +defmt-rtt = "1" + +[target.'cfg( target_arch = "arm" )'.dependencies] +panic-probe = { version = "1", features = ["print-defmt"] } + +[target.'cfg( target_arch = "riscv32" )'.dependencies] +panic-halt = { version = "1.0.0" } + +[target.thumbv6m-none-eabi.dependencies] +rp2040-boot2 = "0.3" +rp2040-hal = { version = "0.11", features = ["rt", "critical-section-impl"] } + +[target.riscv32imac-unknown-none-elf.dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } + +[target."thumbv8m.main-none-eabihf".dependencies] +rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] } diff --git a/drivers/0x0f_flash_rust/build.rs b/drivers/0x0f_flash_rust/build.rs new file mode 100644 index 0000000..90c8e0a --- /dev/null +++ b/drivers/0x0f_flash_rust/build.rs @@ -0,0 +1,144 @@ +//! Implementation module +//! +//! **File:** `build.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Import regex::Regex +use regex::Regex; +// Import dependencies from std::fs +use std::fs::{File, read_to_string}; +// Import std::io::Write +use std::io::Write; +// Import std::path::PathBuf +use std::path::PathBuf; + +/// The main entry point. +fn main() { + let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let contents = read_to_string(".pico-rs") + .unwrap_or_default() + .trim() + .to_lowercase(); + let target = setup_target(&contents, &out); + update_cargo_config(&target); + write_riscv(&out); + print_cfgs(&out); +} + +/// Executes the setup target operation. +/// +/// # Arguments +/// +/// * `c` - Command byte. +/// * `out` - The `out` parameter. +/// +/// # Returns +/// +/// A value of type `String`. +fn setup_target(c: &str, out: &PathBuf) -> String { + if c == "rp2040" { + write_rp2040(out); + return "thumbv6m-none-eabi".to_string(); + } + write_rp2350(out); + if c.contains("riscv") { + "riscv32imac-unknown-none-elf".to_string() + } else { + "thumbv8m.main-none-eabihf".to_string() + } +} + +/// Executes the write rp2040 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2040(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2040.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2040"); + println!("cargo:rerun-if-changed=rp2040.x"); +} + +/// Executes the write rp2350 operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_rp2350(out: &PathBuf) { + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("rp2350.x")) + .unwrap(); + println!("cargo::rustc-cfg=rp2350"); + println!("cargo:rerun-if-changed=rp2350.x"); +} + +/// Executes the update cargo config operation. +/// +/// # Arguments +/// +/// * `target` - The `target` parameter. +fn update_cargo_config(target: &str) { + let re = Regex::new(r"target = .*").unwrap(); + let result = re.replace( + include_str!(".cargo/config.toml"), + format!("target = \"{}\"", target), + ); + File::create(".cargo/config.toml") + .unwrap() + .write_all(result.as_bytes()) + .unwrap(); +} + +/// Executes the write riscv operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn write_riscv(out: &PathBuf) { + File::create(out.join("rp2350_riscv.x")) + .unwrap() + .write_all(include_bytes!("rp2350_riscv.x")) + .unwrap(); +} + +/// Executes the print cfgs operation. +/// +/// # Arguments +/// +/// * `out` - The `out` parameter. +fn print_cfgs(out: &PathBuf) { + println!("cargo::rustc-check-cfg=cfg(rp2040)"); + println!("cargo::rustc-check-cfg=cfg(rp2350)"); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=.pico-rs"); + println!("cargo:rerun-if-changed=rp2350_riscv.x"); + println!("cargo:rerun-if-changed=build.rs"); +} + diff --git a/drivers/0x0f_flash_rust/rp2040.x b/drivers/0x0f_flash_rust/rp2040.x new file mode 100644 index 0000000..6e1a654 --- /dev/null +++ b/drivers/0x0f_flash_rust/rp2040.x @@ -0,0 +1,44 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K + SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k + SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; + +SECTIONS { + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; \ No newline at end of file diff --git a/drivers/0x0f_flash_rust/rp2350.x b/drivers/0x0f_flash_rust/rp2350.x new file mode 100644 index 0000000..570f72c --- /dev/null +++ b/drivers/0x0f_flash_rust/rp2350.x @@ -0,0 +1,47 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K + } + + SECTIONS { + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + } > FLASH + + } INSERT AFTER .vector_table; + + _stext = ADDR(.start_block) + SIZEOF(.start_block); + + SECTIONS { + .bi_entries : ALIGN(4) + { + __bi_entries_start = .; + KEEP(*(.bi_entries)); + . = ALIGN(4); + __bi_entries_end = .; + } > FLASH + } INSERT AFTER .text; + + SECTIONS { + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + + } INSERT AFTER .uninit; + + PROVIDE(start_to_end = __end_block_addr - __start_block_addr); + PROVIDE(end_to_start = __start_block_addr - __end_block_addr); diff --git a/drivers/0x0f_flash_rust/rp2350_riscv.x b/drivers/0x0f_flash_rust/rp2350_riscv.x new file mode 100644 index 0000000..2c9b445 --- /dev/null +++ b/drivers/0x0f_flash_rust/rp2350_riscv.x @@ -0,0 +1,58 @@ +/* +* SPDX-License-Identifier: MIT OR Apache-2.0 +* +* Copyright (c) 2021-2024 The rp-rs Developers +* Copyright (c) 2021 rp-rs organization +* Copyright (c) 2025 Raspberry Pi Ltd. +*/ + +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 2048K + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +PROVIDE(_stext = ORIGIN(FLASH)); +PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler); +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +PROVIDE(__pre_init = default_pre_init); +PROVIDE(_setup_interrupts = default_setup_interrupts); +PROVIDE(_mp_hook = default_mp_hook); +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + . = ABSOLUTE(_stext); + } > FLASH +} diff --git a/drivers/0x0f_flash_rust/src/board.rs b/drivers/0x0f_flash_rust/src/board.rs new file mode 100644 index 0000000..0ce5a91 --- /dev/null +++ b/drivers/0x0f_flash_rust/src/board.rs @@ -0,0 +1,332 @@ +//! Implementation module +//! +//! **File:** `board.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +// Flash driver pure-logic functions and constants +use flash_lib::flash; +// Rate extension trait for .Hz() baud rate construction +use fugit::RateExtU32; +// Clock trait for accessing system clock frequency +use hal::Clock; +// GPIO pin types and function selectors +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +// ROM data flash functions for low-level flash operations +use hal::rom_data; +// UART configuration and peripheral types +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX p. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + xosc, + clocks, + pll_sys, + pll_usb, + resets, + watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Transition flash out of XIP mode and erase the target sector. +/// +/// # Safety +/// +/// Must be called with interrupts disabled and no other flash access active. +/// +/// # Arguments +/// +/// * `flash_offset` - The `flash_offset` parameter. +/// +/// # Arguments +/// +/// * `flash_offset` - The `flash_offset` parameter. +unsafe fn flash_prepare_sector(flash_offset: u32) { + unsafe { + rom_data::connect_internal_flash(); + rom_data::flash_exit_xip(); + rom_data::flash_range_erase( + flash_offset, + flash::FLASH_SECTOR_SIZE as usize, + flash::FLASH_SECTOR_SIZE, + 0x20, + ); + } +} + +/// Program data into the erased sector and restore XIP mode. +/// +/// # Safety +/// +/// Must be called with interrupts disabled and no other flash access active. +/// +/// # Arguments +/// +/// * `flash_offset` - The `flash_offset` parameter. +/// * `data` - Data to send/write. +/// +/// # Arguments +/// +/// * `flash_offset` - The `flash_offset` parameter. +/// * `data` - Data to send/write. +unsafe fn flash_program_and_restore(flash_offset: u32, data: &[u8]) { + unsafe { + rom_data::flash_range_program(flash_offset, data.as_ptr(), data.len()); + rom_data::flash_flush_cache(); + rom_data::flash_enter_cmd_xip(); + } +} + +/// Erase one 4096-byte sector and write data to on-chip flash. +/// +/// Disables interrupts, transitions the flash device out of XIP mode, +/// erases the sector at `flash_offset`, programs `data` into it, flushes +/// the cache, re-enters XIP mode, and restores interrupts. This mirrors +/// the C demo's `flash_driver_write()`. +/// +/// # Arguments +/// +/// * `flash_offset` - Byte offset from the start of flash (must be sector-aligned). +/// * `data` - Data buffer to write (length must be a multiple of 256). +/// +/// # Safety +/// +/// Caller must ensure no other core or DMA is accessing flash/XIP during +/// this operation. +/// +/// # Arguments +/// +/// * `flash_offset` - The `flash_offset` parameter. +/// * `data` - Data to send/write. +pub(crate) fn flash_write(flash_offset: u32, data: &[u8]) { + cortex_m::interrupt::free(|_| unsafe { + flash_prepare_sector(flash_offset); + flash_program_and_restore(flash_offset, data); + }); +} + +/// Read bytes from on-chip flash via the XIP memory map. +/// +/// Flash is memory-mapped starting at XIP_BASE (0x10000000). This +/// function copies `out.len()` bytes from the XIP address corresponding +/// to `flash_offset` into `out`, matching the C demo's +/// `flash_driver_read()`. +/// +/// # Arguments +/// +/// * `flash_offset` - Byte offset from the start of flash. +/// * `out` - Destination buffer. +/// +/// # Arguments +/// +/// * `flash_offset` - The `flash_offset` parameter. +/// * `out` - The `out` parameter. +pub(crate) fn flash_read(flash_offset: u32, out: &mut [u8]) { + let addr = (flash::XIP_BASE + flash_offset) as *const u8; + for (i, byte) in out.iter_mut().enumerate() { + *byte = unsafe { core::ptr::read_volatile(addr.add(i)) }; + } +} + +/// Initialise all peripherals and run the flash demo. +/// +/// # Arguments +/// +/// * `pac` - PAC Peripherals singleton (consumed). +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Arguments +/// +/// * `pac` - The `pac` parameter. +/// +/// # Returns +/// +/// A value of type `!`. +pub(crate) fn run(mut pac: hal::pac::Peripherals) -> ! { + let clocks = init_clocks( + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut hal::Watchdog::new(pac.WATCHDOG), + ); + let p = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = init_uart(pac.UART0, p.gpio0, p.gpio1, &mut pac.RESETS, &clocks); + flash_demo(&uart); + loop { + cortex_m::asm::wfe(); + } +} + +/// Execute the flash write / read-back / report sequence. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +fn flash_demo(uart: &EnabledUart) { + let mut write_buf = [0u8; flash::FLASH_WRITE_LEN]; + flash::prepare_write_buf(&mut write_buf); + flash_write(flash::FLASH_TARGET_OFFSET, &write_buf); + let mut read_buf = [0u8; flash::FLASH_WRITE_LEN]; + flash_read(flash::FLASH_TARGET_OFFSET, &mut read_buf); + report_readback(uart, &read_buf); +} + +/// Format and print the flash read-back result over UART. +/// +/// # Arguments +/// +/// * `uart` - Reference to the enabled UART peripheral for serial output. +/// * `read_buf` - Buffer containing the data read back from flash. +/// +/// # Arguments +/// +/// * `uart` - The `uart` parameter. +/// * `read_buf` - The `read_buf` parameter. +fn report_readback(uart: &EnabledUart, read_buf: &[u8]) { + let mut out = [0u8; 128]; + let n = flash::format_readback(&mut out, read_buf); + uart.write_full_blocking(&out[..n]); +} + diff --git a/drivers/0x0f_flash_rust/src/flash.rs b/drivers/0x0f_flash_rust/src/flash.rs new file mode 100644 index 0000000..2a686be --- /dev/null +++ b/drivers/0x0f_flash_rust/src/flash.rs @@ -0,0 +1,307 @@ +//! Implementation module +//! +//! **File:** `flash.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +/// Total on-chip flash size in bytes (4 MB). +pub const FLASH_SIZE_BYTES: u32 = 4 * 1024 * 1024; + +/// Flash sector size in bytes (4096). +pub const FLASH_SECTOR_SIZE: u32 = 4096; + +/// Flash page size in bytes (256). +pub const FLASH_PAGE_SIZE: u32 = 256; + +/// Flash target offset: last sector of flash. +pub const FLASH_TARGET_OFFSET: u32 = FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE; + +/// Write buffer length matching the C demo (one page = 256 bytes). +pub const FLASH_WRITE_LEN: usize = FLASH_PAGE_SIZE as usize; + +/// XIP base address where flash is memory-mapped. +pub const XIP_BASE: u32 = 0x1000_0000; + +/// Demo string written to flash, matching the C demo exactly. +pub const DEMO_MSG: &[u8] = b"Embedded Hacking flash driver demo"; + +/// Fill the entire buffer with 0xFF (erased flash state). +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +fn fill_erased(buf: &mut [u8]) { + for b in buf.iter_mut() { + *b = 0xFF; + } +} + +/// Copy the demo string and NUL terminator into `buf`, returning meaningful length. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn copy_demo_msg(buf: &mut [u8]) -> usize { + let n = (DEMO_MSG.len() + 1).min(buf.len()); + let copy_len = DEMO_MSG.len().min(n); + buf[..copy_len].copy_from_slice(&DEMO_MSG[..copy_len]); + if copy_len < n { + buf[copy_len] = 0x00; + } + n +} + +/// Prepare a write buffer with 0xFF fill and the demo string at the start. +/// +/// Mirrors the C demo's `_prepare_write_buf()` function. The buffer is +/// filled with 0xFF (erased flash state), then the demo string including +/// its NUL terminator is copied to the beginning. +/// +/// # Arguments +/// +/// * `buf` - Output buffer (should be `FLASH_WRITE_LEN` bytes). +/// +/// # Returns +/// +/// Number of meaningful bytes (string length + NUL terminator). +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn prepare_write_buf(buf: &mut [u8]) -> usize { + fill_erased(buf); + copy_demo_msg(buf) +} + +/// Format the "Flash readback: \r\n" message into `buf`. +/// +/// Reads from `read_data` up to the first NUL byte (or end of slice) +/// and formats the output message matching the C demo's printf. +/// +/// # Arguments +/// +/// * `buf` - Output buffer (should be >= 64 bytes). +/// * `read_data` - Data read back from flash. +/// +/// # Returns +/// +/// Number of bytes written to `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `read_data` - The `read_data` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +pub fn format_readback(buf: &mut [u8], read_data: &[u8]) -> usize { + let prefix = b"Flash readback: "; + let suffix = b"\r\n"; + let mut pos = copy_slice(buf, 0, prefix); + pos += copy_c_string(&mut buf[pos..], read_data); + pos += copy_slice(buf, pos, suffix); + pos +} + +/// Copy a byte slice into `buf` at the given offset, returning bytes written. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `offset` - The `offset` parameter. +/// * `src` - The `src` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `offset` - The `offset` parameter. +/// * `src` - The `src` parameter. +/// +/// # Returns +/// +/// A value of type `usize`. +fn copy_slice(buf: &mut [u8], offset: usize, src: &[u8]) -> usize { + let n = src.len().min(buf.len().saturating_sub(offset)); + buf[offset..offset + n].copy_from_slice(&src[..n]); + n +} + +/// Copy bytes from `data` up to the first NUL into `buf`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// A value of type `usize`. +/// +/// # Arguments +/// +/// * `buf` - The `buf` parameter. +/// * `data` - Data to send/write. +/// +/// # Returns +/// +/// A value of type `usize`. +fn copy_c_string(buf: &mut [u8], data: &[u8]) -> usize { + let str_len = data.iter().position(|&b| b == 0).unwrap_or(data.len()); + let n = str_len.min(buf.len()); + buf[..n].copy_from_slice(&data[..n]); + n +} + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; + + /// Executes the flash size is 4mb operation. + #[test] + fn flash_size_is_4mb() { + assert_eq!(FLASH_SIZE_BYTES, 4 * 1024 * 1024); + } + + /// Executes the sector size is 4096 operation. + #[test] + fn sector_size_is_4096() { + assert_eq!(FLASH_SECTOR_SIZE, 4096); + } + + /// Executes the page size is 256 operation. + #[test] + fn page_size_is_256() { + assert_eq!(FLASH_PAGE_SIZE, 256); + } + + /// Executes the target offset is last sector operation. + #[test] + fn target_offset_is_last_sector() { + assert_eq!(FLASH_TARGET_OFFSET, FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE); + } + + /// Executes the write len matches page size operation. + #[test] + fn write_len_matches_page_size() { + assert_eq!(FLASH_WRITE_LEN, 256); + } + + /// Executes the xip base address operation. + #[test] + fn xip_base_address() { + assert_eq!(XIP_BASE, 0x1000_0000); + } + + /// Executes the prepare write buf fills 0xff operation. + #[test] + fn prepare_write_buf_fills_0xff() { + let mut buf = [0u8; FLASH_WRITE_LEN]; + prepare_write_buf(&mut buf); + for &b in &buf[(DEMO_MSG.len() + 1)..] { + assert_eq!(b, 0xFF); + } + } + + /// Executes the prepare write buf has demo string operation. + #[test] + fn prepare_write_buf_has_demo_string() { + let mut buf = [0u8; FLASH_WRITE_LEN]; + prepare_write_buf(&mut buf); + assert_eq!(&buf[..DEMO_MSG.len()], DEMO_MSG); + } + + /// Executes the prepare write buf has nul terminator operation. + #[test] + fn prepare_write_buf_has_nul_terminator() { + let mut buf = [0u8; FLASH_WRITE_LEN]; + prepare_write_buf(&mut buf); + assert_eq!(buf[DEMO_MSG.len()], 0x00); + } + + /// Executes the format readback matches c output operation. + #[test] + fn format_readback_matches_c_output() { + let mut read_data = [0xFFu8; FLASH_WRITE_LEN]; + let msg = b"Embedded Hacking flash driver demo"; + read_data[..msg.len()].copy_from_slice(msg); + read_data[msg.len()] = 0x00; + let mut buf = [0u8; 128]; + let n = format_readback(&mut buf, &read_data); + assert_eq!( + &buf[..n], + b"Flash readback: Embedded Hacking flash driver demo\r\n" + ); + } + + /// Executes the format readback empty string operation. + #[test] + fn format_readback_empty_string() { + let read_data = [0u8; 8]; + let mut buf = [0u8; 64]; + let n = format_readback(&mut buf, &read_data); + assert_eq!(&buf[..n], b"Flash readback: \r\n"); + } + + /// Executes the format readback no nul operation. + #[test] + fn format_readback_no_nul() { + let read_data = [b'A'; 8]; + let mut buf = [0u8; 64]; + let n = format_readback(&mut buf, &read_data); + assert_eq!(&buf[..n], b"Flash readback: AAAAAAAA\r\n"); + } + + /// Executes the demo msg matches c string operation. + #[test] + fn demo_msg_matches_c_string() { + assert_eq!(DEMO_MSG, b"Embedded Hacking flash driver demo"); + } +} diff --git a/drivers/0x0f_flash_rust/src/lib.rs b/drivers/0x0f_flash_rust/src/lib.rs new file mode 100644 index 0000000..05a6a9d --- /dev/null +++ b/drivers/0x0f_flash_rust/src/lib.rs @@ -0,0 +1,6 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![cfg_attr(not(test), no_std)] +pub mod flash; diff --git a/drivers/0x0f_flash_rust/src/main.rs b/drivers/0x0f_flash_rust/src/main.rs new file mode 100644 index 0000000..7f3e4dc --- /dev/null +++ b/drivers/0x0f_flash_rust/src/main.rs @@ -0,0 +1,102 @@ +//! Driver crate + +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] + +//! Implementation module +//! +//! **File:** `main.rs` +//! **Author:** Kevin Thomas +//! **Date:** 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#![no_std] +#![no_main] + +// Board-level helpers: constants, type aliases, and init functions +mod board; +// Flash driver module — suppress warnings for unused public API functions + +// Debugging output over RTT +use defmt_rtt as _; +// Panic handler for RISC-V targets +#[cfg(target_arch = "riscv32")] +// Import panic_halt as _ +use panic_halt as _; +// Panic handler for ARM targets +#[cfg(target_arch = "arm")] +// Import panic_probe as _ +use panic_probe as _; +// HAL entry-point macro +use hal::entry; +// Alias our HAL crate +#[cfg(rp2350)] +// Import rp235x_hal as hal +use rp235x_hal as hal; +#[cfg(rp2040)] +// Import rp2040_hal as hal +use rp2040_hal as hal; + +/// Second-stage boot loader for RP2040 +#[unsafe(link_section = ".boot2")] +#[used] +#[cfg(rp2040)] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// Boot metadata for the RP2350 Boot ROM +#[unsafe(link_section = ".start_block")] +#[used] +#[cfg(rp2350)] +pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); + +/// Application entry point for the on-chip flash demo. +/// +/// # Returns +/// +/// A value of type `!`. +/// +/// # Returns +/// +/// A value of type `!`. +#[entry] +fn main() -> ! { + board::run(hal::pac::Peripherals::take().unwrap()) +} + +/// Picotool binary info metadata +#[unsafe(link_section = ".bi_entries")] +#[used] +pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [ + hal::binary_info::rp_cargo_bin_name!(), + hal::binary_info::rp_cargo_version!(), + hal::binary_info::rp_program_description!(c"On-chip Flash Demo"), + hal::binary_info::rp_cargo_homepage_url!(), + hal::binary_info::rp_program_build_attribute!(), +]; + +#[cfg(test)] +mod tests { + // Import all parent module items + use super::*; +} diff --git a/drivers/auditor.py b/drivers/auditor.py new file mode 100644 index 0000000..d9c6632 --- /dev/null +++ b/drivers/auditor.py @@ -0,0 +1,88 @@ +import os +import sys +import re + +def audit_file(filepath): + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + lines = content.split('\n') + errors = [] + + # 1. Check header + header_found = False + for i, line in enumerate(lines[:10]): + if line.startswith('//! **File:**') or line.startswith('//! MIT License'): + header_found = True + break + + if not header_found and not filepath.endswith('mod.rs'): + errors.append("Missing or invalid `//!` header block.") + + # 2. Check function length and blank lines + in_function = False + func_start = 0 + func_name = "" + brace_level = 0 + blank_lines_in_func = [] + inline_comment_count = 0 + + for i, line in enumerate(lines): + stripped = line.strip() + + # Check inline comments + if '//' in line and not line.lstrip().startswith('//!'): + if not line.lstrip().startswith('///'): + inline_comment_count += 1 + + if not in_function: + if stripped.startswith("fn ") or stripped.startswith("pub fn ") or " fn " in stripped: + in_function = True + func_start = i + func_name = stripped.split("fn ")[1].split("(")[0].strip() + brace_level += stripped.count('{') - stripped.count('}') + else: + brace_level += stripped.count('{') - stripped.count('}') + + if stripped == "": + blank_lines_in_func.append(i + 1) + + if brace_level == 0: + in_function = False + func_len = i - func_start + 1 + if func_len > 8: + errors.append(f"Function `{func_name}` exceeds 8 lines (length: {func_len}, lines {func_start+1}-{i+1}).") + if blank_lines_in_func: + errors.append(f"Function `{func_name}` contains blank lines at: {blank_lines_in_func}") + blank_lines_in_func = [] + + if inline_comment_count == 0 and not filepath.endswith('mod.rs'): + errors.append("File contains 0 inline (`//`) comments. Must have logic explanations.") + + return errors + +def main(directory): + total_errors = 0 + for root, _, files in os.walk(directory): + if 'target' in root or '.git' in root: + continue + for file in files: + if file.endswith('.rs'): + filepath = os.path.join(root, file) + errors = audit_file(filepath) + if errors: + print(f"\n[FAIL] {filepath}") + for err in errors: + print(f" - {err}") + total_errors += 1 + else: + print(f"[PASS] {filepath}") + + if total_errors > 0: + sys.exit(1) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python auditor.py ") + sys.exit(1) + main(sys.argv[1]) diff --git a/pico-2-r4-pinout.svg b/pico-2-r4-pinout.svg new file mode 100644 index 0000000..be839ba --- /dev/null +++ b/pico-2-r4-pinout.svg @@ -0,0 +1,1816 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2 + + + 39 + + + DEBUG + + + 1 + + + LED + + + USB + + + BOOTSEL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/raspberry-pi-pico-c-sdk.pdf b/raspberry-pi-pico-c-sdk.pdf new file mode 100644 index 0000000..dee8bca Binary files /dev/null and b/raspberry-pi-pico-c-sdk.pdf differ diff --git a/rp2350-datasheet.pdf b/rp2350-datasheet.pdf new file mode 100644 index 0000000..7a11127 Binary files /dev/null and b/rp2350-datasheet.pdf differ diff --git a/uf2conv.py b/uf2conv.py new file mode 100644 index 0000000..529dd96 --- /dev/null +++ b/uf2conv.py @@ -0,0 +1,438 @@ +#!/usr/bin/env python3 +import sys +import struct +import subprocess +import re +import os +import os.path +import argparse +import json +from time import sleep + + +UF2_MAGIC_START0 = 0x0A324655 # "UF2\n" +UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected +UF2_MAGIC_END = 0x0AB16F30 # Ditto + +INFO_FILE = "/INFO_UF2.TXT" + +appstartaddr = 0x2000 +familyid = 0x0 + + +def is_uf2(buf): + w = struct.unpack(" 476: + assert False, "Invalid UF2 data size at " + ptr + newaddr = hd[3] + if (hd[2] & 0x2000) and (currfamilyid == None): + currfamilyid = hd[7] + if curraddr == None or ((hd[2] & 0x2000) and hd[7] != currfamilyid): + currfamilyid = hd[7] + curraddr = newaddr + if familyid == 0x0 or familyid == hd[7]: + appstartaddr = newaddr + padding = newaddr - curraddr + if padding < 0: + assert False, "Block out of order at " + ptr + if padding > 10 * 1024 * 1024: + assert False, "More than 10M of padding needed at " + ptr + if padding % 4 != 0: + assert False, "Non-word padding size at " + ptr + while padding > 0: + padding -= 4 + outp.append(b"\x00\x00\x00\x00") + if familyid == 0x0 or ((hd[2] & 0x2000) and familyid == hd[7]): + outp.append(block[32 : 32 + datalen]) + curraddr = newaddr + datalen + if hd[2] & 0x2000: + if hd[7] in families_found.keys(): + if families_found[hd[7]] > newaddr: + families_found[hd[7]] = newaddr + else: + families_found[hd[7]] = newaddr + if prev_flag == None: + prev_flag = hd[2] + if prev_flag != hd[2]: + all_flags_same = False + if blockno == (numblocks - 1): + print("--- UF2 File Header Info ---") + families = load_families() + for family_hex in families_found.keys(): + family_short_name = "" + for name, value in families.items(): + if value == family_hex: + family_short_name = name + print( + "Family ID is {:s}, hex value is 0x{:08x}".format( + family_short_name, family_hex + ) + ) + print("Target Address is 0x{:08x}".format(families_found[family_hex])) + if all_flags_same: + print("All block flag values consistent, 0x{:04x}".format(hd[2])) + else: + print("Flags were not all the same") + print("----------------------------") + if len(families_found) > 1 and familyid == 0x0: + outp = [] + appstartaddr = 0x0 + return b"".join(outp) + + +def convert_to_carray(file_content): + outp = "const unsigned long bindata_len = %d;\n" % len(file_content) + outp += "const unsigned char bindata[] __attribute__((aligned(16))) = {" + for i in range(len(file_content)): + if i % 16 == 0: + outp += "\n" + outp += "0x%02x, " % file_content[i] + outp += "\n};\n" + return bytes(outp, "utf-8") + + +def convert_to_uf2(file_content): + global familyid + datapadding = b"" + while len(datapadding) < 512 - 256 - 32 - 4: + datapadding += b"\x00\x00\x00\x00" + numblocks = (len(file_content) + 255) // 256 + outp = [] + for blockno in range(numblocks): + ptr = 256 * blockno + chunk = file_content[ptr : ptr + 256] + flags = 0x0 + if familyid: + flags |= 0x2000 + hd = struct.pack( + b"