From 3687e48684c3532904a10273d6717e3b0d0bada2 Mon Sep 17 00:00:00 2001 From: Mohamed Salem Date: Sun, 12 Apr 2026 18:23:24 +0200 Subject: [PATCH] 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) --- .devcontainer/devcontainer.json | 64 ++++++ .gitignore | 9 + CLAUDE.md | 63 +++++- Dockerfile | 39 ++++ README.md | 35 ++- build.sh | 25 ++ cmake/CMakeLists.txt | 65 ++++++ cmake/cmake_config/mcu_config.cmake | 94 ++++++++ cmake/cmake_config/project_config.cmake | 38 ++++ cmake/cmake_config/sources_config.cmake | 61 +++++ cmake/pico_sdk_import.cmake | 121 ++++++++++ docker-compose.yml | 37 +++ flash.sh | 46 ++++ src/APP_CLSW/cfg/APP_CLSW_cfg.c | 13 ++ src/APP_CLSW/cfg/APP_CLSW_cfg.h | 17 ++ src/APP_CLSW/inc/APP_CLSW.h | 46 ++++ src/APP_CLSW/prg/APP_CLSW_prg.c | 40 ++++ src/APP_CLSW/prg/APP_CLSW_priv.h | 17 ++ src/HAL_COM/cfg/HAL_COM_cfg.c | 14 ++ src/HAL_COM/cfg/HAL_COM_cfg.h | 18 ++ src/HAL_COM/inc/HAL_COM.h | 66 ++++++ src/HAL_COM/prg/HAL_COM_prg.c | 56 +++++ src/HAL_COM/prg/HAL_COM_priv.h | 17 ++ src/MCU_UART/cfg/MCU_UART_cfg.c | 14 ++ src/MCU_UART/cfg/MCU_UART_cfg.h | 18 ++ src/MCU_UART/inc/MCU_UART.h | 27 +++ src/MCU_UART/prg/MCU_UART_prg.c | 17 ++ src/MCU_UART/prg/MCU_UART_priv.h | 18 ++ src/MCU_USB/cfg/MCU_USB_cfg.c | 14 ++ src/MCU_USB/cfg/MCU_USB_cfg.h | 86 +++++++ src/MCU_USB/inc/MCU_USB.h | 70 ++++++ src/MCU_USB/prg/MCU_USB_prg.c | 119 ++++++++++ src/MCU_USB/prg/MCU_USB_priv.h | 18 ++ src/STD_TYPES/inc/STD_TYPES.h | 288 ++++++++++++++++++++++++ src/SYS_ECU/cfg/SYS_ECU_cfg | 0 src/SYS_ECU/inc/SYS_ECU.h | 0 src/SYS_ECU/prg/SYS_ECU.c | 67 ++++++ 37 files changed, 1749 insertions(+), 8 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100755 build.sh create mode 100644 cmake/CMakeLists.txt create mode 100644 cmake/cmake_config/mcu_config.cmake create mode 100644 cmake/cmake_config/project_config.cmake create mode 100644 cmake/cmake_config/sources_config.cmake create mode 100644 cmake/pico_sdk_import.cmake create mode 100644 docker-compose.yml create mode 100755 flash.sh create mode 100644 src/APP_CLSW/cfg/APP_CLSW_cfg.c create mode 100644 src/APP_CLSW/cfg/APP_CLSW_cfg.h create mode 100644 src/APP_CLSW/inc/APP_CLSW.h create mode 100644 src/APP_CLSW/prg/APP_CLSW_prg.c create mode 100644 src/APP_CLSW/prg/APP_CLSW_priv.h create mode 100644 src/HAL_COM/cfg/HAL_COM_cfg.c create mode 100644 src/HAL_COM/cfg/HAL_COM_cfg.h create mode 100644 src/HAL_COM/inc/HAL_COM.h create mode 100644 src/HAL_COM/prg/HAL_COM_prg.c create mode 100644 src/HAL_COM/prg/HAL_COM_priv.h create mode 100644 src/MCU_UART/cfg/MCU_UART_cfg.c create mode 100644 src/MCU_UART/cfg/MCU_UART_cfg.h create mode 100644 src/MCU_UART/inc/MCU_UART.h create mode 100644 src/MCU_UART/prg/MCU_UART_prg.c create mode 100644 src/MCU_UART/prg/MCU_UART_priv.h create mode 100644 src/MCU_USB/cfg/MCU_USB_cfg.c create mode 100644 src/MCU_USB/cfg/MCU_USB_cfg.h create mode 100644 src/MCU_USB/inc/MCU_USB.h create mode 100644 src/MCU_USB/prg/MCU_USB_prg.c create mode 100644 src/MCU_USB/prg/MCU_USB_priv.h create mode 100644 src/STD_TYPES/inc/STD_TYPES.h create mode 100644 src/SYS_ECU/cfg/SYS_ECU_cfg create mode 100644 src/SYS_ECU/inc/SYS_ECU.h create mode 100644 src/SYS_ECU/prg/SYS_ECU.c diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..bb5ee5d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -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" + ] + } + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c19231 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/CLAUDE.md b/CLAUDE.md index 939d84a..df81a09 100644 --- a/CLAUDE.md +++ b/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` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..aff7f48 --- /dev/null +++ b/Dockerfile @@ -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 diff --git a/README.md b/README.md index d0323d8..92a3fc8 100644 --- a/README.md +++ b/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 +``` \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..3c803b9 --- /dev/null +++ b/build.sh @@ -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)" diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt new file mode 100644 index 0000000..f2393d5 --- /dev/null +++ b/cmake/CMakeLists.txt @@ -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 /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}) diff --git a/cmake/cmake_config/mcu_config.cmake b/cmake/cmake_config/mcu_config.cmake new file mode 100644 index 0000000..a67d56e --- /dev/null +++ b/cmake/cmake_config/mcu_config.cmake @@ -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() diff --git a/cmake/cmake_config/project_config.cmake b/cmake/cmake_config/project_config.cmake new file mode 100644 index 0000000..99b53ae --- /dev/null +++ b/cmake/cmake_config/project_config.cmake @@ -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) diff --git a/cmake/cmake_config/sources_config.cmake b/cmake/cmake_config/sources_config.cmake new file mode 100644 index 0000000..7687f3a --- /dev/null +++ b/cmake/cmake_config/sources_config.cmake @@ -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 +) diff --git a/cmake/pico_sdk_import.cmake b/cmake/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/cmake/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/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d196836 --- /dev/null +++ b/docker-compose.yml @@ -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 diff --git a/flash.sh b/flash.sh new file mode 100755 index 0000000..d16ef8a --- /dev/null +++ b/flash.sh @@ -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" \ No newline at end of file diff --git a/src/APP_CLSW/cfg/APP_CLSW_cfg.c b/src/APP_CLSW/cfg/APP_CLSW_cfg.c new file mode 100644 index 0000000..02c065e --- /dev/null +++ b/src/APP_CLSW/cfg/APP_CLSW_cfg.c @@ -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 */ \ No newline at end of file diff --git a/src/APP_CLSW/cfg/APP_CLSW_cfg.h b/src/APP_CLSW/cfg/APP_CLSW_cfg.h new file mode 100644 index 0000000..91faffe --- /dev/null +++ b/src/APP_CLSW/cfg/APP_CLSW_cfg.h @@ -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 */ \ No newline at end of file diff --git a/src/APP_CLSW/inc/APP_CLSW.h b/src/APP_CLSW/inc/APP_CLSW.h new file mode 100644 index 0000000..402c76e --- /dev/null +++ b/src/APP_CLSW/inc/APP_CLSW.h @@ -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 */ \ No newline at end of file diff --git a/src/APP_CLSW/prg/APP_CLSW_prg.c b/src/APP_CLSW/prg/APP_CLSW_prg.c new file mode 100644 index 0000000..739e50b --- /dev/null +++ b/src/APP_CLSW/prg/APP_CLSW_prg.c @@ -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); +} \ No newline at end of file diff --git a/src/APP_CLSW/prg/APP_CLSW_priv.h b/src/APP_CLSW/prg/APP_CLSW_priv.h new file mode 100644 index 0000000..1720ffc --- /dev/null +++ b/src/APP_CLSW/prg/APP_CLSW_priv.h @@ -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 */ \ No newline at end of file diff --git a/src/HAL_COM/cfg/HAL_COM_cfg.c b/src/HAL_COM/cfg/HAL_COM_cfg.c new file mode 100644 index 0000000..d5b3295 --- /dev/null +++ b/src/HAL_COM/cfg/HAL_COM_cfg.c @@ -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 */ diff --git a/src/HAL_COM/cfg/HAL_COM_cfg.h b/src/HAL_COM/cfg/HAL_COM_cfg.h new file mode 100644 index 0000000..28f1aa7 --- /dev/null +++ b/src/HAL_COM/cfg/HAL_COM_cfg.h @@ -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 */ diff --git a/src/HAL_COM/inc/HAL_COM.h b/src/HAL_COM/inc/HAL_COM.h new file mode 100644 index 0000000..cc5fc0c --- /dev/null +++ b/src/HAL_COM/inc/HAL_COM.h @@ -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 */ \ No newline at end of file diff --git a/src/HAL_COM/prg/HAL_COM_prg.c b/src/HAL_COM/prg/HAL_COM_prg.c new file mode 100644 index 0000000..f0c2fd1 --- /dev/null +++ b/src/HAL_COM/prg/HAL_COM_prg.c @@ -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; +} diff --git a/src/HAL_COM/prg/HAL_COM_priv.h b/src/HAL_COM/prg/HAL_COM_priv.h new file mode 100644 index 0000000..e1a69a8 --- /dev/null +++ b/src/HAL_COM/prg/HAL_COM_priv.h @@ -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 */ diff --git a/src/MCU_UART/cfg/MCU_UART_cfg.c b/src/MCU_UART/cfg/MCU_UART_cfg.c new file mode 100644 index 0000000..10dc6c6 --- /dev/null +++ b/src/MCU_UART/cfg/MCU_UART_cfg.c @@ -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 */ \ No newline at end of file diff --git a/src/MCU_UART/cfg/MCU_UART_cfg.h b/src/MCU_UART/cfg/MCU_UART_cfg.h new file mode 100644 index 0000000..5c222be --- /dev/null +++ b/src/MCU_UART/cfg/MCU_UART_cfg.h @@ -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 */ \ No newline at end of file diff --git a/src/MCU_UART/inc/MCU_UART.h b/src/MCU_UART/inc/MCU_UART.h new file mode 100644 index 0000000..2cbef32 --- /dev/null +++ b/src/MCU_UART/inc/MCU_UART.h @@ -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 */ \ No newline at end of file diff --git a/src/MCU_UART/prg/MCU_UART_prg.c b/src/MCU_UART/prg/MCU_UART_prg.c new file mode 100644 index 0000000..bcaaa71 --- /dev/null +++ b/src/MCU_UART/prg/MCU_UART_prg.c @@ -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 */ \ No newline at end of file diff --git a/src/MCU_UART/prg/MCU_UART_priv.h b/src/MCU_UART/prg/MCU_UART_priv.h new file mode 100644 index 0000000..9bcfb44 --- /dev/null +++ b/src/MCU_UART/prg/MCU_UART_priv.h @@ -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 */ \ No newline at end of file diff --git a/src/MCU_USB/cfg/MCU_USB_cfg.c b/src/MCU_USB/cfg/MCU_USB_cfg.c new file mode 100644 index 0000000..1efe6f9 --- /dev/null +++ b/src/MCU_USB/cfg/MCU_USB_cfg.c @@ -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 */ diff --git a/src/MCU_USB/cfg/MCU_USB_cfg.h b/src/MCU_USB/cfg/MCU_USB_cfg.h new file mode 100644 index 0000000..6fc02f7 --- /dev/null +++ b/src/MCU_USB/cfg/MCU_USB_cfg.h @@ -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 */ diff --git a/src/MCU_USB/inc/MCU_USB.h b/src/MCU_USB/inc/MCU_USB.h new file mode 100644 index 0000000..9a47d6f --- /dev/null +++ b/src/MCU_USB/inc/MCU_USB.h @@ -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 */ diff --git a/src/MCU_USB/prg/MCU_USB_prg.c b/src/MCU_USB/prg/MCU_USB_prg.c new file mode 100644 index 0000000..00109c6 --- /dev/null +++ b/src/MCU_USB/prg/MCU_USB_prg.c @@ -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; +} \ No newline at end of file diff --git a/src/MCU_USB/prg/MCU_USB_priv.h b/src/MCU_USB/prg/MCU_USB_priv.h new file mode 100644 index 0000000..591d8e8 --- /dev/null +++ b/src/MCU_USB/prg/MCU_USB_priv.h @@ -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 */ diff --git a/src/STD_TYPES/inc/STD_TYPES.h b/src/STD_TYPES/inc/STD_TYPES.h new file mode 100644 index 0000000..f9f6ff3 --- /dev/null +++ b/src/STD_TYPES/inc/STD_TYPES.h @@ -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 */ diff --git a/src/SYS_ECU/cfg/SYS_ECU_cfg b/src/SYS_ECU/cfg/SYS_ECU_cfg new file mode 100644 index 0000000..e69de29 diff --git a/src/SYS_ECU/inc/SYS_ECU.h b/src/SYS_ECU/inc/SYS_ECU.h new file mode 100644 index 0000000..e69de29 diff --git a/src/SYS_ECU/prg/SYS_ECU.c b/src/SYS_ECU/prg/SYS_ECU.c new file mode 100644 index 0000000..b9d26fc --- /dev/null +++ b/src/SYS_ECU/prg/SYS_ECU.c @@ -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); + } +} \ No newline at end of file