Add complete firmware stack with USB-CDC proof of life
Dockerized build system (Dockerfile, docker-compose, build.sh) with Pico SDK cross-compilation. Modular CMake split into project_config, mcu_config, and sources_config under cmake/. Component architecture following inc/prg/cfg convention: STD_TYPES, MCU_USB, HAL_COM, APP_CLSW, SYS_ECU. Full call chain SYS_ECU -> APP_CLSW -> HAL_COM -> MCU_USB verified end-to-end on RP2040-Zero hardware over USB-CDC. Includes flash.sh for automated .uf2 flashing on macOS and devcontainer config for VS Code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
aabcb77641
commit
3687e48684
64
.devcontainer/devcontainer.json
Normal file
64
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,64 @@
|
||||
// ============================================================================
|
||||
// devcontainer.json - VS Code Dev Containers configuration
|
||||
// ----------------------------------------------------------------------------
|
||||
// Tells the VS Code "Dev Containers" extension how to launch this project
|
||||
// inside the Docker container defined by our docker-compose.yml. When you
|
||||
// run "Dev Containers: Reopen in Container" from the command palette, VS
|
||||
// Code uses this file to:
|
||||
// 1. Reuse the existing docker-compose.yml / Dockerfile (no duplication)
|
||||
// 2. Attach to the `pico-build` service and open /project as the workspace
|
||||
// 3. Install clangd + CMake Tools INSIDE the container (so language servers
|
||||
// can see the real Pico SDK at /opt/pico-sdk)
|
||||
// 4. Run CMake once after creation to generate compile_commands.json so
|
||||
// intellisense works from the moment the first file is opened
|
||||
//
|
||||
// NOTE: this file is JSONC (JSON with Comments + trailing commas allowed),
|
||||
// which is why these // comments are legal.
|
||||
// ============================================================================
|
||||
{
|
||||
// Display name shown in the VS Code status bar / window title
|
||||
"name": "Color Switcher PICO",
|
||||
|
||||
// Path to the docker-compose file, relative to THIS file.
|
||||
// .devcontainer/ is one level below the project root, so ../ reaches it.
|
||||
"dockerComposeFile": "../docker-compose.yml",
|
||||
|
||||
// Which service in docker-compose.yml to attach to. We only have one.
|
||||
"service": "pico-build",
|
||||
|
||||
// Folder inside the container to open as the VS Code workspace.
|
||||
// Must match the WORKDIR set in the Dockerfile.
|
||||
"workspaceFolder": "/project",
|
||||
|
||||
// Runs exactly once, the first time the container is created. We invoke
|
||||
// CMake to configure the build so that compile_commands.json is written
|
||||
// to build/ before clangd tries to parse the first source file.
|
||||
"postCreateCommand": "cmake -S cmake -B build",
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
// VS Code extensions installed INSIDE the container (not on the
|
||||
// host Mac). These run with access to /opt/pico-sdk and can
|
||||
// resolve all Pico SDK headers correctly.
|
||||
"extensions": [
|
||||
"llvm-vs-code-extensions.vscode-clangd",
|
||||
"ms-vscode.cmake-tools"
|
||||
],
|
||||
|
||||
// Arguments forwarded to clangd when it starts.
|
||||
// --compile-commands-dir tells clangd where CMake wrote the
|
||||
// compile_commands.json (inside build/, not the project root)
|
||||
// --header-insertion=never stops clangd from auto-adding
|
||||
// #include lines as you type, which tends to be noisy
|
||||
// --background-index builds a project-wide symbol index in
|
||||
// the background for fast go-to-definition / find-references
|
||||
"settings": {
|
||||
"clangd.arguments": [
|
||||
"--compile-commands-dir=${workspaceFolder}/build",
|
||||
"--header-insertion=never",
|
||||
"--background-index"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# ============================================================================
|
||||
# .gitignore for the color_switcher Pico firmware project
|
||||
# ============================================================================
|
||||
|
||||
# Build artifacts directory created by CMake / build.sh.
|
||||
# Contains Makefiles, object files, the final .elf/.uf2/.bin/.hex/.dis/.map
|
||||
# outputs, and any fetched dependencies (e.g. picotool). Regenerated on
|
||||
# every build, so it should never be committed.
|
||||
build/
|
||||
63
CLAUDE.md
63
CLAUDE.md
@ -4,4 +4,65 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project
|
||||
|
||||
Demo project for pico-1, Anthropic's agentic coding tool.
|
||||
Raspberry Pi Pico firmware that sends commands over UART to a host computer. Written entirely in C using the Pico SDK (RP2040 / Cortex-M0+).
|
||||
|
||||
## Build System
|
||||
|
||||
All builds run inside Docker — no local toolchain required.
|
||||
|
||||
- `docker compose build` — build the container image (first time / after Dockerfile changes)
|
||||
- `docker compose run --rm pico-build bash build.sh` — compile the firmware, output lands in `build/`
|
||||
- `docker compose run --rm pico-build bash` — interactive shell for debugging
|
||||
- `./flash.sh` — flash the `.uf2` to a Pico in BOOTSEL mode (host-side only, not Docker)
|
||||
- `docker compose run --rm pico-build bash build.sh && ./flash.sh` — build + flash in one command
|
||||
|
||||
The build uses CMake with the Pico SDK's ARM cross-compilation toolchain. The final artifact is a `.uf2` file in `build/`. Flashing must run on the host macOS because it copies to `/Volumes/RPI-RP2` (the Pico's USB mass storage mount).
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
color_switcher/
|
||||
├── cmake/ # all build-system files
|
||||
│ ├── CMakeLists.txt # thin orchestrator, drives build phases in order
|
||||
│ ├── pico_sdk_import.cmake # Pico SDK bootstrap (copied from SDK)
|
||||
│ └── cmake_config/
|
||||
│ ├── project_config.cmake # project-wide vars (name, version, languages)
|
||||
│ ├── mcu_config.cmake # MCU helpers: mcu_init / mcu_sdk_config / mcu_link_target
|
||||
│ └── sources_config.cmake # source glob + include dir list
|
||||
├── src/
|
||||
│ ├── STD_TYPES/inc/ # shared fixed-width types, status enums, macros
|
||||
│ ├── MCU_UART/{inc,prg,cfg}/ # hardware UART peripheral abstraction
|
||||
│ ├── MCU_USB/{inc,prg,cfg}/ # USB-CDC (virtual serial port) abstraction
|
||||
│ ├── HAL_COM/{inc,prg,cfg}/ # transport-agnostic comm layer (dispatches to UART/USB)
|
||||
│ ├── APP_CLSW/{inc,prg,cfg}/ # color switcher application logic
|
||||
│ └── SYS_ECU/prg/ # top-level app orchestrator (main entry)
|
||||
├── build.sh # out-of-source build wrapper
|
||||
├── Dockerfile / docker-compose.yml # containerized build env
|
||||
```
|
||||
|
||||
## Component file convention
|
||||
|
||||
Each component uses three subfolders:
|
||||
- `inc/` — public API headers (safe for any other component to `#include`)
|
||||
- `prg/` — private header (`*_priv.h`) + implementation (`*_prg.c`)
|
||||
- `cfg/` — configuration header and definitions (`*_cfg.h` / `*_cfg.c`)
|
||||
|
||||
## CMake build phases
|
||||
|
||||
The top-level `cmake/CMakeLists.txt` runs in a strict order dictated by the Pico SDK:
|
||||
|
||||
1. `include(project_config)` — defines variables
|
||||
2. `include(mcu_config)` — defines helper macros (no side effects)
|
||||
3. `mcu_init()` — includes `pico_sdk_import.cmake` (must be before `project()`)
|
||||
4. `project(...)`
|
||||
5. `mcu_sdk_config()` — calls `pico_sdk_init()`
|
||||
6. `include(sources_config)` — sets `PROJECT_SOURCES` and `PROJECT_INCLUDE_DIRS`
|
||||
7. `add_executable(...)`
|
||||
8. `mcu_link_target(...)` — `target_link_libraries`, stdio routing, UF2 output
|
||||
|
||||
`PROJECT_ROOT_DIR` is computed in the top-level `CMakeLists.txt` as the parent of `cmake/` and used everywhere that refers to paths outside the build system (e.g. `src/`).
|
||||
|
||||
## Conventions
|
||||
|
||||
- Always add descriptive comments to all code and config files
|
||||
- Avoid magic numbers — use named constants in `config.h`
|
||||
|
||||
39
Dockerfile
Normal file
39
Dockerfile
Normal file
@ -0,0 +1,39 @@
|
||||
# Base image: Ubuntu 24.04 provides a stable environment for the ARM cross-compilation toolchain
|
||||
FROM ubuntu:24.04
|
||||
|
||||
# Install all dependencies required to cross-compile C code for the Raspberry Pi Pico (RP2040):
|
||||
# - cmake: build system generator used by the Pico SDK
|
||||
# - gcc-arm-none-eabi: ARM cross-compiler that produces binaries for the Pico's Cortex-M0+ CPU
|
||||
# - libnewlib-arm-none-eabi: C standard library implementation for bare-metal ARM targets
|
||||
# - build-essential: provides make and other essential build utilities
|
||||
# - git: needed to clone the Pico SDK and its submodules
|
||||
# - python3: required by the Pico SDK build scripts for UF2 generation and other tooling
|
||||
# - clangd: C/C++ language server used by the VS Code Dev Container for
|
||||
# intellisense (go-to-definition, hover types, autocomplete) -
|
||||
# must live INSIDE the container so it can resolve Pico SDK
|
||||
# headers under /opt/pico-sdk at their real paths
|
||||
RUN apt-get update && apt-get install -y \
|
||||
cmake \
|
||||
gcc-arm-none-eabi \
|
||||
libnewlib-arm-none-eabi \
|
||||
build-essential \
|
||||
git \
|
||||
python3 \
|
||||
clangd \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Clone the official Raspberry Pi Pico SDK into the container.
|
||||
# The SDK provides hardware abstraction libraries, startup code, and linker scripts
|
||||
# needed to build firmware for the RP2040 microcontroller.
|
||||
# Submodules include tinyusb (USB stack) and other vendor-specific dependencies.
|
||||
RUN git clone https://github.com/raspberrypi/pico-sdk.git /opt/pico-sdk \
|
||||
&& cd /opt/pico-sdk \
|
||||
&& git submodule update --init
|
||||
|
||||
# Tell CMake where to find the Pico SDK. The SDK's CMake scripts look for this
|
||||
# environment variable to locate platform files, toolchain config, and libraries.
|
||||
ENV PICO_SDK_PATH=/opt/pico-sdk
|
||||
|
||||
# All build commands will run from /project, which is where the host source code
|
||||
# gets mounted via docker-compose volumes
|
||||
WORKDIR /project
|
||||
35
README.md
35
README.md
@ -1,15 +1,36 @@
|
||||
# Demo1
|
||||
# Color Switcher
|
||||
|
||||
A demo project for [pico-1](https://github.com/anthropics/pico-1), Anthropic's agentic coding tool.
|
||||
A Raspberry Pi Pico project that sends commands over UART to a connected computer. Written entirely in C using the Pico SDK.
|
||||
|
||||
## Overview
|
||||
## Prerequisites
|
||||
|
||||
This project serves as a demonstration and playground for exploring pico-1 capabilities.
|
||||
- [Docker](https://docs.docker.com/get-docker/) and Docker Compose
|
||||
|
||||
## Getting Started
|
||||
No local toolchain installation needed — everything runs inside the container.
|
||||
|
||||
Clone the repository and start experimenting:
|
||||
## Building
|
||||
|
||||
```bash
|
||||
cd demo1
|
||||
# Build the Docker image (first time only, or after Dockerfile changes)
|
||||
docker compose build
|
||||
|
||||
# Compile the firmware
|
||||
docker compose up
|
||||
```
|
||||
|
||||
The `.uf2` firmware file will appear in `build/`.
|
||||
|
||||
## Flashing
|
||||
|
||||
1. Hold the **BOOTSEL** button on the Pico and plug it into USB
|
||||
2. It mounts as a USB mass storage device
|
||||
3. Drag the `.uf2` file from `build/` onto the Pico
|
||||
4. The Pico reboots and runs the firmware
|
||||
|
||||
## Interactive Shell
|
||||
|
||||
To drop into the build container for debugging or manual commands:
|
||||
|
||||
```bash
|
||||
docker compose run --rm pico-build bash
|
||||
```
|
||||
25
build.sh
Executable file
25
build.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Build script for the Pico firmware.
|
||||
# Creates an out-of-source build directory to keep generated files
|
||||
# separate from the project source code.
|
||||
#
|
||||
# Directory layout expected at project root:
|
||||
# cmake/ - all build-system files (CMakeLists.txt + cmake_config/)
|
||||
# src/ - application source code
|
||||
# build/ - created by this script, holds all CMake/Make output
|
||||
#
|
||||
# Using -S and -B lets us point CMake at the cmake/ source folder while
|
||||
# keeping the build artifacts in a sibling build/ folder at the project root.
|
||||
|
||||
# Fail fast on any error so a broken configure step doesn't silently lead
|
||||
# to a confusing make error further down.
|
||||
set -e
|
||||
|
||||
# Configure the build: tell CMake the source directory is cmake/ and the
|
||||
# binary (build) directory is build/. CMake will create build/ if needed.
|
||||
cmake -S cmake -B build
|
||||
|
||||
# Compile everything using all available CPU cores. The final output is a
|
||||
# .uf2 file in build/ that can be dragged onto the Pico's USB mass storage.
|
||||
cmake --build build -j"$(nproc)"
|
||||
65
cmake/CMakeLists.txt
Normal file
65
cmake/CMakeLists.txt
Normal file
@ -0,0 +1,65 @@
|
||||
# ============================================================================
|
||||
# Top-level CMakeLists.txt for the color_switcher Pico firmware project
|
||||
# ============================================================================
|
||||
# This file is intentionally kept thin: it orchestrates the build phases in
|
||||
# the strict order CMake requires. All real configuration lives in the
|
||||
# cmake_config/*.cmake fragments so each concern is isolated and easy to find.
|
||||
#
|
||||
# Build phase order (forced by the Pico SDK + CMake semantics):
|
||||
# 1. project_config - variables only, no side effects
|
||||
# 2. mcu_config - defines mcu_init / mcu_sdk_config / mcu_link_target
|
||||
# 3. mcu_init() - SDK bootstrap (MUST run before project())
|
||||
# 4. project() - declares the CMake project using project_config vars
|
||||
# 5. mcu_sdk_config() - pico_sdk_init() (MUST run after project())
|
||||
# 6. sources_config - collects PROJECT_SOURCES and PROJECT_INCLUDE_DIRS
|
||||
# 7. add_executable() - the actual firmware build target
|
||||
# 8. mcu_link_target() - target_link_libraries + stdio + UF2 output
|
||||
# ============================================================================
|
||||
|
||||
# Require CMake 3.13 or newer. The Pico SDK uses features (like
|
||||
# target_link_libraries on object libraries) that were added in 3.13,
|
||||
# so older versions will fail to configure with cryptic errors.
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
# The real project root is one level above this file (this CMakeLists.txt
|
||||
# lives in <project>/cmake/). Everything outside the build system - the
|
||||
# src/ tree, the Dockerfile, etc. - is referenced via PROJECT_ROOT_DIR so
|
||||
# we never have to sprinkle "../" paths throughout the cmake fragments.
|
||||
get_filename_component(PROJECT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
|
||||
|
||||
# Make cmake_config/ searchable so include(foo) resolves to
|
||||
# cmake_config/foo.cmake without needing the full path each time.
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_config)
|
||||
|
||||
# Phase 1 - project-wide variables (PROJECT_NAME, PROJECT_VERSION, etc.)
|
||||
include(project_config)
|
||||
|
||||
# Phase 2 - MCU helper macros/functions (no side effects, just definitions)
|
||||
include(mcu_config)
|
||||
|
||||
# Phase 3 - Pico SDK bootstrap. MUST run before project() so the ARM
|
||||
# cross-compile toolchain is configured before CMake enables languages.
|
||||
mcu_init()
|
||||
|
||||
# Phase 4 - declare the CMake project using variables from project_config.
|
||||
# This triggers CMake to detect the (already-configured) cross compiler.
|
||||
project(${PROJECT_NAME}
|
||||
VERSION ${PROJECT_VERSION}
|
||||
LANGUAGES ${PROJECT_LANGUAGES})
|
||||
|
||||
# Phase 5 - register every Pico SDK library as a CMake target so we can
|
||||
# pick which ones to link later. This does NOT pull libraries into the
|
||||
# final binary - mcu_link_target() does that.
|
||||
mcu_sdk_config()
|
||||
|
||||
# Phase 6 - collect the list of .c files and include directories into
|
||||
# PROJECT_SOURCES and PROJECT_INCLUDE_DIRS variables.
|
||||
include(sources_config)
|
||||
|
||||
# Phase 7 - declare the firmware executable and its include paths.
|
||||
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_INCLUDE_DIRS})
|
||||
|
||||
# Phase 8 - link only the SDK libraries we actually use (pico_stdlib,
|
||||
# hardware_uart), route stdio over USB-CDC, and emit the .uf2 firmware file.
|
||||
mcu_link_target(${PROJECT_NAME})
|
||||
94
cmake/cmake_config/mcu_config.cmake
Normal file
94
cmake/cmake_config/mcu_config.cmake
Normal file
@ -0,0 +1,94 @@
|
||||
# ============================================================================
|
||||
# mcu_config.cmake
|
||||
# ----------------------------------------------------------------------------
|
||||
# Encapsulates all Raspberry Pi Pico / RP2040 specific configuration.
|
||||
# Depends on project_config.cmake having been included first (reads
|
||||
# PROJECT_C_STANDARD).
|
||||
#
|
||||
# Because CMake forces a strict order (SDK bootstrap before project(),
|
||||
# pico_sdk_init() after project(), linking after add_executable), this file
|
||||
# exposes three macros/functions that the top-level CMakeLists.txt calls
|
||||
# at the correct points in the build flow:
|
||||
#
|
||||
# mcu_init() - includes pico_sdk_import.cmake (pre-project)
|
||||
# mcu_sdk_config() - runs pico_sdk_init() (post-project)
|
||||
# mcu_link_target(target) - links SDK libs, configures stdio, emits UF2
|
||||
# ============================================================================
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Step 1/3 - called BEFORE project() in the top-level CMakeLists.txt.
|
||||
# Pulls in the Pico SDK bootstrap file so the ARM cross-compile toolchain
|
||||
# (arm-none-eabi-gcc) is configured before CMake enables the project
|
||||
# languages. Calling project() before this runs would cause CMake to detect
|
||||
# the host compiler instead and produce a broken build.
|
||||
# ----------------------------------------------------------------------------
|
||||
macro(mcu_init)
|
||||
# pico_sdk_import.cmake lives alongside the top-level CMakeLists.txt
|
||||
# inside the cmake/ folder, so CMAKE_SOURCE_DIR (which points at cmake/
|
||||
# when that file is the one being processed) is the right base path.
|
||||
include(${CMAKE_SOURCE_DIR}/pico_sdk_import.cmake)
|
||||
endmacro()
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Step 2/3 - called AFTER project() in the top-level CMakeLists.txt.
|
||||
# Applies the C standard chosen in project_config.cmake and registers every
|
||||
# Pico SDK library as a CMake target (pico_stdlib, hardware_uart,
|
||||
# hardware_gpio, ...) so we can later pick only the ones we need.
|
||||
# Note: pico_sdk_init() does NOT pull any libraries into the final binary
|
||||
# by itself - it only makes them available as link targets.
|
||||
# ----------------------------------------------------------------------------
|
||||
macro(mcu_sdk_config)
|
||||
set(CMAKE_C_STANDARD ${PROJECT_C_STANDARD})
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
pico_sdk_init()
|
||||
endmacro()
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Step 3/3 - called AFTER add_executable() in the top-level CMakeLists.txt.
|
||||
# Links only the Pico SDK libraries we actually use, routes stdio over
|
||||
# USB-CDC so printf output is visible on the host serial monitor without
|
||||
# any extra hardware, and emits the .uf2 file used for drag-and-drop
|
||||
# flashing onto the Pico's mass-storage bootloader.
|
||||
#
|
||||
# Parameters:
|
||||
# target - name of the executable target created with add_executable()
|
||||
# ----------------------------------------------------------------------------
|
||||
function(mcu_link_target target)
|
||||
# Pick only the libraries we need:
|
||||
# - pico_stdlib: core runtime, GPIO, clocks, basic init
|
||||
# - hardware_uart: UART peripheral API (used by the MCU_UART driver)
|
||||
target_link_libraries(${target} PRIVATE
|
||||
pico_stdlib
|
||||
hardware_uart
|
||||
)
|
||||
|
||||
# Route stdio over USB-CDC: the Pico will appear as a virtual serial
|
||||
# port on the host when plugged in, so printf/getchar are visible in
|
||||
# any serial monitor without needing a USB-to-UART adapter.
|
||||
# 1 = enabled, 0 = disabled.
|
||||
pico_enable_stdio_usb(${target} 1)
|
||||
pico_enable_stdio_uart(${target} 0)
|
||||
|
||||
# Ask the SDK to generate the .uf2 (plus .hex, .bin, .map) alongside
|
||||
# the .elf so the firmware can be flashed by dragging it onto the
|
||||
# Pico's USB mass-storage device after holding BOOTSEL.
|
||||
pico_add_extra_outputs(${target})
|
||||
|
||||
# Custom "flash" target: builds the firmware first (DEPENDS ensures
|
||||
# the .uf2 is up to date), then runs flash.sh on the host to copy
|
||||
# it to the Pico in BOOTSEL mode.
|
||||
#
|
||||
# Usage: cmake --build build --target flash
|
||||
#
|
||||
# NOTE: This target only works when cmake runs on the HOST macOS
|
||||
# (not inside Docker), because it needs access to /Volumes/RPI-RP2.
|
||||
# When running inside Docker, the target will fail with a clear error
|
||||
# from flash.sh ("not found" or "/Volumes/RPI-RP2 not accessible").
|
||||
add_custom_target(flash
|
||||
COMMAND bash ${PROJECT_ROOT_DIR}/flash.sh
|
||||
DEPENDS ${target}
|
||||
WORKING_DIRECTORY ${PROJECT_ROOT_DIR}
|
||||
COMMENT "Flashing firmware to Pico via USB mass storage"
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
38
cmake/cmake_config/project_config.cmake
Normal file
38
cmake/cmake_config/project_config.cmake
Normal file
@ -0,0 +1,38 @@
|
||||
# ============================================================================
|
||||
# project_config.cmake
|
||||
# ----------------------------------------------------------------------------
|
||||
# Project-wide identity and language settings. No side effects - this file
|
||||
# only sets variables that later fragments (mcu_config, sources) and the
|
||||
# top-level CMakeLists.txt read from.
|
||||
# ============================================================================
|
||||
|
||||
# Human-readable project name. Used as the CMake project name AND as the
|
||||
# build target name, so the final firmware artifact will be named
|
||||
# Color_Switcher_PICO.uf2 / .elf / .bin etc.
|
||||
set(PROJECT_NAME Color_Switcher_PICO)
|
||||
|
||||
# Semantic version of the firmware. Bump manually on releases.
|
||||
set(PROJECT_VERSION 0.1.0)
|
||||
|
||||
# Languages this project compiles:
|
||||
# - C : our application source files
|
||||
# - CXX : parts of the Pico SDK are C++ internally, so CMake must enable it
|
||||
# even though we write no C++ of our own
|
||||
# - ASM : RP2040 startup code, vector table, and boot2 are .S files that
|
||||
# must be passed through the assembler
|
||||
set(PROJECT_LANGUAGES C CXX ASM)
|
||||
|
||||
# C standard used across all targets (C11 gives us _Static_assert, stdint,
|
||||
# stdbool, etc. without relying on compiler extensions).
|
||||
set(PROJECT_C_STANDARD 11)
|
||||
|
||||
# Tell CMake to emit a compile_commands.json file inside the build directory
|
||||
# whenever the project is configured. This file lists every source file and
|
||||
# the exact compile command (including every -I include path and -D define)
|
||||
# that CMake will use to build it. clangd (the language server used by VS
|
||||
# Code for C/C++ intellisense) reads it to resolve #include directives and
|
||||
# provide accurate hover types, go-to-definition, and diagnostics. Without
|
||||
# this flag, clangd would guess blindly and fail to find Pico SDK headers
|
||||
# under /opt/pico-sdk. This setting is project-wide and safe - it costs
|
||||
# nothing at build time and is ignored by the compiler itself.
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
61
cmake/cmake_config/sources_config.cmake
Normal file
61
cmake/cmake_config/sources_config.cmake
Normal file
@ -0,0 +1,61 @@
|
||||
# ============================================================================
|
||||
# sources_config.cmake
|
||||
# ----------------------------------------------------------------------------
|
||||
# Collects the list of source files and include directories for the build.
|
||||
# Does NOT declare the executable - the top-level CMakeLists.txt handles
|
||||
# add_executable() so the build target is visible in one obvious place.
|
||||
#
|
||||
# Exported variables:
|
||||
# PROJECT_SOURCES - list of every .c file under src/
|
||||
# PROJECT_INCLUDE_DIRS - list of include paths for each component
|
||||
# ============================================================================
|
||||
|
||||
# NOTE: all source/header paths below are rooted at PROJECT_ROOT_DIR, which
|
||||
# the top-level CMakeLists.txt computes as the parent of its own directory
|
||||
# (i.e. one level above cmake/). We do NOT use CMAKE_SOURCE_DIR here because
|
||||
# that variable points at the cmake/ folder itself, not at the project root.
|
||||
|
||||
# Recursively collect every .c file under src/. CONFIGURE_DEPENDS makes
|
||||
# CMake re-check the glob on every build, so newly added .c files are
|
||||
# picked up without having to manually re-run cmake. The trade-off is a
|
||||
# tiny build-time stat check on each file, which is well worth it for a
|
||||
# small project where we add files often.
|
||||
file(GLOB_RECURSE PROJECT_SOURCES CONFIGURE_DEPENDS
|
||||
"${PROJECT_ROOT_DIR}/src/*.c")
|
||||
|
||||
# Every component follows the same folder convention:
|
||||
# inc/ - public API headers (safe for any other component to include)
|
||||
# prg/ - private headers and implementation files (component-internal)
|
||||
# cfg/ - configuration headers and constants
|
||||
#
|
||||
# All three are added to the include path here so #include "MCU_UART.h"
|
||||
# etc. resolves regardless of which translation unit is doing the include.
|
||||
set(PROJECT_INCLUDE_DIRS
|
||||
# Shared library layer - fundamental types used by every component
|
||||
${PROJECT_ROOT_DIR}/src/STD_TYPES/inc
|
||||
|
||||
# MCU layer - hardware abstraction for the RP2040 UART peripheral
|
||||
${PROJECT_ROOT_DIR}/src/MCU_UART/inc
|
||||
${PROJECT_ROOT_DIR}/src/MCU_UART/prg
|
||||
${PROJECT_ROOT_DIR}/src/MCU_UART/cfg
|
||||
|
||||
# MCU layer - hardware abstraction for the RP2040 USB-CDC peripheral
|
||||
# (the Pico appears as a virtual serial port on the host computer)
|
||||
${PROJECT_ROOT_DIR}/src/MCU_USB/inc
|
||||
${PROJECT_ROOT_DIR}/src/MCU_USB/prg
|
||||
${PROJECT_ROOT_DIR}/src/MCU_USB/cfg
|
||||
|
||||
# HAL layer - transport-agnostic communication abstraction that
|
||||
# dispatches to MCU_UART, MCU_USB, or both depending on configuration
|
||||
${PROJECT_ROOT_DIR}/src/HAL_COM/inc
|
||||
${PROJECT_ROOT_DIR}/src/HAL_COM/prg
|
||||
${PROJECT_ROOT_DIR}/src/HAL_COM/cfg
|
||||
|
||||
# Application layer - color switcher application logic
|
||||
${PROJECT_ROOT_DIR}/src/APP_CLSW/inc
|
||||
${PROJECT_ROOT_DIR}/src/APP_CLSW/prg
|
||||
${PROJECT_ROOT_DIR}/src/APP_CLSW/cfg
|
||||
|
||||
# Application layer - system ECU / top-level orchestrator
|
||||
${PROJECT_ROOT_DIR}/src/SYS_ECU/prg
|
||||
)
|
||||
121
cmake/pico_sdk_import.cmake
Normal file
121
cmake/pico_sdk_import.cmake
Normal file
@ -0,0 +1,121 @@
|
||||
# This is a copy of <PICO_SDK_PATH>/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})
|
||||
37
docker-compose.yml
Normal file
37
docker-compose.yml
Normal file
@ -0,0 +1,37 @@
|
||||
# ============================================================================
|
||||
# Docker Compose configuration for the Raspberry Pi Pico firmware project
|
||||
# ============================================================================
|
||||
# The container is configured to be long-running (sleep infinity) rather than
|
||||
# running the build on startup. This lets the same container be used for:
|
||||
# 1. One-shot builds from the host
|
||||
# 2. Interactive shells (docker compose exec) for debugging
|
||||
# 3. VS Code Dev Containers - which expects the container to stay alive
|
||||
# so it can attach clangd, install extensions, and open a terminal
|
||||
#
|
||||
# Usage:
|
||||
# docker compose build - (re)build the image after Dockerfile changes
|
||||
# docker compose up -d - start the persistent container in the background
|
||||
# docker compose exec pico-build bash - shell into the running container
|
||||
# docker compose run --rm pico-build bash build.sh - one-shot firmware build, container removed after
|
||||
# docker compose down - stop and remove the persistent container
|
||||
# ============================================================================
|
||||
|
||||
services:
|
||||
pico-build:
|
||||
# Build the image from the Dockerfile in the project root
|
||||
build: .
|
||||
|
||||
# Mount the project source code into the container's working directory.
|
||||
# This lets the container read our source files and write build artifacts
|
||||
# (including the .uf2 firmware file) back to the host filesystem.
|
||||
volumes:
|
||||
- .:/project
|
||||
|
||||
# Keep the container alive indefinitely. We intentionally do NOT run the
|
||||
# build on startup - `sleep infinity` lets the container stay up so it can
|
||||
# be used as a persistent dev environment (VS Code Dev Containers, shells
|
||||
# via `docker compose exec`, etc.). To trigger a build, run:
|
||||
# docker compose run --rm pico-build bash build.sh
|
||||
# or, if you're already inside the container:
|
||||
# bash build.sh
|
||||
command: sleep infinity
|
||||
46
flash.sh
Executable file
46
flash.sh
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# flash.sh - Flash firmware to the Raspberry Pi Pico
|
||||
# ============================================================================
|
||||
# This script MUST run on the host macOS (not inside Docker) because it
|
||||
# needs access to /Volumes/RPI-RP2, the USB mass storage mount point that
|
||||
# appears when the Pico is held in BOOTSEL mode during power-on.
|
||||
#
|
||||
# Usage:
|
||||
# 1. Hold BOOTSEL on the Pico and plug it into USB
|
||||
# 2. Run: ./flash.sh
|
||||
#
|
||||
# Or chain with a build:
|
||||
# docker compose run --rm pico-build bash build.sh && ./flash.sh
|
||||
# ============================================================================
|
||||
|
||||
PICO_MOUNT="/Volumes/RPI-RP2"
|
||||
UF2_FILE="build/Color_Switcher_PICO.uf2"
|
||||
|
||||
# Verify the firmware file exists before waiting for the Pico
|
||||
if [ ! -f "$UF2_FILE" ]; then
|
||||
echo "Error: $UF2_FILE not found. Run the build first:"
|
||||
echo " docker compose run --rm pico-build bash build.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wait for the Pico to appear in BOOTSEL mode
|
||||
echo "Waiting for Pico in BOOTSEL mode ($PICO_MOUNT)..."
|
||||
echo " -> Hold BOOTSEL and plug in the Pico via USB"
|
||||
while [ ! -d "$PICO_MOUNT" ]; do
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
# Copy the firmware to the Pico's USB mass storage
|
||||
echo "Pico detected. Copying $UF2_FILE..."
|
||||
cp "$UF2_FILE" "$PICO_MOUNT/"
|
||||
|
||||
# Wait for the Pico to unmount (it reboots automatically after receiving the .uf2)
|
||||
echo "Waiting for Pico to reboot..."
|
||||
while [ -d "$PICO_MOUNT" ]; do
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
echo "Done! Pico rebooted with new firmware."
|
||||
echo " -> Open a serial monitor: screen /dev/tty.usbmodem* 115200"
|
||||
13
src/APP_CLSW/cfg/APP_CLSW_cfg.c
Normal file
13
src/APP_CLSW/cfg/APP_CLSW_cfg.c
Normal file
@ -0,0 +1,13 @@
|
||||
/******************************************************************************
|
||||
* File: APP_CLSW_cfg.c
|
||||
* Component: APP_CLSW
|
||||
* Description: Configuration implementation for the APP_CLSW component.
|
||||
* Holds the actual configuration values consumed by
|
||||
* APP_CLSW_prg.c.
|
||||
*
|
||||
* Layer: Application - configuration
|
||||
*****************************************************************************/
|
||||
|
||||
#include "APP_CLSW_cfg.h"
|
||||
|
||||
/* Configuration definitions will go here */
|
||||
17
src/APP_CLSW/cfg/APP_CLSW_cfg.h
Normal file
17
src/APP_CLSW/cfg/APP_CLSW_cfg.h
Normal file
@ -0,0 +1,17 @@
|
||||
/******************************************************************************
|
||||
* File: APP_CLSW_cfg.h
|
||||
* Component: APP_CLSW
|
||||
* Description: Configuration header for the APP_CLSW component.
|
||||
* Declares configuration structures and constants that can be
|
||||
* edited to adapt the color switcher application behavior
|
||||
* (e.g., command strings, timing intervals, color sequences).
|
||||
*
|
||||
* Layer: Application - configuration
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef APP_CLSW_CFG_H
|
||||
#define APP_CLSW_CFG_H
|
||||
|
||||
/* Configuration constants and structure declarations will go here */
|
||||
|
||||
#endif /* APP_CLSW_CFG_H */
|
||||
46
src/APP_CLSW/inc/APP_CLSW.h
Normal file
46
src/APP_CLSW/inc/APP_CLSW.h
Normal file
@ -0,0 +1,46 @@
|
||||
/******************************************************************************
|
||||
* File: APP_CLSW.h
|
||||
* Component: APP_CLSW
|
||||
* Description: Public interface for the Application Color Switcher component.
|
||||
* Exposes the functions that the system orchestrator (SYS_ECU)
|
||||
* calls to run the color-switching application logic. This
|
||||
* component owns the "what to send" - it builds commands and
|
||||
* passes them down through the communication stack (HAL_COM)
|
||||
* to reach the host computer.
|
||||
*
|
||||
* Layer: Application
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef APP_CLSW_H
|
||||
#define APP_CLSW_H
|
||||
|
||||
/* STD_TYPES provides fixed-width typedefs (u8, u32) and the STD_tenuResult
|
||||
* enum used to report success/failure from the init function. */
|
||||
#include "STD_TYPES.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* PUBLIC API */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Initialize the color switcher application.
|
||||
*
|
||||
* Sets up all lower-layer dependencies needed by the application (currently
|
||||
* MCU_USB for USB-CDC communication). Must be called exactly once from
|
||||
* SYS_ECU before entering the main loop.
|
||||
*
|
||||
* @return STD_OK on success (all subsystems initialized),
|
||||
* STD_NOK if any subsystem initialization fails.
|
||||
*/
|
||||
STD_tenuResult APP_CLSW_enuInit(void);
|
||||
|
||||
/**
|
||||
* @brief Execute one iteration of the color switcher application logic.
|
||||
*
|
||||
* Called once per super-loop tick from SYS_ECU. Performs whatever the
|
||||
* application needs to do each cycle - for now, sends a test message
|
||||
* over USB-CDC to prove the communication stack is alive.
|
||||
*/
|
||||
void APP_CLSW_vRunnable(void);
|
||||
|
||||
#endif /* APP_CLSW_H */
|
||||
40
src/APP_CLSW/prg/APP_CLSW_prg.c
Normal file
40
src/APP_CLSW/prg/APP_CLSW_prg.c
Normal file
@ -0,0 +1,40 @@
|
||||
/******************************************************************************
|
||||
* File: APP_CLSW_prg.c
|
||||
* Component: APP_CLSW
|
||||
* Description: Program (implementation) file for the Application Color
|
||||
* Switcher. Contains the actual implementations of the public
|
||||
* functions declared in APP_CLSW.h. Communicates with the host
|
||||
* exclusively through HAL_COM - never touches MCU drivers
|
||||
* directly.
|
||||
*
|
||||
* Layer: Application
|
||||
*****************************************************************************/
|
||||
|
||||
#include "APP_CLSW.h"
|
||||
#include "APP_CLSW_priv.h"
|
||||
#include "APP_CLSW_cfg.h"
|
||||
|
||||
/* HAL_COM is the only communication interface this component uses.
|
||||
* It abstracts away whether bytes go over USB-CDC, hardware UART, or both. */
|
||||
#include "HAL_COM.h"
|
||||
|
||||
STD_tenuResult APP_CLSW_enuInit(void)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
|
||||
/* APP_CLSW has no internal state to set up yet. When color sequences,
|
||||
* state machines, or command tables are added, initialize them here.
|
||||
* The communication stack (HAL_COM, MCU_USB, etc.) is already
|
||||
* initialized by SYS_ECU before this function is called. */
|
||||
|
||||
return enuResultLoc;
|
||||
}
|
||||
|
||||
void APP_CLSW_vRunnable(void)
|
||||
{
|
||||
/* Proof-of-life: send a repeating test message to the host via the
|
||||
* transport-agnostic HAL_COM layer. This will be replaced with real
|
||||
* color-switching command logic once the communication stack is
|
||||
* verified end-to-end. */
|
||||
HAL_COM_enuSendBuffer((const u8 *)"this is a color switcher application!\r\n", 40U);
|
||||
}
|
||||
17
src/APP_CLSW/prg/APP_CLSW_priv.h
Normal file
17
src/APP_CLSW/prg/APP_CLSW_priv.h
Normal file
@ -0,0 +1,17 @@
|
||||
/******************************************************************************
|
||||
* File: APP_CLSW_priv.h
|
||||
* Component: APP_CLSW
|
||||
* Description: Private header for the APP_CLSW component.
|
||||
* Contains internal macros, helper declarations, and any
|
||||
* state/definitions that are only used inside this component.
|
||||
* Nothing declared here is exposed to external components.
|
||||
*
|
||||
* Layer: Application - internal use only
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef APP_CLSW_PRIV_H
|
||||
#define APP_CLSW_PRIV_H
|
||||
|
||||
/* Private declarations, internal macros and helpers will go here */
|
||||
|
||||
#endif /* APP_CLSW_PRIV_H */
|
||||
14
src/HAL_COM/cfg/HAL_COM_cfg.c
Normal file
14
src/HAL_COM/cfg/HAL_COM_cfg.c
Normal file
@ -0,0 +1,14 @@
|
||||
/******************************************************************************
|
||||
* File: HAL_COM_cfg.c
|
||||
* Component: HAL_COM
|
||||
* Description: Configuration implementation for the HAL_COM abstraction.
|
||||
* Holds the actual configuration values (transport selection
|
||||
* tables, timeout constants, buffer sizes) consumed by
|
||||
* HAL_COM_prg.c.
|
||||
*
|
||||
* Layer: HAL - configuration
|
||||
*****************************************************************************/
|
||||
|
||||
#include "HAL_COM_cfg.h"
|
||||
|
||||
/* Configuration definitions will go here */
|
||||
18
src/HAL_COM/cfg/HAL_COM_cfg.h
Normal file
18
src/HAL_COM/cfg/HAL_COM_cfg.h
Normal file
@ -0,0 +1,18 @@
|
||||
/******************************************************************************
|
||||
* File: HAL_COM_cfg.h
|
||||
* Component: HAL_COM
|
||||
* Description: Configuration header for the HAL_COM abstraction.
|
||||
* Selects which physical transport(s) HAL_COM should route
|
||||
* data through: MCU_UART, MCU_USB, or both (mirrored output).
|
||||
* Also exposes any timeout / buffer sizing parameters used by
|
||||
* the dispatch logic.
|
||||
*
|
||||
* Layer: HAL - configuration
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef HAL_COM_CFG_H
|
||||
#define HAL_COM_CFG_H
|
||||
|
||||
/* Configuration constants and transport selection macros will go here */
|
||||
|
||||
#endif /* HAL_COM_CFG_H */
|
||||
66
src/HAL_COM/inc/HAL_COM.h
Normal file
66
src/HAL_COM/inc/HAL_COM.h
Normal file
@ -0,0 +1,66 @@
|
||||
/******************************************************************************
|
||||
* File: HAL_COM.h
|
||||
* Component: HAL_COM
|
||||
* Description: Public interface for the HAL communication abstraction layer.
|
||||
* Provides a transport-agnostic API for sending and receiving
|
||||
* bytes to/from a host computer. Underneath, HAL_COM dispatches
|
||||
* to one or more MCU-level drivers (MCU_UART for the hardware
|
||||
* UART peripheral, MCU_USB for USB-CDC virtual serial) based on
|
||||
* the configuration in HAL_COM_cfg.h.
|
||||
*
|
||||
* Higher layers (e.g. APP_CLSW) should call only HAL_COM_* and
|
||||
* stay unaware of which physical transport is in use.
|
||||
*
|
||||
* Layer: HAL (hardware abstraction, one level above MCU drivers)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef HAL_COM_H
|
||||
#define HAL_COM_H
|
||||
|
||||
/* STD_TYPES provides fixed-width typedefs (u8, u32) and the STD_tenuResult
|
||||
* enum used to report success/failure from every function. */
|
||||
#include "STD_TYPES.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* PUBLIC API */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Initialize the HAL communication layer's own internal state.
|
||||
*
|
||||
* Does NOT initialize the underlying MCU drivers (MCU_USB, MCU_UART) -
|
||||
* SYS_ECU owns the init sequence and calls each driver's enuInit()
|
||||
* separately in the correct dependency order before calling this.
|
||||
*
|
||||
* @return STD_OK on success,
|
||||
* STD_NOK on failure.
|
||||
*/
|
||||
STD_tenuResult HAL_COM_enuInit(void);
|
||||
|
||||
/**
|
||||
* @brief Send a single byte through the active transport.
|
||||
*
|
||||
* Dispatches to MCU_USB_enuSendByte or MCU_UART_enuSendByte (or both)
|
||||
* depending on the transport selection configured in HAL_COM_cfg.h.
|
||||
*
|
||||
* @param u8Byte The byte to transmit.
|
||||
* @return STD_OK on success,
|
||||
* STD_NOK on transmit failure.
|
||||
*/
|
||||
STD_tenuResult HAL_COM_enuSendByte(u8 u8Byte);
|
||||
|
||||
/**
|
||||
* @brief Send a buffer of bytes through the active transport.
|
||||
*
|
||||
* Dispatches to MCU_USB_enuSendBuffer or MCU_UART_enuSendBuffer (or both)
|
||||
* depending on the transport selection configured in HAL_COM_cfg.h.
|
||||
*
|
||||
* @param pu8Data Pointer to the byte buffer to transmit. Must not be NULL.
|
||||
* @param u16Length Number of bytes to transmit.
|
||||
* @return STD_OK on success,
|
||||
* STD_NULL_POINTER_ERROR if pu8Data is NULL,
|
||||
* STD_NOK on transmit failure.
|
||||
*/
|
||||
STD_tenuResult HAL_COM_enuSendBuffer(const u8 *pu8Data, u16 u16Length);
|
||||
|
||||
#endif /* HAL_COM_H */
|
||||
56
src/HAL_COM/prg/HAL_COM_prg.c
Normal file
56
src/HAL_COM/prg/HAL_COM_prg.c
Normal file
@ -0,0 +1,56 @@
|
||||
/******************************************************************************
|
||||
* File: HAL_COM_prg.c
|
||||
* Component: HAL_COM
|
||||
* Description: Program (implementation) file for the HAL_COM abstraction.
|
||||
* Implements the public functions declared in HAL_COM.h by
|
||||
* dispatching to the MCU-level transport drivers (MCU_UART
|
||||
* and/or MCU_USB) according to the active configuration.
|
||||
*
|
||||
* Currently hardwired to MCU_USB. When MCU_UART is implemented,
|
||||
* the dispatch will be driven by a transport-selection macro
|
||||
* in HAL_COM_cfg.h.
|
||||
*
|
||||
* Layer: HAL
|
||||
*****************************************************************************/
|
||||
|
||||
#include "HAL_COM.h"
|
||||
#include "HAL_COM_priv.h"
|
||||
#include "HAL_COM_cfg.h"
|
||||
|
||||
/* MCU_USB is the active transport for now. When MCU_UART is ready,
|
||||
* a config macro will select which driver(s) to call here. */
|
||||
#include "MCU_USB.h"
|
||||
|
||||
STD_tenuResult HAL_COM_enuInit(void)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
|
||||
/* HAL_COM has no internal state to set up yet. When transport
|
||||
* selection logic or internal buffers are added, initialize them here.
|
||||
* The underlying MCU drivers are already initialized by SYS_ECU
|
||||
* before this function is called. */
|
||||
|
||||
return enuResultLoc;
|
||||
}
|
||||
|
||||
STD_tenuResult HAL_COM_enuSendByte(u8 u8Byte)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
|
||||
/* Dispatch to the active transport driver */
|
||||
enuResultLoc = MCU_USB_enuSendByte(u8Byte);
|
||||
|
||||
return enuResultLoc;
|
||||
}
|
||||
|
||||
STD_tenuResult HAL_COM_enuSendBuffer(const u8 *pu8Data, u16 u16Length)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
|
||||
/* Dispatch to the active transport driver.
|
||||
* Null-pointer validation is handled inside MCU_USB_enuSendBuffer
|
||||
* so we don't duplicate the check here. */
|
||||
enuResultLoc = MCU_USB_enuSendBuffer(pu8Data, u16Length);
|
||||
|
||||
return enuResultLoc;
|
||||
}
|
||||
17
src/HAL_COM/prg/HAL_COM_priv.h
Normal file
17
src/HAL_COM/prg/HAL_COM_priv.h
Normal file
@ -0,0 +1,17 @@
|
||||
/******************************************************************************
|
||||
* File: HAL_COM_priv.h
|
||||
* Component: HAL_COM
|
||||
* Description: Private header for the HAL_COM abstraction.
|
||||
* Contains internal macros, helper declarations, and any
|
||||
* state/definitions that are only used inside the component.
|
||||
* Nothing declared here is exposed to external components.
|
||||
*
|
||||
* Layer: HAL - internal use only
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef HAL_COM_PRIV_H
|
||||
#define HAL_COM_PRIV_H
|
||||
|
||||
/* Private declarations, internal macros and helpers will go here */
|
||||
|
||||
#endif /* HAL_COM_PRIV_H */
|
||||
14
src/MCU_UART/cfg/MCU_UART_cfg.c
Normal file
14
src/MCU_UART/cfg/MCU_UART_cfg.c
Normal file
@ -0,0 +1,14 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_UART_cfg.c
|
||||
* Component: MCU_UART
|
||||
* Description: Configuration implementation for the MCU_UART driver.
|
||||
* Holds the actual configuration values (baud rate, pin numbers,
|
||||
* UART instance selection, etc.) defined as constants or
|
||||
* configuration structures consumed by MCU_UART_prg.c.
|
||||
*
|
||||
* Layer: MCU (hardware abstraction) - configuration
|
||||
*****************************************************************************/
|
||||
|
||||
#include "MCU_UART_cfg.h"
|
||||
|
||||
/* Configuration definitions will go here */
|
||||
18
src/MCU_UART/cfg/MCU_UART_cfg.h
Normal file
18
src/MCU_UART/cfg/MCU_UART_cfg.h
Normal file
@ -0,0 +1,18 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_UART_cfg.h
|
||||
* Component: MCU_UART
|
||||
* Description: Configuration header for the MCU_UART driver.
|
||||
* Declares configuration structures and constants that can be
|
||||
* edited to adapt the UART driver to the specific hardware
|
||||
* setup (e.g., which UART instance, pin assignments, baud rate,
|
||||
* data bits, stop bits, parity).
|
||||
*
|
||||
* Layer: MCU (hardware abstraction) - configuration
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MCU_UART_CFG_H
|
||||
#define MCU_UART_CFG_H
|
||||
|
||||
/* Configuration constants and structure declarations will go here */
|
||||
|
||||
#endif /* MCU_UART_CFG_H */
|
||||
27
src/MCU_UART/inc/MCU_UART.h
Normal file
27
src/MCU_UART/inc/MCU_UART.h
Normal file
@ -0,0 +1,27 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_UART.h
|
||||
* Component: MCU_UART
|
||||
* Description: Public interface for the MCU UART driver component.
|
||||
* This header exposes the functions and types that other
|
||||
* components are allowed to use when interacting with the UART
|
||||
* peripheral on the RP2040 microcontroller.
|
||||
*
|
||||
* Layer: MCU (hardware abstraction)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MCU_UART_H
|
||||
#define MCU_UART_H
|
||||
#include "STD_TYPES.h"
|
||||
|
||||
typedef enum {
|
||||
MCU_UART_OK = 0,
|
||||
MCU_UART_ERROR,
|
||||
MCU_UART_TIMEOUT
|
||||
} MCU_UART_Status_t;
|
||||
|
||||
/* Public API declarations will go here */
|
||||
MCU_UART_Status_t MCU_UART_enuInit(void);
|
||||
MCU_UART_Status_t MCU_UART_enuSendByte(u8 byte);
|
||||
MCU_UART_Status_t MCU_UART_enuSendBuffer(const u8 *data,
|
||||
u16 length);
|
||||
#endif /* MCU_UART_H */
|
||||
17
src/MCU_UART/prg/MCU_UART_prg.c
Normal file
17
src/MCU_UART/prg/MCU_UART_prg.c
Normal file
@ -0,0 +1,17 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_UART_prg.c
|
||||
* Component: MCU_UART
|
||||
* Description: Program (implementation) file for the MCU_UART driver.
|
||||
* Contains the actual implementations of the public functions
|
||||
* declared in MCU_UART.h. This is where we call the Pico SDK
|
||||
* UART APIs to initialize the peripheral, transmit bytes, and
|
||||
* receive data.
|
||||
*
|
||||
* Layer: MCU (hardware abstraction)
|
||||
*****************************************************************************/
|
||||
|
||||
#include "MCU_UART.h"
|
||||
#include "MCU_UART_priv.h"
|
||||
#include "MCU_UART_cfg.h"
|
||||
|
||||
/* Function implementations will go here */
|
||||
18
src/MCU_UART/prg/MCU_UART_priv.h
Normal file
18
src/MCU_UART/prg/MCU_UART_priv.h
Normal file
@ -0,0 +1,18 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_UART_priv.h
|
||||
* Component: MCU_UART
|
||||
* Description: Private header for the MCU_UART driver.
|
||||
* Contains internal macros, helper declarations, and
|
||||
* register-level definitions that are only used inside the
|
||||
* component itself. Nothing declared here is exposed to
|
||||
* external components.
|
||||
*
|
||||
* Layer: MCU (hardware abstraction) - internal use only
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MCU_UART_PRIV_H
|
||||
#define MCU_UART_PRIV_H
|
||||
|
||||
/* Private declarations, internal macros and helpers will go here */
|
||||
|
||||
#endif /* MCU_UART_PRIV_H */
|
||||
14
src/MCU_USB/cfg/MCU_USB_cfg.c
Normal file
14
src/MCU_USB/cfg/MCU_USB_cfg.c
Normal file
@ -0,0 +1,14 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_USB_cfg.c
|
||||
* Component: MCU_USB
|
||||
* Description: Configuration implementation for the MCU_USB driver.
|
||||
* Holds the actual configuration values (timeouts, buffer
|
||||
* sizes, mode flags) defined as constants or configuration
|
||||
* structures consumed by MCU_USB_prg.c.
|
||||
*
|
||||
* Layer: MCU (hardware abstraction) - configuration
|
||||
*****************************************************************************/
|
||||
|
||||
#include "MCU_USB_cfg.h"
|
||||
|
||||
/* Configuration definitions will go here */
|
||||
86
src/MCU_USB/cfg/MCU_USB_cfg.h
Normal file
86
src/MCU_USB/cfg/MCU_USB_cfg.h
Normal file
@ -0,0 +1,86 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_USB_cfg.h
|
||||
* Component: MCU_USB
|
||||
* Description: Configuration header for the MCU_USB driver.
|
||||
* Declares configuration structures and constants that can be
|
||||
* edited to adapt the USB-CDC driver to the application's
|
||||
* needs (e.g., enable/disable connection wait, timeout values,
|
||||
* transmit buffer sizes).
|
||||
*
|
||||
* Layer: MCU (hardware abstraction) - configuration
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MCU_USB_CFG_H
|
||||
#define MCU_USB_CFG_H
|
||||
|
||||
/* STD_TYPES is needed for STD_TRUE / STD_FALSE and the u8/u16/u32 typedefs
|
||||
* used by the config values and timeout comparisons below. */
|
||||
#include "STD_TYPES.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* CONNECTION / INIT BEHAVIOR */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Whether MCU_USB_enuInit() should block until the host has opened
|
||||
* the CDC serial port before returning.
|
||||
*
|
||||
* Set to STD_TRUE to avoid losing the first bytes sent by the application
|
||||
* (the host needs ~1-2 s after power-up to enumerate and open the port).
|
||||
* Set to STD_FALSE for a fire-and-forget init that returns immediately -
|
||||
* useful if the firmware must not stall when no host is attached.
|
||||
*/
|
||||
#define MCU_USB_WAIT_FOR_CONNECTION MCU_USB_WAIT_FOR_CONNECTION_ENABLED
|
||||
|
||||
/**
|
||||
* @brief Maximum time (in milliseconds) to wait for the host to open
|
||||
* the CDC serial port during MCU_USB_enuInit().
|
||||
*
|
||||
* Only applies when MCU_USB_WAIT_FOR_CONNECTION is ENABLED. USB host
|
||||
* enumeration typically takes ~1-2 seconds, so 3000 ms gives a
|
||||
* comfortable margin. Set to 0 for an infinite wait (never times out).
|
||||
* This is separate from TRANSMIT/RECEIVE timeouts because connection
|
||||
* setup is a one-time event with different timing characteristics
|
||||
* than per-byte I/O operations.
|
||||
*/
|
||||
#define MCU_USB_CONNECTION_TIMEOUT_MS 3000U
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* TIMEOUTS */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Maximum time (in milliseconds) to wait for a single transmit
|
||||
* operation to complete before returning STD_NOK / STD_TIMEOUT.
|
||||
*
|
||||
* Applied to each transmit call in MCU_USB_prg.c. Prevents the driver
|
||||
* from blocking forever if the host-side serial port is closed mid-send
|
||||
* or the USB bus becomes unresponsive.
|
||||
*/
|
||||
#define MCU_USB_TRANSMIT_TIMEOUT_MS 1000U
|
||||
|
||||
/**
|
||||
* @brief Maximum time (in milliseconds) to wait for a byte to arrive on
|
||||
* the receive side before returning a timeout result.
|
||||
*
|
||||
* Applied to each blocking receive call. Prevents the driver from hanging
|
||||
* when the host is attached but simply not sending anything.
|
||||
*/
|
||||
#define MCU_USB_RECEIVE_TIMEOUT_MS 1000U
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* BUFFER SIZING */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Size (in bytes) of the software transmit buffer used by the
|
||||
* USB driver to queue outbound data.
|
||||
*
|
||||
* A larger buffer lets the application call MCU_USB_enuSendBuffer with
|
||||
* bigger chunks without having to wait for the USB peripheral to drain,
|
||||
* at the cost of more SRAM. 64 bytes matches the USB Full-Speed bulk
|
||||
* endpoint packet size, which is a convenient minimum for alignment.
|
||||
*/
|
||||
#define MCU_USB_TRANSMIT_BUFFER_SIZE 64U
|
||||
|
||||
#endif /* MCU_USB_CFG_H */
|
||||
70
src/MCU_USB/inc/MCU_USB.h
Normal file
70
src/MCU_USB/inc/MCU_USB.h
Normal file
@ -0,0 +1,70 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_USB.h
|
||||
* Component: MCU_USB
|
||||
* Description: Public interface for the MCU USB driver component.
|
||||
* This header exposes the functions and types that other
|
||||
* components are allowed to use to send and receive data over
|
||||
* the RP2040 USB-CDC (virtual serial port) interface. From the
|
||||
* host computer's perspective, the Pico appears as a regular
|
||||
* serial device (/dev/tty.usbmodem* on macOS, COMx on Windows).
|
||||
*
|
||||
* Layer: MCU (hardware abstraction)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MCU_USB_H
|
||||
#define MCU_USB_H
|
||||
|
||||
/* STD_TYPES brings in the fixed-width integer typedefs (u8, u32) and the
|
||||
* STD_tenuResult enum used to report success/failure from every function. */
|
||||
#include "STD_TYPES.h"
|
||||
|
||||
#define MCU_USB_WAIT_FOR_CONNECTION_DISABLED 0U
|
||||
#define MCU_USB_WAIT_FOR_CONNECTION_ENABLED 1U
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* PUBLIC API */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Initialize the USB-CDC interface.
|
||||
*
|
||||
* Sets up the RP2040 USB peripheral and the TinyUSB CDC device so the
|
||||
* board enumerates as a virtual serial port on the host. If the config
|
||||
* macro MCU_USB_WAIT_FOR_CONNECTION is MCU_USB_WAIT_FOR_CONNECTION_ENABLED,
|
||||
* this function blocks until the host opens the port (so early bytes are
|
||||
* not lost), subject to MCU_USB_CONNECTION_TIMEOUT_MS in MCU_USB_cfg.h.
|
||||
* If the timeout elapses before the host connects, returns STD_NOK.
|
||||
*
|
||||
* Must be called exactly once, before any Send/Receive function.
|
||||
*
|
||||
* @return STD_OK on success (USB-CDC initialized, host connected if wait enabled),
|
||||
* STD_NOK on init failure or connection timeout.
|
||||
*/
|
||||
STD_tenuResult MCU_USB_enuInit(void);
|
||||
|
||||
/**
|
||||
* @brief Send a single byte over USB-CDC.
|
||||
*
|
||||
* Blocks until the byte has been handed off to the USB stack or the
|
||||
* transmit timeout (MCU_USB_TRANSMIT_TIMEOUT_MS) elapses.
|
||||
*
|
||||
* @param u8Byte The byte to transmit.
|
||||
* @return STD_OK on success,
|
||||
* STD_NOK on transmit failure or timeout.
|
||||
*/
|
||||
STD_tenuResult MCU_USB_enuSendByte(u8 u8Byte);
|
||||
|
||||
/**
|
||||
* @brief Send a buffer of bytes over USB-CDC.
|
||||
*
|
||||
* Transmits u32Length bytes starting at pu8Data. Blocks until all bytes
|
||||
* are sent or the transmit timeout elapses. The buffer is not modified.
|
||||
*
|
||||
* @param pu8Data Pointer to the byte buffer to transmit. Must not be NULL.
|
||||
* @param u16Length Number of bytes to transmit.
|
||||
* @return STD_OK on success,
|
||||
* STD_NULL_POINTER_ERROR if pu8Data is NULL,
|
||||
* STD_NOK on transmit failure or timeout.
|
||||
*/
|
||||
STD_tenuResult MCU_USB_enuSendBuffer(const u8 *pu8Data, u16 u16Length);
|
||||
|
||||
#endif /* MCU_USB_H */
|
||||
119
src/MCU_USB/prg/MCU_USB_prg.c
Normal file
119
src/MCU_USB/prg/MCU_USB_prg.c
Normal file
@ -0,0 +1,119 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_USB_prg.c
|
||||
* Component: MCU_USB
|
||||
* Description: Program (implementation) file for the MCU_USB driver.
|
||||
* Contains the actual implementations of the public functions
|
||||
* declared in MCU_USB.h. Wraps the Pico SDK's USB-CDC / stdio
|
||||
* facilities to provide a simple send/receive API for the
|
||||
* virtual serial port exposed over the Pico's USB connection.
|
||||
*
|
||||
* Layer: MCU (hardware abstraction)
|
||||
*****************************************************************************/
|
||||
#include "STD_TYPES.h"
|
||||
|
||||
#include "pico/stdio_usb.h" /* stdio_usb_init(), stdio_usb_connected() */
|
||||
#include "pico/stdio.h" /* putchar_raw() - writes one byte into the stdio driver chain */
|
||||
#include "pico/time.h" /* absolute_time_t, make_timeout_time_ms(), time_reached() */
|
||||
#include "MCU_USB.h"
|
||||
#include "MCU_USB_priv.h"
|
||||
#include "MCU_USB_cfg.h"
|
||||
|
||||
STD_tenuResult MCU_USB_enuInit(void)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
STD_tBool bSdkInitSuccess = STD_FALSE;
|
||||
|
||||
/* Call the Pico SDK's USB-only stdio init. This brings up the TinyUSB
|
||||
* device stack, registers the USB-CDC stdio driver, and starts the
|
||||
* background task that services USB events. Returns true on success. */
|
||||
bSdkInitSuccess = (stdio_usb_init() != 0) ? STD_TRUE : STD_FALSE;
|
||||
|
||||
if (bSdkInitSuccess == STD_FALSE)
|
||||
{
|
||||
enuResultLoc = STD_NOK; /* Initialization failed */
|
||||
}else
|
||||
{
|
||||
#if MCU_USB_WAIT_FOR_CONNECTION == MCU_USB_WAIT_FOR_CONNECTION_ENABLED
|
||||
/* Wait for the host to open the CDC port, with a timeout. */
|
||||
absolute_time_t absTimeout = make_timeout_time_ms(MCU_USB_CONNECTION_TIMEOUT_MS);
|
||||
STD_tBool bHostOpen = STD_FALSE;
|
||||
STD_tBool bTimeoutReached = STD_FALSE;
|
||||
do
|
||||
{
|
||||
/* Yield for 10 ms between checks. This serves two purposes:
|
||||
* 1. Avoids burning 100% CPU on a busy-wait spin loop
|
||||
* 2. Gives the TinyUSB background task time to process USB
|
||||
* enumeration events — without yielding, the USB stack
|
||||
* may not advance and the host connection is delayed.
|
||||
* 10 ms matches the interval used by the Pico SDK's own
|
||||
* stdio_usb_init() connection-wait implementation. */
|
||||
sleep_ms(10);
|
||||
|
||||
/* Update status variables — avoid function calls in the
|
||||
* while condition for readability and debuggability */
|
||||
bHostOpen = (stdio_usb_connected() != 0) ? STD_TRUE : STD_FALSE;
|
||||
bTimeoutReached = (time_reached(absTimeout) != 0) ? STD_TRUE : STD_FALSE;
|
||||
} while ((bHostOpen == STD_FALSE) && (bTimeoutReached == STD_FALSE));
|
||||
|
||||
/* If we exited the loop because of timeout rather than a successful
|
||||
* connection, report failure so the caller knows the host never
|
||||
* opened the port within the configured window. */
|
||||
if (bHostOpen == STD_FALSE)
|
||||
{
|
||||
enuResultLoc = STD_NOK;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return enuResultLoc; /* Return the result */
|
||||
}
|
||||
|
||||
STD_tenuResult MCU_USB_enuSendByte(u8 u8Byte)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
|
||||
/* putchar_raw is the stdio framework's "push one byte into the driver
|
||||
* chain" primitive. It is declared as `int putchar_raw(int c)` because
|
||||
* the C stdio family uses EOF (-1) as a sentinel return value. Passing
|
||||
* u8Byte directly relies on the implicit widening conversion u8 -> int,
|
||||
* which is always safe (every u8 value fits in an int) and deliberately
|
||||
* keeps native C type names out of our code.
|
||||
*
|
||||
* Note on semantics: putchar_raw is fire-and-forget at this layer - it
|
||||
* queues the byte into the USB stdio driver and returns immediately.
|
||||
* The actual USB transfer happens in the TinyUSB background task. There
|
||||
* is no way to detect a transmit failure from this call, so we always
|
||||
* return STD_OK. When we need real delivery guarantees, we will upgrade
|
||||
* this to tud_cdc_write_char + tud_cdc_write_flush. */
|
||||
putchar_raw(u8Byte);
|
||||
|
||||
return enuResultLoc;
|
||||
}
|
||||
|
||||
STD_tenuResult MCU_USB_enuSendBuffer(const u8 *pu8Data, u16 u16Length)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
u16 u16IndexLoc;
|
||||
|
||||
/* Guard against null pointer dereference. On the RP2040 (Cortex-M0+),
|
||||
* reading address 0x00000000 does NOT fault — it silently reads from
|
||||
* the beginning of flash (the vector table), which means the firmware
|
||||
* would send garbage bytes over USB instead of crashing. The explicit
|
||||
* check catches the mistake at the source with a clear error code. */
|
||||
if (pu8Data == STD_NULL)
|
||||
{
|
||||
enuResultLoc = STD_NULL_POINTER_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Send each byte individually via putchar_raw. Same fire-and-forget
|
||||
* semantics as MCU_USB_enuSendByte — bytes are queued into the USB
|
||||
* stdio driver and transmitted by the TinyUSB background task.
|
||||
* No per-byte error detection is possible at this layer. */
|
||||
for (u16IndexLoc = 0U; u16IndexLoc < u16Length; u16IndexLoc++)
|
||||
{
|
||||
putchar_raw(pu8Data[u16IndexLoc]);
|
||||
}
|
||||
}
|
||||
|
||||
return enuResultLoc;
|
||||
}
|
||||
18
src/MCU_USB/prg/MCU_USB_priv.h
Normal file
18
src/MCU_USB/prg/MCU_USB_priv.h
Normal file
@ -0,0 +1,18 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_USB_priv.h
|
||||
* Component: MCU_USB
|
||||
* Description: Private header for the MCU_USB driver.
|
||||
* Contains internal macros, helper declarations, and any
|
||||
* lower-level definitions that are only used inside this
|
||||
* component itself. Nothing declared here is exposed to
|
||||
* external components.
|
||||
*
|
||||
* Layer: MCU (hardware abstraction) - internal use only
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MCU_USB_PRIV_H
|
||||
#define MCU_USB_PRIV_H
|
||||
|
||||
/* Private declarations, internal macros and helpers will go here */
|
||||
|
||||
#endif /* MCU_USB_PRIV_H */
|
||||
288
src/STD_TYPES/inc/STD_TYPES.h
Normal file
288
src/STD_TYPES/inc/STD_TYPES.h
Normal file
@ -0,0 +1,288 @@
|
||||
/******************************************************************************
|
||||
* File: STD_TYPES.h
|
||||
* Component: STD_TYPES
|
||||
* Description: Standard type definitions used across all components.
|
||||
* Provides project-wide fixed-width integer typedefs, boolean
|
||||
* types, and common return/status codes so that every module
|
||||
* speaks the same "type language".
|
||||
*
|
||||
* Layer: Library (shared by all layers)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef STD_TYPES_H
|
||||
#define STD_TYPES_H
|
||||
|
||||
/* Standard type definitions will go here */
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* ********************** PUBLIC TYPE DEFINITIONS **************************** */
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* @addtogroup 1-LIB_TYP_Types
|
||||
* @ingroup LIB_TYP
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* ************************ BASIC INTEGER TYPES ****************************** */
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* @brief 8-bit unsigned integer type
|
||||
* @details Size: 1 Byte, Range: [0 : 255]
|
||||
*/
|
||||
typedef unsigned char u8;
|
||||
#define STD_u8MIN_VALUE ((u8)0) /**< Minimum value for u8 type */
|
||||
#define STD_u8MAX_VALUE ((u8)0xFF) /**< Maximum value for u8 type */
|
||||
|
||||
/**
|
||||
* @brief 16-bit unsigned integer type
|
||||
* @details Size: 2 Bytes, Range: [0 : 65535]
|
||||
*/
|
||||
typedef unsigned short int u16;
|
||||
#define STD_u16MIN_VALUE ((u16)0) /**< Minimum value for u16 type */
|
||||
#define STD_u16MAX_VALUE ((u16)0xFFFFU) /**< Maximum value for u16 type */
|
||||
|
||||
/**
|
||||
* @brief 32-bit unsigned integer type
|
||||
* @details Size: 4 Bytes, Range: [0 : 4,294,967,295]
|
||||
*/
|
||||
typedef unsigned long int u32;
|
||||
#define STD_u32MIN_VALUE ((u32)0) /**< Minimum value for u32 type */
|
||||
#define STD_u32MAX_VALUE ((u32)0xFFFFFFFFU) /**< Maximum value for u32 type */
|
||||
|
||||
/**
|
||||
* @brief 64-bit unsigned integer type
|
||||
* @details Size: 8 Bytes, Range: [0 : 18,446,744,073,709,551,615]
|
||||
*/
|
||||
typedef unsigned long long u64;
|
||||
#define STD_u64MIN_VALUE ((u64)0) /**< Minimum value for u64 type */
|
||||
#define STD_u64MAX_VALUE ((u64)0xFFFFFFFFFFFFFFFFULL) /**< Maximum value for u64 type */
|
||||
|
||||
/**
|
||||
* @brief 8-bit signed integer type
|
||||
* @details Size: 1 Byte, Range: [-128 : 127]
|
||||
*/
|
||||
typedef signed char s8;
|
||||
#define STD_s8MIN_VALUE ((s8)-128) /**< Minimum value for s8 type */
|
||||
#define STD_s8MAX_VALUE ((s8)127) /**< Maximum value for s8 type */
|
||||
|
||||
/**
|
||||
* @brief 16-bit signed integer type
|
||||
* @details Size: 2 Bytes, Range: [-32,768 : 32,767]
|
||||
*/
|
||||
typedef signed short int s16;
|
||||
#define STD_s16MIN_VALUE ((s16)-32768) /**< Minimum value for s16 type */
|
||||
#define STD_s16MAX_VALUE ((s16)32767) /**< Maximum value for s16 type */
|
||||
|
||||
/**
|
||||
* @brief 32-bit signed integer type
|
||||
* @details Size: 4 Bytes, Range: [-2,147,483,648 : 2,147,483,647]
|
||||
*/
|
||||
typedef signed long int s32;
|
||||
#define STD_s32MIN_VALUE ((s32)-2147483648) /**< Minimum value for s32 type */
|
||||
#define STD_s32MAX_VALUE ((s32)2147483647) /**< Maximum value for s32 type */
|
||||
|
||||
/**
|
||||
* @brief 64-bit signed integer type
|
||||
* @details Size: 8 Bytes, Range: [-9,223,372,036,854,775,808 : 9,223,372,036,854,775,807]
|
||||
*/
|
||||
typedef signed long long s64;
|
||||
#define STD_s64MIN_VALUE ((s64)-9223372036854775807LL - 1LL) /**< Minimum value for s64 type */
|
||||
#define STD_s64MAX_VALUE ((s64)9223372036854775807LL) /**< Maximum value for s64 type */
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* ************************* RESULT TYPES *********************************** */
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* @brief Standard Result Type
|
||||
*
|
||||
* Enumeration for function return values indicating operation status
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
STD_OK = 0U, /**< Operation completed successfully */
|
||||
STD_INDEX_OUT_OF_RANGE_ERROR, /**< Array index out of bounds */
|
||||
STD_NULL_POINTER_ERROR, /**< Null pointer detected */
|
||||
STD_NOK /**< Operation failed */
|
||||
} STD_tenuResult;
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* ************************ CALLBACK TYPES ********************************** */
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* @brief Callback Function Type
|
||||
* @details Type definition for void callback functions
|
||||
*/
|
||||
typedef void (*STD_tpfCallbackFunc)(void);
|
||||
|
||||
/**
|
||||
* @brief Initialization Function Type
|
||||
* @details Type definition for initialization functions returning STD_tenuResult
|
||||
*/
|
||||
typedef STD_tenuResult (*STD_tpfInitFunc)(void);
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* ************************ BOOLEAN AND STATE TYPES ************************* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* @brief Boolean Type
|
||||
* @details Standard boolean enumeration
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
STD_FALSE, /**< False value */
|
||||
STD_TRUE /**< True value */
|
||||
} STD_tBool;
|
||||
|
||||
/**
|
||||
* @brief State Type
|
||||
* @details Standard state enumeration
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
STD_IDLE, /**< Idle state */
|
||||
STD_BUSY /**< Busy state */
|
||||
} STD_tenuState;
|
||||
|
||||
/**
|
||||
* @brief Compare State Type
|
||||
* @details Type for comparison results
|
||||
*/
|
||||
typedef u8 STD_tu8CMPstate;
|
||||
|
||||
/** @} */ // end of 1-LIB_TYP_Types group
|
||||
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* ************************* UTILITY MACROS ********************************* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* @addtogroup 2-LIB_TYP_Macros
|
||||
* @ingroup LIB_TYP
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @brief Null pointer definition */
|
||||
#define STD_NULL ((void *)0)
|
||||
|
||||
/**
|
||||
* @brief Constant qualifier macro
|
||||
* @details Removes const qualifier in unit test builds
|
||||
*/
|
||||
#ifdef UTD
|
||||
# define STD_CONST
|
||||
#else
|
||||
# define STD_CONST const
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Static qualifier macro
|
||||
* @details Removes static qualifier in unit test builds
|
||||
*/
|
||||
#ifdef UTD
|
||||
# define STD_STATIC
|
||||
#else
|
||||
# define STD_STATIC static
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Inline function macro
|
||||
* @details Controls function inlining based on build type
|
||||
*/
|
||||
#ifdef UTD
|
||||
# define STD_INLINE
|
||||
#else
|
||||
# define STD_INLINE __attribute__((always_inline)) inline
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Interrupt function macro
|
||||
* @details Marks functions as interrupts in non-test builds
|
||||
*/
|
||||
#ifdef UTD
|
||||
# define STD_INTERRUPT
|
||||
#else
|
||||
# define STD_INTERRUPT __attribute__((interrupt))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Non-inlined interrupt function macro
|
||||
* @details Marks functions as non-inlined interrupts in non-test builds. This prevents
|
||||
* the compiler from inlining interrupt service routines, which can be important
|
||||
* for debugging and maintaining consistent interrupt latency.
|
||||
*/
|
||||
#ifdef UTD
|
||||
# define STD_INTERRUPT_NO_INLINE
|
||||
#else
|
||||
# define STD_INTERRUPT_NO_INLINE __attribute__((interrupt, noinline))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Weak symbol macro
|
||||
* @details Marks symbols as weak in non-test builds
|
||||
*/
|
||||
#ifdef UTD
|
||||
# define STD_WEAK
|
||||
#else
|
||||
# define STD_WEAK __attribute__((weak))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Packed structure macro
|
||||
* @details Forces packed memory layout
|
||||
*/
|
||||
#define STD_PACKED __attribute__((packed))
|
||||
|
||||
/**
|
||||
* @brief 2-byte alignment macro
|
||||
* @details Forces 2-byte alignment for structures and variables.
|
||||
* Required for MLX16 architecture DMA buffers and optimal performance.
|
||||
*/
|
||||
#define STD_ALIGNED_2BYTE __attribute__((aligned(2)))
|
||||
|
||||
/**
|
||||
* @brief Switch case fallthrough macro
|
||||
* @details Explicitly indicates intentional fallthrough in switch statements.
|
||||
* Suppresses compiler warnings about implicit fallthrough between cases.
|
||||
* Use this when you intentionally omit a break statement.
|
||||
* @note Only available in GCC 7 and later
|
||||
*/
|
||||
#if __GNUC__ >= 7
|
||||
#define STD_FALLTHROUGH __attribute__((fallthrough))
|
||||
#else
|
||||
#define STD_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Static assertion macro
|
||||
* @details Provides compile-time assertions for configuration validation.
|
||||
* This macro abstracts the compiler-specific static assert mechanism
|
||||
* allowing for easy portability across different compilers.
|
||||
*
|
||||
* @param condition The condition to assert (must be compile-time constant)
|
||||
* @param message The error message to display if assertion fails
|
||||
*
|
||||
* @note For C11 compliant compilers, uses _Static_assert.
|
||||
* For older compilers, falls back to a compatible implementation.
|
||||
*/
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
|
||||
/* C11 and later: use standard _Static_assert */
|
||||
#define STD_STATIC_ASSERT(condition, message) _Static_assert(condition, message)
|
||||
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
|
||||
/* GCC 4.6+: use _Static_assert extension */
|
||||
#define STD_STATIC_ASSERT(condition, message) _Static_assert(condition, message)
|
||||
#else
|
||||
/* Fallback for older compilers: use array size trick */
|
||||
#define STD_STATIC_ASSERT(condition, message) \
|
||||
typedef char static_assertion_##__LINE__[(condition) ? 1 : -1]
|
||||
#endif
|
||||
|
||||
/** @} */ // end of 2-LIB_TYP_Macros group
|
||||
|
||||
#endif /* STD_TYPES_H */
|
||||
0
src/SYS_ECU/cfg/SYS_ECU_cfg
Normal file
0
src/SYS_ECU/cfg/SYS_ECU_cfg
Normal file
0
src/SYS_ECU/inc/SYS_ECU.h
Normal file
0
src/SYS_ECU/inc/SYS_ECU.h
Normal file
67
src/SYS_ECU/prg/SYS_ECU.c
Normal file
67
src/SYS_ECU/prg/SYS_ECU.c
Normal file
@ -0,0 +1,67 @@
|
||||
/******************************************************************************
|
||||
* File: SYS_ECU.c
|
||||
* Component: SYS_ECU
|
||||
* Description: System ECU entry point and main application loop.
|
||||
* This is the top-level orchestrator for the firmware: it owns
|
||||
* the initialization sequence (calling each component's enuInit
|
||||
* in dependency order) and the main super-loop scheduler that
|
||||
* dispatches runnables at the configured cadence.
|
||||
*
|
||||
* SYS_ECU never contains application logic itself - it only
|
||||
* calls into the components that do.
|
||||
*
|
||||
* Layer: System (top of the stack)
|
||||
*****************************************************************************/
|
||||
|
||||
#include "STD_TYPES.h"
|
||||
#include "pico/stdlib.h" /* sleep_ms */
|
||||
|
||||
/* Components initialized and scheduled by SYS_ECU, listed in
|
||||
* dependency order (drivers first, then HAL, then application). */
|
||||
#include "MCU_USB.h"
|
||||
#include "HAL_COM.h"
|
||||
#include "APP_CLSW.h"
|
||||
|
||||
/* ========================================================================= */
|
||||
/* INITIALIZATION SEQUENCE */
|
||||
/* ========================================================================= */
|
||||
|
||||
/**
|
||||
* @brief Initialize all components in dependency order.
|
||||
*
|
||||
* Each component's enuInit only sets up its own internal state.
|
||||
* The call order matters: MCU drivers first (they talk to hardware),
|
||||
* then HAL (depends on drivers), then application (depends on HAL).
|
||||
*/
|
||||
static void SYS_ECU_vInitAll(void)
|
||||
{
|
||||
/* MCU layer - hardware drivers */
|
||||
MCU_USB_enuInit();
|
||||
|
||||
/* HAL layer - abstractions over hardware */
|
||||
HAL_COM_enuInit();
|
||||
|
||||
/* Application layer */
|
||||
APP_CLSW_enuInit();
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* MAIN ENTRY */
|
||||
/* ========================================================================= */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Phase 1: bring up every component in the correct order */
|
||||
SYS_ECU_vInitAll();
|
||||
|
||||
/* Phase 2: super-loop scheduler - dispatches runnables each tick.
|
||||
* For the proof-of-life test, APP_CLSW_vRunnable sends a message
|
||||
* over USB-CDC once per second. The sleep_ms call here controls the
|
||||
* scheduler tick rate - it will eventually be replaced by a proper
|
||||
* timer-driven scheduler. */
|
||||
while (1)
|
||||
{
|
||||
APP_CLSW_vRunnable();
|
||||
sleep_ms(1000);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user