From b49c7f60bb21d14b2c3b6140be59cd0d45cfc358 Mon Sep 17 00:00:00 2001 From: Mohamed Salem Date: Mon, 13 Apr 2026 03:32:18 +0200 Subject: [PATCH] Add WS2812B LED support via PIO with rainbow and color commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MCU_PIO: generic PIO driver with config-driven program loading, function-pointer init callbacks, blocking put, and DMA async put. ws2812.pio written from scratch — 800 kHz, 10 cycles/bit, side-set. HAL_LED: pixel buffer with intensity scaling, RGB byte order for RP2040-Zero WS2812 variant. SetColor immediately pushes the strip. APP_CLSW: rainbow HSV hue rotation on startup (auto-mode). Color commands (red/green/blue/off/rainbow) stop the rainbow and set the LED to a static color. Integer-only HSV-to-RGB conversion. CMake: added hardware_pio link and pico_generate_pio_header for ws2812.pio compilation. SYS_ECU init sequence updated. --- cmake/cmake_config/mcu_config.cmake | 12 ++- cmake/cmake_config/sources_config.cmake | 10 ++ src/APP_CLSW/cfg/APP_CLSW_cfg.h | 8 ++ src/APP_CLSW/prg/APP_CLSW_prg.c | 137 +++++++++++++++++++----- src/APP_CLSW/prg/APP_CLSW_priv.h | 6 +- src/HAL_LED/cfg/HAL_LED_cfg.c | 19 ++++ src/HAL_LED/cfg/HAL_LED_cfg.h | 46 ++++++++ src/HAL_LED/inc/HAL_LED.h | 70 ++++++++++++ src/HAL_LED/prg/HAL_LED_prg.c | 117 ++++++++++++++++++++ src/HAL_LED/prg/HAL_LED_priv.h | 51 +++++++++ src/MCU_PIO/cfg/MCU_PIO_cfg.c | 53 +++++++++ src/MCU_PIO/cfg/MCU_PIO_cfg.h | 40 +++++++ src/MCU_PIO/inc/MCU_PIO.h | 107 ++++++++++++++++++ src/MCU_PIO/pio/ws2812.pio | 73 +++++++++++++ src/MCU_PIO/prg/MCU_PIO_prg.c | 115 ++++++++++++++++++++ src/MCU_PIO/prg/MCU_PIO_priv.h | 34 ++++++ src/SYS_ECU/prg/SYS_ECU.c | 4 + 17 files changed, 871 insertions(+), 31 deletions(-) create mode 100644 src/HAL_LED/cfg/HAL_LED_cfg.c create mode 100644 src/HAL_LED/cfg/HAL_LED_cfg.h create mode 100644 src/HAL_LED/inc/HAL_LED.h create mode 100644 src/HAL_LED/prg/HAL_LED_prg.c create mode 100644 src/HAL_LED/prg/HAL_LED_priv.h create mode 100644 src/MCU_PIO/cfg/MCU_PIO_cfg.c create mode 100644 src/MCU_PIO/cfg/MCU_PIO_cfg.h create mode 100644 src/MCU_PIO/inc/MCU_PIO.h create mode 100644 src/MCU_PIO/pio/ws2812.pio create mode 100644 src/MCU_PIO/prg/MCU_PIO_prg.c create mode 100644 src/MCU_PIO/prg/MCU_PIO_priv.h diff --git a/cmake/cmake_config/mcu_config.cmake b/cmake/cmake_config/mcu_config.cmake index b7a1dc9..d1d7837 100644 --- a/cmake/cmake_config/mcu_config.cmake +++ b/cmake/cmake_config/mcu_config.cmake @@ -57,13 +57,23 @@ 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) - # - hardware_dma: DMA controller API (used by MCU_UART non-blocking TX) + # - hardware_dma: DMA controller API (used by MCU_UART + MCU_PIO async) + # - hardware_pio: PIO state machine API (used by MCU_PIO for WS2812 etc.) target_link_libraries(${target} PRIVATE pico_stdlib hardware_uart hardware_dma + hardware_pio ) + # Generate C header from PIO assembly programs. The generated header + # provides the compiled program struct (ws2812_program) and the + # ws2812_program_init() helper used by MCU_PIO_cfg.c. The header is + # placed in the build directory and automatically added to the + # target's include path. + pico_generate_pio_header(${target} + ${PROJECT_ROOT_DIR}/src/MCU_PIO/pio/ws2812.pio) + # 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. diff --git a/cmake/cmake_config/sources_config.cmake b/cmake/cmake_config/sources_config.cmake index 7687f3a..7f91e45 100644 --- a/cmake/cmake_config/sources_config.cmake +++ b/cmake/cmake_config/sources_config.cmake @@ -39,6 +39,11 @@ set(PROJECT_INCLUDE_DIRS ${PROJECT_ROOT_DIR}/src/MCU_UART/prg ${PROJECT_ROOT_DIR}/src/MCU_UART/cfg + # MCU layer - PIO peripheral driver for programmable I/O state machines + ${PROJECT_ROOT_DIR}/src/MCU_PIO/inc + ${PROJECT_ROOT_DIR}/src/MCU_PIO/prg + ${PROJECT_ROOT_DIR}/src/MCU_PIO/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 @@ -51,6 +56,11 @@ set(PROJECT_INCLUDE_DIRS ${PROJECT_ROOT_DIR}/src/HAL_COM/prg ${PROJECT_ROOT_DIR}/src/HAL_COM/cfg + # HAL layer - LED strip/pixel abstraction over PIO-based WS2812 driver + ${PROJECT_ROOT_DIR}/src/HAL_LED/inc + ${PROJECT_ROOT_DIR}/src/HAL_LED/prg + ${PROJECT_ROOT_DIR}/src/HAL_LED/cfg + # Application layer - color switcher application logic ${PROJECT_ROOT_DIR}/src/APP_CLSW/inc ${PROJECT_ROOT_DIR}/src/APP_CLSW/prg diff --git a/src/APP_CLSW/cfg/APP_CLSW_cfg.h b/src/APP_CLSW/cfg/APP_CLSW_cfg.h index 5918948..3887116 100644 --- a/src/APP_CLSW/cfg/APP_CLSW_cfg.h +++ b/src/APP_CLSW/cfg/APP_CLSW_cfg.h @@ -20,4 +20,12 @@ /** @brief HAL_COM channel used for host communication. */ #define APP_CLSW_COM_CHANNEL ((u8)HAL_COM_CHANNEL_0) +/** @brief HAL_LED instance for the onboard LED. + * Maps to HAL_LED_INSTANCE_ONBOARD (0). */ +#define APP_CLSW_LED_INSTANCE 0U + +/** @brief Default LED brightness (0-255). 128 = half brightness to avoid + * blinding at close range and reduce current draw. */ +#define APP_CLSW_LED_INTENSITY 128U + #endif /* APP_CLSW_CFG_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 index 6ca66e0..e81d69f 100644 --- a/src/APP_CLSW/prg/APP_CLSW_prg.c +++ b/src/APP_CLSW/prg/APP_CLSW_prg.c @@ -2,14 +2,12 @@ * File: APP_CLSW_prg.c * Component: APP_CLSW * Description: Color switcher application logic. Receives commands from the - * host via HAL_COM, parses them, and responds with confirmation. + * host via HAL_COM, parses them, and drives the onboard LED + * via HAL_LED. * - * Supported commands: - * "red" → "Color set to: RED\r\n" - * "green" → "Color set to: GREEN\r\n" - * "blue" → "Color set to: BLUE\r\n" - * "help" → prints available commands - * other → "Unknown command: \r\n" + * On startup, runs a rainbow HSV hue rotation (auto-mode). + * When a color command is received ("red", "green", "blue"), + * the rainbow stops and the LED is set to the requested color. * * Layer: Application *****************************************************************************/ @@ -19,6 +17,7 @@ #include "APP_CLSW_cfg.h" #include "HAL_COM.h" +#include "HAL_LED.h" /* ------------------------------------------------------------------------ */ /* INTERNAL STATE */ @@ -45,12 +44,6 @@ static STD_tBool bStrEqual(const u8 *pu8A, const u8 *pu8B) { bResultLoc = STD_FALSE; } - - /* Only advance if we haven't found a mismatch yet, OR if we - * need to reach the end of both strings to confirm equality. - * Actually: always advance — we need to check all characters. - * But once we found a mismatch, we know the result. We still - * loop to avoid early return (single exit point). */ u8IndexLoc++; } @@ -64,7 +57,6 @@ static void vSendString(const u8 *pu8Str) { u16 u16LenLoc = 0U; - /* Calculate string length */ while (pu8Str[u16LenLoc] != 0U) { u16LenLoc++; @@ -73,35 +65,115 @@ static void vSendString(const u8 *pu8Str) HAL_COM_enuSendBuffer(APP_CLSW_COM_CHANNEL, pu8Str, u16LenLoc); } +/** + * @brief Convert HSV color to RGB using integer-only math. + * + * Standard 6-sector HSV-to-RGB algorithm. All arithmetic uses u16/u32 + * intermediates to avoid overflow — no floating point. + * + * @param u16Hue Hue angle in degrees (0-359). + * @param u8Sat Saturation (0-255). 255 = fully saturated. + * @param u8Val Value / brightness (0-255). 255 = full brightness. + * @param pu8R Output: red channel (0-255). + * @param pu8G Output: green channel (0-255). + * @param pu8B Output: blue channel (0-255). + */ +static void vHsvToRgb(u16 u16Hue, u8 u8Sat, u8 u8Val, + u8 *pu8R, u8 *pu8G, u8 *pu8B) +{ + u8 u8SectorLoc; + u16 u16RemainderLoc; + u8 u8PLoc; /* chroma minimum */ + u8 u8QLoc; /* descending transition */ + u8 u8TLoc; /* ascending transition */ + + /* p = V * (255 - S) / 255 */ + u8PLoc = (u8)(((u16)u8Val * (255U - (u16)u8Sat)) / 255U); + + /* Which 60-degree sector of the hue wheel (0-5) */ + u8SectorLoc = (u8)(u16Hue / 60U); + + /* Remainder within the sector, scaled to 0-255 range. + * (hue % 60) * 255 / 60 gives a 0-255 ramp within the sector. */ + u16RemainderLoc = (u16)(((u32)(u16Hue % 60U) * 255U) / 60U); + + /* q = V * (255 - S * remainder / 255) / 255 — descending edge */ + u8QLoc = (u8)(((u16)u8Val * (255U - ((u16)u8Sat * u16RemainderLoc / 255U))) / 255U); + + /* t = V * (255 - S * (255 - remainder) / 255) / 255 — ascending edge */ + u8TLoc = (u8)(((u16)u8Val * (255U - ((u16)u8Sat * (255U - u16RemainderLoc) / 255U))) / 255U); + + /* Map sector to RGB channels */ + if (u8SectorLoc == 0U) + { + *pu8R = u8Val; *pu8G = u8TLoc; *pu8B = u8PLoc; + } + else if (u8SectorLoc == 1U) + { + *pu8R = u8QLoc; *pu8G = u8Val; *pu8B = u8PLoc; + } + else if (u8SectorLoc == 2U) + { + *pu8R = u8PLoc; *pu8G = u8Val; *pu8B = u8TLoc; + } + else if (u8SectorLoc == 3U) + { + *pu8R = u8PLoc; *pu8G = u8QLoc; *pu8B = u8Val; + } + else if (u8SectorLoc == 4U) + { + *pu8R = u8TLoc; *pu8G = u8PLoc; *pu8B = u8Val; + } + else + { + *pu8R = u8Val; *pu8G = u8PLoc; *pu8B = u8QLoc; + } +} + /** * @brief Process a complete command string. * Called when a delimiter (\r or \n) is received. */ static void vProcessCommand(void) { - /* Null-terminate the command buffer */ strState.au8CmdBuffer[strState.u8CmdIndex] = 0U; - /* Skip empty commands (just pressing Enter) */ if (strState.u8CmdIndex == 0U) { - /* Do nothing — no command to process */ + /* Empty command — do nothing */ } else if (bStrEqual(strState.au8CmdBuffer, (const u8 *)"red") == STD_TRUE) { + strState.bAutoMode = STD_FALSE; + HAL_LED_enuSetColor(APP_CLSW_LED_INSTANCE, 0U, 255U, 0U, 0U, APP_CLSW_LED_INTENSITY); vSendString((const u8 *)"Color set to: RED\r\n"); } else if (bStrEqual(strState.au8CmdBuffer, (const u8 *)"green") == STD_TRUE) { + strState.bAutoMode = STD_FALSE; + HAL_LED_enuSetColor(APP_CLSW_LED_INSTANCE, 0U, 0U, 255U, 0U, APP_CLSW_LED_INTENSITY); vSendString((const u8 *)"Color set to: GREEN\r\n"); } else if (bStrEqual(strState.au8CmdBuffer, (const u8 *)"blue") == STD_TRUE) { + strState.bAutoMode = STD_FALSE; + HAL_LED_enuSetColor(APP_CLSW_LED_INSTANCE, 0U, 0U, 0U, 255U, APP_CLSW_LED_INTENSITY); vSendString((const u8 *)"Color set to: BLUE\r\n"); } + else if (bStrEqual(strState.au8CmdBuffer, (const u8 *)"rainbow") == STD_TRUE) + { + strState.bAutoMode = STD_TRUE; + vSendString((const u8 *)"Rainbow mode enabled\r\n"); + } + else if (bStrEqual(strState.au8CmdBuffer, (const u8 *)"off") == STD_TRUE) + { + strState.bAutoMode = STD_FALSE; + HAL_LED_enuSetColor(APP_CLSW_LED_INSTANCE, 0U, 0U, 0U, 0U, 0U); + vSendString((const u8 *)"LED off\r\n"); + } else if (bStrEqual(strState.au8CmdBuffer, (const u8 *)"help") == STD_TRUE) { - vSendString((const u8 *)"Available commands: red, green, blue, help\r\n"); + vSendString((const u8 *)"Commands: red, green, blue, rainbow, off, help\r\n"); } else { @@ -110,7 +182,6 @@ static void vProcessCommand(void) vSendString((const u8 *)"\r\n"); } - /* Reset the buffer for the next command */ strState.u8CmdIndex = 0U; } @@ -122,8 +193,9 @@ STD_tenuResult APP_CLSW_enuInit(void) { STD_tenuResult enuResultLoc = STD_OK; - /* Clear the command buffer state */ strState.u8CmdIndex = 0U; + strState.u16Hue = 0U; + strState.bAutoMode = STD_TRUE; return enuResultLoc; } @@ -138,29 +210,24 @@ void APP_CLSW_vRunnable(void) u8 u8ByteLoc = 0U; STD_tenuResult enuRxResultLoc = STD_OK; - /* Check if the host sent any data */ + /* --- Process incoming commands --- */ bDataAvailableLoc = HAL_COM_bIsRxDataAvailable(APP_CLSW_COM_CHANNEL); while (bDataAvailableLoc == STD_TRUE) { - /* Read one byte */ enuRxResultLoc = HAL_COM_enuReadByte(APP_CLSW_COM_CHANNEL, &u8ByteLoc); if (enuRxResultLoc == STD_OK) { - /* Echo the character back so the user sees what they typed */ HAL_COM_enuSendByte(APP_CLSW_COM_CHANNEL, u8ByteLoc); - /* Check for command delimiter */ if ((u8ByteLoc == (u8)'\r') || (u8ByteLoc == (u8)'\n')) { - /* Send a newline for display, then process the command */ vSendString((const u8 *)"\r\n"); vProcessCommand(); } else { - /* Accumulate into the command buffer (truncate if full) */ if (strState.u8CmdIndex < (APP_CLSW_CMD_BUFFER_SIZE - 1U)) { strState.au8CmdBuffer[strState.u8CmdIndex] = u8ByteLoc; @@ -169,7 +236,21 @@ void APP_CLSW_vRunnable(void) } } - /* Check for more data */ bDataAvailableLoc = HAL_COM_bIsRxDataAvailable(APP_CLSW_COM_CHANNEL); } -} + + /* --- Rainbow auto-mode: cycle hue each tick --- */ + if (strState.bAutoMode == STD_TRUE) + { + u8 u8RLoc = 0U; + u8 u8GLoc = 0U; + u8 u8BLoc = 0U; + + vHsvToRgb(strState.u16Hue, 255U, 255U, &u8RLoc, &u8GLoc, &u8BLoc); + + HAL_LED_enuSetColor(APP_CLSW_LED_INSTANCE, 0U, + u8RLoc, u8GLoc, u8BLoc, APP_CLSW_LED_INTENSITY); + + strState.u16Hue = (strState.u16Hue + 1U) % 360U; + } +} \ 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 index 17a94ea..b874a84 100644 --- a/src/APP_CLSW/prg/APP_CLSW_priv.h +++ b/src/APP_CLSW/prg/APP_CLSW_priv.h @@ -27,8 +27,10 @@ */ typedef struct { - u8 au8CmdBuffer[APP_CLSW_CMD_BUFFER_SIZE]; /**< Accumulated command bytes */ - u8 u8CmdIndex; /**< Next write position */ + u8 au8CmdBuffer[APP_CLSW_CMD_BUFFER_SIZE]; /**< Accumulated command bytes */ + u8 u8CmdIndex; /**< Next write position */ + u16 u16Hue; /**< Current hue for rainbow (0-359) */ + STD_tBool bAutoMode; /**< STD_TRUE = rainbow, STD_FALSE = manual color */ } APP_CLSW_tstrState; #endif /* APP_CLSW_PRIV_H */ \ No newline at end of file diff --git a/src/HAL_LED/cfg/HAL_LED_cfg.c b/src/HAL_LED/cfg/HAL_LED_cfg.c new file mode 100644 index 0000000..2e90e93 --- /dev/null +++ b/src/HAL_LED/cfg/HAL_LED_cfg.c @@ -0,0 +1,19 @@ +/****************************************************************************** + * File: HAL_LED_cfg.c + * Component: HAL_LED + * Description: Configuration array definition for the LED abstraction. + * + * Layer: HAL - configuration + *****************************************************************************/ + +#include "HAL_LED.h" +#include "HAL_LED_cfg.h" + +const HAL_LED_tstrConfig HAL_LED_astrConfig[HAL_LED_NUM_INSTANCES] = +{ + [HAL_LED_INSTANCE_ONBOARD] = + { + .u8NumLeds = HAL_LED_ONBOARD_NUM_LEDS, + .u8PioInstance = HAL_LED_ONBOARD_PIO_INSTANCE, + }, +}; \ No newline at end of file diff --git a/src/HAL_LED/cfg/HAL_LED_cfg.h b/src/HAL_LED/cfg/HAL_LED_cfg.h new file mode 100644 index 0000000..06b1b47 --- /dev/null +++ b/src/HAL_LED/cfg/HAL_LED_cfg.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * File: HAL_LED_cfg.h + * Component: HAL_LED + * Description: Configuration header for the LED abstraction layer. + * Defines LED strip instances, maximum strip length, + * and per-instance settings. + * + * Layer: HAL - configuration + *****************************************************************************/ + +#ifndef HAL_LED_CFG_H +#define HAL_LED_CFG_H + +#include "STD_TYPES.h" + +/* ------------------------------------------------------------------------ */ +/* INSTANCE ENUMERATION */ +/* ------------------------------------------------------------------------ */ + +typedef enum +{ + HAL_LED_INSTANCE_ONBOARD = 0U, /**< Onboard WS2812B on RP2040-Zero */ + HAL_LED_NUM_INSTANCES +} HAL_LED_tenuInstance; + +/* ------------------------------------------------------------------------ */ +/* BUFFER SIZING */ +/* ------------------------------------------------------------------------ */ + +/** @brief Maximum number of LEDs any single instance can support. + * Determines the pixel buffer size in the control struct. + * Increase when adding longer strips. */ +#define HAL_LED_MAX_LEDS_PER_INSTANCE 1U + +/* ------------------------------------------------------------------------ */ +/* INSTANCE 0 (ONBOARD) CONFIGURATION */ +/* ------------------------------------------------------------------------ */ + +/** @brief Number of LEDs in the onboard strip (just 1 on the RP2040-Zero). */ +#define HAL_LED_ONBOARD_NUM_LEDS 1U + +/** @brief MCU_PIO instance index for the onboard LED. + * Maps to MCU_PIO_INSTANCE_WS2812 (defined in MCU_PIO_cfg.h). */ +#define HAL_LED_ONBOARD_PIO_INSTANCE 0U + +#endif /* HAL_LED_CFG_H */ \ No newline at end of file diff --git a/src/HAL_LED/inc/HAL_LED.h b/src/HAL_LED/inc/HAL_LED.h new file mode 100644 index 0000000..536194e --- /dev/null +++ b/src/HAL_LED/inc/HAL_LED.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * File: HAL_LED.h + * Component: HAL_LED + * Description: Public interface for the LED abstraction layer. + * Manages a pixel buffer for WS2812-style addressable LEDs + * and pushes color data through MCU_PIO. Supports indexed + * LED strips with per-pixel intensity scaling. + * + * SetColor sets one LED's color and immediately pushes the + * entire strip to the hardware — no separate Update call needed. + * + * Layer: HAL (hardware abstraction, one level above MCU drivers) + *****************************************************************************/ + +#ifndef HAL_LED_H +#define HAL_LED_H + +#include "STD_TYPES.h" + +/* ------------------------------------------------------------------------ */ +/* CONFIGURATION STRUCTURE */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Per-instance LED strip/array configuration. + * + * One entry per LED strip, stored in HAL_LED_astrConfig[]. + * The array index is the instance parameter in all public API calls. + */ +typedef struct +{ + u8 u8NumLeds; /**< Number of LEDs in this strip (1 for single LED) */ + u8 u8PioInstance; /**< MCU_PIO config index for the data output */ +} HAL_LED_tstrConfig; + +/* ------------------------------------------------------------------------ */ +/* PUBLIC API */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Initialize the LED abstraction layer. + * + * Clears the internal pixel buffer to all-off (black). Does NOT init + * MCU_PIO — SYS_ECU must call MCU_PIO_enuInit() before this. + * + * @return STD_OK on success. + */ +STD_tenuResult HAL_LED_enuInit(void); + +/** + * @brief Set the color of a single LED and push the entire strip. + * + * Scales each color channel by u8Intensity (0-255), stores the result + * in the internal pixel buffer, then immediately pushes all LEDs in + * this strip to the PIO state machine. + * + * @param u8Instance HAL_LED config instance. + * @param u8LedIndex LED position in the strip (0-based). + * @param u8Red Red intensity (0-255, before scaling). + * @param u8Green Green intensity (0-255, before scaling). + * @param u8Blue Blue intensity (0-255, before scaling). + * @param u8Intensity Global brightness scaler (0-255). 255 = full. + * @return STD_OK on success, + * STD_INDEX_OUT_OF_RANGE_ERROR if u8LedIndex >= u8NumLeds. + */ +STD_tenuResult HAL_LED_enuSetColor(u8 u8Instance, u8 u8LedIndex, + u8 u8Red, u8 u8Green, u8 u8Blue, + u8 u8Intensity); + +#endif /* HAL_LED_H */ \ No newline at end of file diff --git a/src/HAL_LED/prg/HAL_LED_prg.c b/src/HAL_LED/prg/HAL_LED_prg.c new file mode 100644 index 0000000..272911e --- /dev/null +++ b/src/HAL_LED/prg/HAL_LED_prg.c @@ -0,0 +1,117 @@ +/****************************************************************************** + * File: HAL_LED_prg.c + * Component: HAL_LED + * Description: LED abstraction implementation. Manages a pixel buffer with + * intensity scaling and pushes color data to the hardware via + * MCU_PIO. SetColor updates one LED and immediately pushes the + * entire strip — no separate Update call needed. + * + * Layer: HAL + *****************************************************************************/ + +#include "HAL_LED.h" +#include "HAL_LED_priv.h" +#include "HAL_LED_cfg.h" + +#include "MCU_PIO.h" + +/* ------------------------------------------------------------------------ */ +/* RUNTIME STATE */ +/* ------------------------------------------------------------------------ */ + +static HAL_LED_tstrControl strControl; + +/* ------------------------------------------------------------------------ */ +/* INTERNAL HELPERS */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Push the entire pixel buffer for one instance to the PIO FIFO. + * + * Iterates through the pixel buffer, packs each pixel into a 32-bit + * GRB word (left-justified: G in [31:24], R in [23:16], B in [15:8], + * bits [7:0] unused), and sends it via MCU_PIO_vPutBlocking. + * + * @param u8Instance HAL_LED instance index. + */ +static void vPushStrip(u8 u8Instance) +{ + u8 u8PioInstLoc = HAL_LED_astrConfig[u8Instance].u8PioInstance; + u8 u8NumLedsLoc = HAL_LED_astrConfig[u8Instance].u8NumLeds; + u8 u8LedLoc; + + for (u8LedLoc = 0U; u8LedLoc < u8NumLedsLoc; u8LedLoc++) + { + HAL_LED_tstrPixel *pstrPixLoc = &strControl.astrPixels[u8Instance][u8LedLoc]; + + /* Pack RGB into bits [31:8] of the 32-bit word. + * The WS2812 variant on the RP2040-Zero uses RGB byte order + * (red first, green second, blue third) rather than the standard + * GRB. Bits [7:0] are padding (shifted out but ignored). */ + u32 u32RgbLoc = ((u32)pstrPixLoc->u8Red << 24U) + | ((u32)pstrPixLoc->u8Green << 16U) + | ((u32)pstrPixLoc->u8Blue << 8U); + + MCU_PIO_vPutBlocking(u8PioInstLoc, u32RgbLoc); + } +} + +/* ========================================================================= */ +/* INIT */ +/* ========================================================================= */ + +STD_tenuResult HAL_LED_enuInit(void) +{ + STD_tenuResult enuResultLoc = STD_OK; + u8 u8InstLoc; + u8 u8LedLoc; + + /* Clear all pixels to off (black) */ + for (u8InstLoc = 0U; u8InstLoc < (u8)HAL_LED_NUM_INSTANCES; u8InstLoc++) + { + for (u8LedLoc = 0U; u8LedLoc < HAL_LED_astrConfig[u8InstLoc].u8NumLeds; u8LedLoc++) + { + strControl.astrPixels[u8InstLoc][u8LedLoc].u8Green = 0U; + strControl.astrPixels[u8InstLoc][u8LedLoc].u8Red = 0U; + strControl.astrPixels[u8InstLoc][u8LedLoc].u8Blue = 0U; + } + } + + return enuResultLoc; +} + +/* ========================================================================= */ +/* SET COLOR */ +/* ========================================================================= */ + +STD_tenuResult HAL_LED_enuSetColor(u8 u8Instance, u8 u8LedIndex, + u8 u8Red, u8 u8Green, u8 u8Blue, + u8 u8Intensity) +{ + STD_tenuResult enuResultLoc = STD_OK; + u8 u8NumLedsLoc = HAL_LED_astrConfig[u8Instance].u8NumLeds; + + if (u8LedIndex >= u8NumLedsLoc) + { + enuResultLoc = STD_INDEX_OUT_OF_RANGE_ERROR; + } + else + { + /* Scale each channel by intensity: (channel * intensity) / 255. + * Use u16 intermediate to avoid overflow (255 * 255 = 65025, + * which fits in u16 but not u8). */ + u8 u8ScaledRLoc = (u8)(((u16)u8Red * (u16)u8Intensity) / 255U); + u8 u8ScaledGLoc = (u8)(((u16)u8Green * (u16)u8Intensity) / 255U); + u8 u8ScaledBLoc = (u8)(((u16)u8Blue * (u16)u8Intensity) / 255U); + + /* Store the scaled pixel in the buffer */ + strControl.astrPixels[u8Instance][u8LedIndex].u8Red = u8ScaledRLoc; + strControl.astrPixels[u8Instance][u8LedIndex].u8Green = u8ScaledGLoc; + strControl.astrPixels[u8Instance][u8LedIndex].u8Blue = u8ScaledBLoc; + + /* Immediately push the entire strip to the hardware */ + vPushStrip(u8Instance); + } + + return enuResultLoc; +} \ No newline at end of file diff --git a/src/HAL_LED/prg/HAL_LED_priv.h b/src/HAL_LED/prg/HAL_LED_priv.h new file mode 100644 index 0000000..169757e --- /dev/null +++ b/src/HAL_LED/prg/HAL_LED_priv.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * File: HAL_LED_priv.h + * Component: HAL_LED + * Description: Private header — pixel struct, control struct with the + * pixel buffer, and extern config array declaration. + * + * Layer: HAL - internal use only + *****************************************************************************/ + +#ifndef HAL_LED_PRIV_H +#define HAL_LED_PRIV_H + +#include "HAL_LED.h" +#include "HAL_LED_cfg.h" + +extern const HAL_LED_tstrConfig HAL_LED_astrConfig[HAL_LED_NUM_INSTANCES]; + +/* ------------------------------------------------------------------------ */ +/* PIXEL STRUCTURE */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Per-LED color data in GRB order (matching WS2812 protocol). + * + * Stored after intensity scaling has been applied, so these values + * are the actual brightnesses sent to the hardware. + */ +typedef struct +{ + u8 u8Green; + u8 u8Red; + u8 u8Blue; +} HAL_LED_tstrPixel; + +/* ------------------------------------------------------------------------ */ +/* CONTROL STRUCTURE */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Internal runtime state for all HAL_LED instances. + * + * astrPixels is a 2D array: [instance][led_index]. Each pixel holds + * the intensity-scaled GRB values ready to be packed into 32-bit words + * for the PIO FIFO. + */ +typedef struct +{ + HAL_LED_tstrPixel astrPixels[HAL_LED_NUM_INSTANCES][HAL_LED_MAX_LEDS_PER_INSTANCE]; +} HAL_LED_tstrControl; + +#endif /* HAL_LED_PRIV_H */ \ No newline at end of file diff --git a/src/MCU_PIO/cfg/MCU_PIO_cfg.c b/src/MCU_PIO/cfg/MCU_PIO_cfg.c new file mode 100644 index 0000000..39c6d34 --- /dev/null +++ b/src/MCU_PIO/cfg/MCU_PIO_cfg.c @@ -0,0 +1,53 @@ +/****************************************************************************** + * File: MCU_PIO_cfg.c + * Component: MCU_PIO + * Description: Configuration array definition for the PIO driver. + * Wires each PIO instance to its compiled program and + * program-specific init function. The WS2812 init wrapper + * adapts the auto-generated ws2812_program_init() signature + * to match the generic MCU_PIO_tpfProgramInit callback type. + * + * Layer: MCU (hardware abstraction) - configuration + *****************************************************************************/ + +#include "MCU_PIO.h" +#include "MCU_PIO_cfg.h" + +/* Auto-generated header from ws2812.pio — provides ws2812_program struct + * and ws2812_program_init() helper. Generated at build time by + * pico_generate_pio_header() in mcu_config.cmake. */ +#include "ws2812.pio.h" + +/* ------------------------------------------------------------------------ */ +/* WS2812 INIT WRAPPER */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Adapts ws2812_program_init() to the MCU_PIO_tpfProgramInit + * signature by hardcoding the WS2812 bit frequency (800 kHz). + * + * The auto-generated ws2812_program_init() takes an extra `float freq` + * parameter that is not part of the generic callback type. This wrapper + * fills it in so the generic driver can call it without knowing about + * WS2812 specifics. + */ +static void vWs2812Init(PIO pstrPio, u8 u8Sm, u8 u8Pin, u32 u32Offset) +{ + ws2812_program_init(pstrPio, (u32)u8Sm, (u32)u32Offset, (u32)u8Pin, 800000.0f); +} + +/* ------------------------------------------------------------------------ */ +/* CONFIGURATION ARRAY */ +/* ------------------------------------------------------------------------ */ + +const MCU_PIO_tstrConfig MCU_PIO_astrConfig[MCU_PIO_NUM_INSTANCES] = +{ + [MCU_PIO_INSTANCE_WS2812] = + { + .pstrPio = pio0, /* use PIO block 0 */ + .u8Sm = 0U, /* state machine 0 within pio0 */ + .u8Pin = MCU_PIO_WS2812_PIN, + .pstrProgram = &ws2812_program, + .pfProgramInit = vWs2812Init, + }, +}; diff --git a/src/MCU_PIO/cfg/MCU_PIO_cfg.h b/src/MCU_PIO/cfg/MCU_PIO_cfg.h new file mode 100644 index 0000000..f676eda --- /dev/null +++ b/src/MCU_PIO/cfg/MCU_PIO_cfg.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * File: MCU_PIO_cfg.h + * Component: MCU_PIO + * Description: Configuration header for the generic PIO driver. + * Defines which PIO programs are loaded and on which + * state machines / GPIO pins they operate. + * + * Layer: MCU (hardware abstraction) - configuration + *****************************************************************************/ + +#ifndef MCU_PIO_CFG_H +#define MCU_PIO_CFG_H + +#include "STD_TYPES.h" + +/* ------------------------------------------------------------------------ */ +/* INSTANCE ENUMERATION */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Enumeration of configured PIO program instances. + * + * Each entry represents one PIO program running on one state machine. + * The enumerator value is the array index into MCU_PIO_astrConfig[]. + */ +typedef enum +{ + MCU_PIO_INSTANCE_WS2812 = 0U, /**< WS2812 LED driver on GP16 */ + MCU_PIO_NUM_INSTANCES +} MCU_PIO_tenuInstance; + +/* ------------------------------------------------------------------------ */ +/* WS2812 INSTANCE CONFIGURATION */ +/* ------------------------------------------------------------------------ */ + +/** @brief GPIO pin for the WS2812B data line. + * GP16 is the onboard WS2812B on the Waveshare RP2040-Zero. */ +#define MCU_PIO_WS2812_PIN 16U + +#endif /* MCU_PIO_CFG_H */ diff --git a/src/MCU_PIO/inc/MCU_PIO.h b/src/MCU_PIO/inc/MCU_PIO.h new file mode 100644 index 0000000..600a9ec --- /dev/null +++ b/src/MCU_PIO/inc/MCU_PIO.h @@ -0,0 +1,107 @@ +/****************************************************************************** + * File: MCU_PIO.h + * Component: MCU_PIO + * Description: Public interface for the generic PIO driver component. + * Abstracts the RP2040's Programmable I/O hardware — loading + * PIO programs into instruction memory, configuring state + * machines, and pushing data through the TX FIFO. + * + * The driver is program-agnostic: each config entry holds a + * pointer to a compiled PIO program and a function pointer + * for the program-specific state machine init. WS2812 is one + * such program; future PIO uses (custom protocols, etc.) + * plug in the same way with zero driver code changes. + * + * Layer: MCU (hardware abstraction) + *****************************************************************************/ + +#ifndef MCU_PIO_H +#define MCU_PIO_H + +#include "STD_TYPES.h" +#include "hardware/pio.h" + +/* ------------------------------------------------------------------------ */ +/* PROGRAM INIT FUNCTION POINTER TYPE */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Callback type for program-specific state machine configuration. + * + * Each PIO program has its own pin mapping, shift config, clock divider, + * etc. The generic MCU_PIO driver calls this function after loading the + * program into instruction memory. The function must fully configure and + * enable the state machine. + * + * @param pstrPio PIO instance (pio0 or pio1). + * @param u8Sm State machine index (0-3) within that PIO instance. + * @param u8Pin GPIO pin from the config struct. + * @param u32Offset Instruction memory offset where the program was loaded. + */ +typedef void (*MCU_PIO_tpfProgramInit)(PIO pstrPio, u8 u8Sm, u8 u8Pin, u32 u32Offset); + +/* ------------------------------------------------------------------------ */ +/* CONFIGURATION STRUCTURE */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Per-instance PIO configuration. + * + * One entry per PIO program/state-machine pair, stored in + * MCU_PIO_astrConfig[]. The array index is used as the instance + * parameter in all public API calls. + */ +typedef struct +{ + PIO pstrPio; /**< PIO block: pio0 or pio1 */ + u8 u8Sm; /**< State machine index (0-3) */ + u8 u8Pin; /**< GPIO pin used by this program */ + const pio_program_t *pstrProgram; /**< Pointer to compiled PIO program */ + MCU_PIO_tpfProgramInit pfProgramInit; /**< Program-specific SM config callback */ +} MCU_PIO_tstrConfig; + +/* ------------------------------------------------------------------------ */ +/* PUBLIC API */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Initialize all configured PIO instances. + * + * For each config entry: loads the PIO program into the instruction + * memory of the selected PIO block, then calls the program-specific + * init callback to configure the state machine (pins, shift, clock). + * + * SYS_ECU calls this once during the init sequence. + * + * @return STD_OK on success, STD_NOK if any instance fails. + */ +STD_tenuResult MCU_PIO_enuInit(void); + +/** + * @brief Push a 32-bit word into a PIO state machine's TX FIFO (blocking). + * + * Blocks until the FIFO has space, then writes the data. For WS2812, + * each call sends one pixel (24-bit GRB left-justified in the 32-bit word). + * + * @param u8Instance Config array index (MCU_PIO_tenuInstance). + * @param u32Data The 32-bit value to push. + */ +void MCU_PIO_vPutBlocking(u8 u8Instance, u32 u32Data); + +/** + * @brief Push a buffer of 32-bit words to the TX FIFO via DMA (non-blocking). + * + * Starts a DMA transfer from pu32Data into the PIO TX FIFO. Returns + * immediately. The DMA channel was pre-claimed during Init. + * + * The caller MUST keep pu32Data valid until the transfer completes. + * + * @param u8Instance Config array index. + * @param pu32Data Pointer to array of 32-bit words. Must not be NULL. + * @param u16Count Number of 32-bit words to transfer. + * @return STD_OK transfer started, + * STD_NULL_POINTER_ERROR if pu32Data is NULL. + */ +STD_tenuResult MCU_PIO_enuPutBufferAsync(u8 u8Instance, const u32 *pu32Data, u16 u16Count); + +#endif /* MCU_PIO_H */ diff --git a/src/MCU_PIO/pio/ws2812.pio b/src/MCU_PIO/pio/ws2812.pio new file mode 100644 index 0000000..2c60c3d --- /dev/null +++ b/src/MCU_PIO/pio/ws2812.pio @@ -0,0 +1,73 @@ +; ============================================================================ +; ws2812.pio — WS2812B NZR protocol driver for RP2040 PIO +; ============================================================================ +; Written from scratch for the color_switcher project. +; +; WS2812 protocol: each bit is a fixed-length pulse (~1.25 us at 800 kHz). +; "1" bit: long high (~875 ns) + short low (~375 ns) +; "0" bit: short high (~375 ns) + long low (~875 ns) +; +; Timing: 10 PIO cycles per bit at 8 MHz PIO clock (sysclk 125 MHz / 15.625). +; "1" bit: high 7 cycles, low 3 cycles +; "0" bit: high 3 cycles, low 7 cycles +; +; Data: 24-bit GRB, MSB first, left-justified in 32-bit FIFO word. +; Autopull at 24 bits, shift left. Side-set pin drives the data line. +; ============================================================================ + +.program ws2812 +.side_set 1 + +.wrap_target +bitloop: + out x, 1 side 0 [2] ; shift 1 bit into X, drive LOW, 3 cycles total + jmp !x do_zero side 1 [1] ; if bit=0 jump, drive HIGH, 2 cycles total +do_one: + jmp bitloop side 1 [4] ; bit=1: stay HIGH 5 more (total HIGH=7), loop +do_zero: + nop side 0 [4] ; bit=0: drive LOW 5 more (total LOW from next out=3+5) +.wrap + +; ============================================================================ +; C SDK helper — emitted into ws2812.pio.h by pico_generate_pio_header. +; Configures the state machine for WS2812 output on a single GPIO pin. +; +; Parameters: +; pio — PIO instance (pio0 or pio1) +; sm — state machine index (0-3) +; offset — instruction memory offset where the program was loaded +; pin — GPIO pin connected to the WS2812 data line +; freq — bit frequency in Hz (800000.0f for standard WS2812) +; ============================================================================ + +% c-sdk { +#include "hardware/clocks.h" + +static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq) +{ + /* Configure the GPIO pin for PIO output */ + pio_gpio_init(pio, pin); + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); + + /* Get the default config (sets wrap points and sideset count from the .pio) */ + pio_sm_config c = ws2812_program_get_default_config(offset); + + /* Side-set pin = the WS2812 data line */ + sm_config_set_sideset_pins(&c, pin); + + /* Shift left, autopull at 24 bits (GRB = 3 bytes). + * Data must be left-justified in the 32-bit FIFO word (bits [31:8]). */ + sm_config_set_out_shift(&c, false, true, 24); + + /* Join both FIFOs into a single 8-entry TX FIFO for deeper buffering */ + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + /* Clock divider: 10 PIO cycles per bit, so PIO freq = bit_freq * 10. + * clkdiv = sysclk / (freq * 10). E.g., 125 MHz / 8 MHz = 15.625 */ + sm_config_set_clkdiv(&c, clock_get_hz(clk_sys) / (freq * 10.0f)); + + /* Apply the config and start the state machine */ + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +%} \ No newline at end of file diff --git a/src/MCU_PIO/prg/MCU_PIO_prg.c b/src/MCU_PIO/prg/MCU_PIO_prg.c new file mode 100644 index 0000000..3867048 --- /dev/null +++ b/src/MCU_PIO/prg/MCU_PIO_prg.c @@ -0,0 +1,115 @@ +/****************************************************************************** + * File: MCU_PIO_prg.c + * Component: MCU_PIO + * Description: Generic PIO driver implementation. Loads PIO programs into + * instruction memory, calls program-specific init callbacks, + * claims DMA channels, and provides blocking + async writes. + * + * Layer: MCU (hardware abstraction) + *****************************************************************************/ + +#include "MCU_PIO.h" +#include "MCU_PIO_priv.h" +#include "MCU_PIO_cfg.h" + +#include "hardware/pio.h" +#include "hardware/dma.h" + +/* ------------------------------------------------------------------------ */ +/* RUNTIME STATE */ +/* ------------------------------------------------------------------------ */ + +static MCU_PIO_tstrControl strControl; + +/* ========================================================================= */ +/* INIT */ +/* ========================================================================= */ + +STD_tenuResult MCU_PIO_enuInit(void) +{ + STD_tenuResult enuResultLoc = STD_OK; + u8 u8IndexLoc; + + for (u8IndexLoc = 0U; u8IndexLoc < (u8)MCU_PIO_NUM_INSTANCES; u8IndexLoc++) + { + const MCU_PIO_tstrConfig *pstrCfgLoc = &MCU_PIO_astrConfig[u8IndexLoc]; + + /* Load the PIO program into instruction memory */ + u32 u32OffsetLoc = (u32)pio_add_program(pstrCfgLoc->pstrPio, + pstrCfgLoc->pstrProgram); + + /* Call the program-specific init callback to configure the SM */ + pstrCfgLoc->pfProgramInit(pstrCfgLoc->pstrPio, + pstrCfgLoc->u8Sm, + pstrCfgLoc->u8Pin, + u32OffsetLoc); + + /* Claim a DMA channel for async FIFO writes. Reserved for the + * lifetime of the application — never released. true = panic + * if no channels available (misconfiguration, not a runtime error). */ + strControl.as8DmaChannel[u8IndexLoc] = (s8)dma_claim_unused_channel(true); + } + + return enuResultLoc; +} + +/* ========================================================================= */ +/* PUT BLOCKING (SINGLE WORD) */ +/* ========================================================================= */ + +void MCU_PIO_vPutBlocking(u8 u8Instance, u32 u32Data) +{ + const MCU_PIO_tstrConfig *pstrCfgLoc = &MCU_PIO_astrConfig[u8Instance]; + + /* Blocks until the TX FIFO has space, then writes the 32-bit word */ + pio_sm_put_blocking(pstrCfgLoc->pstrPio, pstrCfgLoc->u8Sm, u32Data); +} + +/* ========================================================================= */ +/* PUT BUFFER ASYNC (DMA, NON-BLOCKING) */ +/* ========================================================================= */ + +STD_tenuResult MCU_PIO_enuPutBufferAsync(u8 u8Instance, const u32 *pu32Data, u16 u16Count) +{ + STD_tenuResult enuResultLoc = STD_OK; + + if (pu32Data == STD_NULL) + { + enuResultLoc = STD_NULL_POINTER_ERROR; + } + else + { + const MCU_PIO_tstrConfig *pstrCfgLoc = &MCU_PIO_astrConfig[u8Instance]; + s8 s8ChLoc = strControl.as8DmaChannel[u8Instance]; + + dma_channel_config strCfgLoc = dma_channel_get_default_config((u32)s8ChLoc); + + /* 32-bit transfers — matches the PIO FIFO word size */ + channel_config_set_transfer_data_size(&strCfgLoc, DMA_SIZE_32); + + /* Source: increment through the caller's buffer */ + channel_config_set_read_increment(&strCfgLoc, true); + + /* Destination: PIO TX FIFO register (fixed address) */ + channel_config_set_write_increment(&strCfgLoc, false); + + /* Pace by PIO TX FIFO DREQ — DMA only pushes when SM can accept */ + channel_config_set_dreq(&strCfgLoc, pio_get_dreq(pstrCfgLoc->pstrPio, + pstrCfgLoc->u8Sm, + true)); + + /* Start the DMA transfer. Runs autonomously until u16Count words + * have been pushed into the FIFO. Caller must keep pu32Data valid + * until transfer completes. */ + dma_channel_configure( + (u32)s8ChLoc, + &strCfgLoc, + &pstrCfgLoc->pstrPio->txf[pstrCfgLoc->u8Sm], /* dest: PIO TX FIFO */ + pu32Data, /* source: buffer */ + u16Count, /* word count */ + true /* start immediately */ + ); + } + + return enuResultLoc; +} \ No newline at end of file diff --git a/src/MCU_PIO/prg/MCU_PIO_priv.h b/src/MCU_PIO/prg/MCU_PIO_priv.h new file mode 100644 index 0000000..a50a9ce --- /dev/null +++ b/src/MCU_PIO/prg/MCU_PIO_priv.h @@ -0,0 +1,34 @@ +/****************************************************************************** + * File: MCU_PIO_priv.h + * Component: MCU_PIO + * Description: Private header — extern config array and runtime control + * struct holding pre-claimed DMA channels per instance. + * + * Layer: MCU (hardware abstraction) - internal use only + *****************************************************************************/ + +#ifndef MCU_PIO_PRIV_H +#define MCU_PIO_PRIV_H + +#include "MCU_PIO.h" +#include "MCU_PIO_cfg.h" + +extern const MCU_PIO_tstrConfig MCU_PIO_astrConfig[MCU_PIO_NUM_INSTANCES]; + +/* ------------------------------------------------------------------------ */ +/* RUNTIME CONTROL STRUCTURE */ +/* ------------------------------------------------------------------------ */ + +/** + * @brief Per-instance runtime state. + * + * as8DmaChannel — DMA channel claimed during Init for async FIFO writes. + * One channel per PIO instance, reserved for the lifetime + * of the application. + */ +typedef struct +{ + s8 as8DmaChannel[MCU_PIO_NUM_INSTANCES]; +} MCU_PIO_tstrControl; + +#endif /* MCU_PIO_PRIV_H */ \ No newline at end of file diff --git a/src/SYS_ECU/prg/SYS_ECU.c b/src/SYS_ECU/prg/SYS_ECU.c index 4ecd4b3..9e27508 100644 --- a/src/SYS_ECU/prg/SYS_ECU.c +++ b/src/SYS_ECU/prg/SYS_ECU.c @@ -19,7 +19,9 @@ /* Components initialized and scheduled by SYS_ECU, listed in * dependency order (drivers first, then HAL, then application). */ #include "MCU_USB.h" +#include "MCU_PIO.h" #include "HAL_COM.h" +#include "HAL_LED.h" #include "APP_CLSW.h" /* ========================================================================= */ @@ -37,9 +39,11 @@ static void SYS_ECU_vInitAll(void) { /* MCU layer - hardware drivers */ MCU_USB_enuInit(); + MCU_PIO_enuInit(); /* HAL layer - abstractions over hardware */ HAL_COM_enuInit(); + HAL_LED_enuInit(); /* Application layer */ APP_CLSW_enuInit();