Extract reusable components into git submodules

STD_TYPES, MCU_UART, MCU_USB, MCU_PIO, HAL_COM, HAL_LED moved to
separate repos under common/ as git submodules. Each submodule ships
with default config (cfg/) that projects can override.

color_switcher/src/ now contains only project-specific components
(APP_CLSW, SYS_ECU). CMake sources_config references common/ via
COMMON_DIR. Docker volume mounts ../common:/common so the container
sees the submodules. Build verified — zero errors.
This commit is contained in:
Mohamed Salem 2026-04-13 03:55:25 +02:00
parent 5779ac2fa4
commit 66e18ed248
37 changed files with 73 additions and 2564 deletions

18
.gitmodules vendored Normal file
View File

@ -0,0 +1,18 @@
[submodule "common/STD_TYPES"]
path = common/STD_TYPES
url = http://git.teqanylogix.com:3000/mohamed.salem/std_types.git
[submodule "common/MCU_UART"]
path = common/MCU_UART
url = http://git.teqanylogix.com:3000/mohamed.salem/mcu_uart.git
[submodule "common/MCU_USB"]
path = common/MCU_USB
url = http://git.teqanylogix.com:3000/mohamed.salem/mcu_usb.git
[submodule "common/MCU_PIO"]
path = common/MCU_PIO
url = http://git.teqanylogix.com:3000/mohamed.salem/mcu_pio.git
[submodule "common/HAL_COM"]
path = common/HAL_COM
url = http://git.teqanylogix.com:3000/mohamed.salem/hal_com.git
[submodule "common/HAL_LED"]
path = common/HAL_LED
url = http://git.teqanylogix.com:3000/mohamed.salem/hal_led.git

View File

@ -72,7 +72,7 @@ function(mcu_link_target target)
# placed in the build directory and automatically added to the # placed in the build directory and automatically added to the
# target's include path. # target's include path.
pico_generate_pio_header(${target} pico_generate_pio_header(${target}
${PROJECT_ROOT_DIR}/src/MCU_PIO/pio/ws2812.pio) ${PROJECT_ROOT_DIR}/../common/MCU_PIO/pio/ws2812.pio)
# Route stdio over USB-CDC: the Pico will appear as a virtual serial # 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 # port on the host when plugged in, so printf/getchar are visible in

View File

@ -5,61 +5,62 @@
# Does NOT declare the executable - the top-level CMakeLists.txt handles # Does NOT declare the executable - the top-level CMakeLists.txt handles
# add_executable() so the build target is visible in one obvious place. # add_executable() so the build target is visible in one obvious place.
# #
# Sources come from two locations:
# - PROJECT_ROOT_DIR/src/ project-specific components (APP, SYS)
# - COMMON_DIR/ reusable components (git submodules)
#
# Exported variables: # Exported variables:
# PROJECT_SOURCES - list of every .c file under src/ # PROJECT_SOURCES - list of every .c file to compile
# PROJECT_INCLUDE_DIRS - list of include paths for each component # PROJECT_INCLUDE_DIRS - list of include paths for all components
# ============================================================================ # ============================================================================
# NOTE: all source/header paths below are rooted at PROJECT_ROOT_DIR, which # PROJECT_ROOT_DIR = one level above cmake/ (the color_switcher/ folder).
# the top-level CMakeLists.txt computes as the parent of its own directory # COMMON_DIR = the shared component submodules at the repo root level.
# (i.e. one level above cmake/). We do NOT use CMAKE_SOURCE_DIR here because set(COMMON_DIR "${PROJECT_ROOT_DIR}/../common")
# that variable points at the cmake/ folder itself, not at the project root.
# Recursively collect every .c file under src/. CONFIGURE_DEPENDS makes # Collect .c files from BOTH the project's own src/ and the common submodules.
# CMake re-check the glob on every build, so newly added .c files are # CONFIGURE_DEPENDS re-checks on every build so new files are picked up.
# 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 file(GLOB_RECURSE PROJECT_SOURCES CONFIGURE_DEPENDS
"${PROJECT_ROOT_DIR}/src/*.c") "${PROJECT_ROOT_DIR}/src/*.c"
"${COMMON_DIR}/STD_TYPES/*.c"
"${COMMON_DIR}/MCU_UART/*.c"
"${COMMON_DIR}/MCU_USB/*.c"
"${COMMON_DIR}/MCU_PIO/*.c"
"${COMMON_DIR}/HAL_COM/*.c"
"${COMMON_DIR}/HAL_LED/*.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 set(PROJECT_INCLUDE_DIRS
# ---- Common components (git submodules under pico/common/) ----
# Shared library layer - fundamental types used by every component # Shared library layer - fundamental types used by every component
${PROJECT_ROOT_DIR}/src/STD_TYPES/inc ${COMMON_DIR}/STD_TYPES/inc
# MCU layer - hardware abstraction for the RP2040 UART peripheral # MCU layer - hardware UART driver with DMA/ISR TX and ring buffer RX
${PROJECT_ROOT_DIR}/src/MCU_UART/inc ${COMMON_DIR}/MCU_UART/inc
${PROJECT_ROOT_DIR}/src/MCU_UART/prg ${COMMON_DIR}/MCU_UART/prg
${PROJECT_ROOT_DIR}/src/MCU_UART/cfg ${COMMON_DIR}/MCU_UART/cfg
# MCU layer - PIO peripheral driver for programmable I/O state machines # MCU layer - generic PIO state machine driver (WS2812 etc.)
${PROJECT_ROOT_DIR}/src/MCU_PIO/inc ${COMMON_DIR}/MCU_PIO/inc
${PROJECT_ROOT_DIR}/src/MCU_PIO/prg ${COMMON_DIR}/MCU_PIO/prg
${PROJECT_ROOT_DIR}/src/MCU_PIO/cfg ${COMMON_DIR}/MCU_PIO/cfg
# MCU layer - hardware abstraction for the RP2040 USB-CDC peripheral # MCU layer - USB-CDC virtual serial port driver
# (the Pico appears as a virtual serial port on the host computer) ${COMMON_DIR}/MCU_USB/inc
${PROJECT_ROOT_DIR}/src/MCU_USB/inc ${COMMON_DIR}/MCU_USB/prg
${PROJECT_ROOT_DIR}/src/MCU_USB/prg ${COMMON_DIR}/MCU_USB/cfg
${PROJECT_ROOT_DIR}/src/MCU_USB/cfg
# HAL layer - transport-agnostic communication abstraction that # HAL layer - transport-agnostic communication (function-pointer dispatch)
# dispatches to MCU_UART, MCU_USB, or both depending on configuration ${COMMON_DIR}/HAL_COM/inc
${PROJECT_ROOT_DIR}/src/HAL_COM/inc ${COMMON_DIR}/HAL_COM/prg
${PROJECT_ROOT_DIR}/src/HAL_COM/prg ${COMMON_DIR}/HAL_COM/cfg
${PROJECT_ROOT_DIR}/src/HAL_COM/cfg
# HAL layer - LED strip/pixel abstraction over PIO-based WS2812 driver # HAL layer - LED strip/pixel abstraction over PIO
${PROJECT_ROOT_DIR}/src/HAL_LED/inc ${COMMON_DIR}/HAL_LED/inc
${PROJECT_ROOT_DIR}/src/HAL_LED/prg ${COMMON_DIR}/HAL_LED/prg
${PROJECT_ROOT_DIR}/src/HAL_LED/cfg ${COMMON_DIR}/HAL_LED/cfg
# ---- Project-specific components (under color_switcher/src/) ----
# Application layer - color switcher application logic # Application layer - color switcher application logic
${PROJECT_ROOT_DIR}/src/APP_CLSW/inc ${PROJECT_ROOT_DIR}/src/APP_CLSW/inc

View File

@ -21,11 +21,13 @@ services:
# Build the image from the Dockerfile in the project root # Build the image from the Dockerfile in the project root
build: . build: .
# Mount the project source code into the container's working directory. # Mount project source and the shared common components into the container.
# This lets the container read our source files and write build artifacts # The project mounts at /project (working directory). The common submodules
# (including the .uf2 firmware file) back to the host filesystem. # mount at /common so the CMake COMMON_DIR path (../common relative to the
# project root) resolves correctly inside the container.
volumes: volumes:
- .:/project - .:/project
- ../common:/common
# Keep the container alive indefinitely. We intentionally do NOT run the # Keep the container alive indefinitely. We intentionally do NOT run the
# build on startup - `sleep infinity` lets the container stay up so it can # build on startup - `sleep infinity` lets the container stay up so it can

View File

@ -1,106 +0,0 @@
/******************************************************************************
* File: HAL_COM_cfg.c
* Component: HAL_COM
* Description: Configuration implementation for the HAL_COM abstraction.
* Defines the channel config array that wires each HAL_COM
* channel to a specific MCU-level transport driver via function
* pointers. Also contains thin wrapper functions to normalize
* driver signatures that don't match the generic prototype
* (e.g., MCU_USB which has no instance parameter).
*
* Layer: HAL - configuration
*****************************************************************************/
#include "HAL_COM.h"
#include "HAL_COM_cfg.h"
/* MCU drivers that channels may be wired to */
#include "MCU_USB.h"
#include "MCU_UART.h"
/* ------------------------------------------------------------------------ */
/* SIGNATURE NORMALIZATION WRAPPERS */
/* ------------------------------------------------------------------------ */
/**
* @brief Wrapper for MCU_USB_enuSendByte to match HAL_COM_tpfSendByte.
*
* MCU_USB has only one instance (the RP2040's single USB peripheral),
* so its public API does not take a u8Instance parameter. This wrapper
* adds the parameter and ignores it, making the signature compatible
* with HAL_COM's generic function pointer type.
*/
static STD_tenuResult vUsbSendByte(u8 u8Instance, u8 u8Byte)
{
(void)u8Instance;
return MCU_USB_enuSendByte(u8Byte);
}
/**
* @brief Wrapper for MCU_USB_enuSendBuffer to match HAL_COM_tpfSendBuffer.
*
* Same rationale as vUsbSendByte normalizes the missing instance
* parameter so USB can be plugged into a HAL_COM channel.
*/
static STD_tenuResult vUsbSendBuffer(u8 u8Instance, const u8 *pu8Data, u16 u16Length)
{
(void)u8Instance;
return MCU_USB_enuSendBuffer(pu8Data, u16Length);
}
/* MCU_UART already matches the HAL_COM function pointer signatures
* (u8 u8Instance as the first parameter), so no wrapper is needed
* for TX or RX. Its functions can be assigned directly. */
/* --- USB RX wrappers (normalize missing instance parameter) --- */
static STD_tenuResult vUsbReadByte(u8 u8Instance, u8 *pu8Byte)
{
(void)u8Instance;
return MCU_USB_enuReadByte(pu8Byte);
}
static STD_tenuResult vUsbReadBuffer(u8 u8Instance, u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read)
{
(void)u8Instance;
return MCU_USB_enuReadBuffer(pu8Data, u16MaxLength, pu16Read);
}
static STD_tBool vUsbIsRxDataAvailable(u8 u8Instance)
{
(void)u8Instance;
return MCU_USB_bIsRxDataAvailable();
}
/* ------------------------------------------------------------------------ */
/* CHANNEL CONFIGURATION ARRAY */
/* ------------------------------------------------------------------------ */
/**
* @brief Per-channel config, indexed by HAL_COM_tenuChannel.
*
* [HAL_COM_CHANNEL_0] = USB-CDC (primary communication path).
*
* To add a UART channel:
* 1. Add HAL_COM_CHANNEL_1 to the enum in HAL_COM_cfg.h
* 2. Add an entry here with MCU_UART functions (no wrapper needed):
* [HAL_COM_CHANNEL_1] = {
* .pfSendByte = MCU_UART_enuSendByte,
* .pfSendBuffer = MCU_UART_enuSendBuffer,
* .pfReceiveByte = MCU_UART_enuReceiveByte,
* .pfIsRxDataAvailable = MCU_UART_bIsRxDataAvailable,
* .u8Instance = 0U,
* },
*/
const HAL_COM_tstrChannelConfig HAL_COM_astrChannelConfig[HAL_COM_NUM_CHANNELS] =
{
[HAL_COM_CHANNEL_0] =
{
.pfSendByte = vUsbSendByte,
.pfSendBuffer = vUsbSendBuffer,
.pfReadByte = vUsbReadByte,
.pfReadBuffer = vUsbReadBuffer,
.pfIsRxDataAvailable = vUsbIsRxDataAvailable,
.u8Instance = 0U,
},
};

View File

@ -1,39 +0,0 @@
/******************************************************************************
* File: HAL_COM_cfg.h
* Component: HAL_COM
* Description: Configuration header for the HAL_COM abstraction.
* Defines the communication channels and which MCU-level
* transport each one routes through via function pointers.
* Adding a new channel or swapping a transport is a config-only
* change no code in HAL_COM_prg.c needs to be modified.
*
* Layer: HAL - configuration
*****************************************************************************/
#ifndef HAL_COM_CFG_H
#define HAL_COM_CFG_H
#include "STD_TYPES.h"
/* ------------------------------------------------------------------------ */
/* CHANNEL ENUMERATION */
/* ------------------------------------------------------------------------ */
/**
* @brief Enumeration of configured HAL_COM channels.
*
* Each channel is one logical communication path wired to a specific
* MCU-level driver. The enumerator values are used as array indices
* into HAL_COM_astrChannelConfig[].
*
* To add a channel: add an enumerator before HAL_COM_NUM_CHANNELS,
* define its function pointer macros below, and add an entry in
* HAL_COM_cfg.c.
*/
typedef enum
{
HAL_COM_CHANNEL_0 = 0U, /**< Primary channel (USB-CDC by default) */
HAL_COM_NUM_CHANNELS
} HAL_COM_tenuChannel;
#endif /* HAL_COM_CFG_H */

View File

@ -1,151 +0,0 @@
/******************************************************************************
* File: HAL_COM.h
* Component: HAL_COM
* Description: Public interface for the HAL communication abstraction layer.
* Provides a transport-agnostic, multi-channel API for sending
* and receiving bytes. Each channel is independently configured
* with function pointers to an MCU-level driver (USB, UART,
* SPI, I2C, or any future transport that matches the function
* signatures).
*
* Higher layers (e.g. APP_CLSW) call HAL_COM_* with a channel
* number and stay unaware of which physical transport is in use.
* Adding a new transport requires zero changes to this file or
* to HAL_COM_prg.c only the config needs a new channel entry.
*
* Layer: HAL (hardware abstraction, one level above MCU drivers)
*****************************************************************************/
#ifndef HAL_COM_H
#define HAL_COM_H
#include "STD_TYPES.h"
#include "HAL_COM_cfg.h"
/* ------------------------------------------------------------------------ */
/* TRANSPORT FUNCTION POINTER TYPES */
/* ------------------------------------------------------------------------ */
/**
* @brief Generic send-byte function pointer type.
*
* Any MCU driver that can send a single byte and matches this signature
* can be plugged into a HAL_COM channel. The u8Instance parameter
* identifies the peripheral instance within that driver (e.g., UART0
* vs UART1). Drivers that have only one instance (e.g., MCU_USB) use
* a thin wrapper that ignores the parameter.
*
* @param u8Instance Peripheral instance index within the driver.
* @param u8Byte The byte to transmit.
* @return STD_tenuResult
*/
typedef STD_tenuResult (*HAL_COM_tpfSendByte)(u8 u8Instance, u8 u8Byte);
/**
* @brief Generic send-buffer function pointer type.
*
* Same concept as HAL_COM_tpfSendByte but for multi-byte transfers.
*
* @param u8Instance Peripheral instance index within the driver.
* @param pu8Data Pointer to the byte buffer.
* @param u16Length Number of bytes to transmit.
* @return STD_tenuResult
*/
typedef STD_tenuResult (*HAL_COM_tpfSendBuffer)(u8 u8Instance, const u8 *pu8Data, u16 u16Length);
/**
* @brief Generic read-byte function pointer type (non-blocking).
*/
typedef STD_tenuResult (*HAL_COM_tpfReadByte)(u8 u8Instance, u8 *pu8Byte);
/**
* @brief Generic read-buffer function pointer type (non-blocking).
*/
typedef STD_tenuResult (*HAL_COM_tpfReadBuffer)(u8 u8Instance, u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read);
/**
* @brief Generic RX-data-available check function pointer type.
*/
typedef STD_tBool (*HAL_COM_tpfIsRxDataAvailable)(u8 u8Instance);
/* ------------------------------------------------------------------------ */
/* CONFIGURATION STRUCTURE */
/* ------------------------------------------------------------------------ */
/**
* @brief Per-channel communication configuration.
*
* One entry per HAL_COM channel, stored in HAL_COM_astrChannelConfig[].
* The array index is the channel number passed to all public functions.
*
* Each channel is wired to exactly one MCU-level transport by holding
* function pointers to that driver's send functions + the instance index
* to pass through. To route through a different transport, just change
* the function pointers in the config no code changes needed.
*/
typedef struct
{
HAL_COM_tpfSendByte pfSendByte; /**< Driver's send-byte function */
HAL_COM_tpfSendBuffer pfSendBuffer; /**< Driver's send-buffer function */
HAL_COM_tpfReadByte pfReadByte; /**< Driver's read-byte function */
HAL_COM_tpfReadBuffer pfReadBuffer; /**< Driver's read-buffer function */
HAL_COM_tpfIsRxDataAvailable pfIsRxDataAvailable; /**< Driver's RX-available check */
u8 u8Instance; /**< Peripheral instance to pass through */
} HAL_COM_tstrChannelConfig;
/* ------------------------------------------------------------------------ */
/* PUBLIC API */
/* ------------------------------------------------------------------------ */
/**
* @brief Initialize the HAL communication layer's own internal state.
*
* Does NOT initialize the underlying MCU drivers SYS_ECU owns the init
* sequence and calls each driver's enuInit() 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 specified channel.
*
* @param u8Channel Channel index (from HAL_COM_cfg.h channel enum).
* @param u8Byte The byte to transmit.
* @return STD_OK on success, STD_NOK on failure.
*/
STD_tenuResult HAL_COM_enuSendByte(u8 u8Channel, u8 u8Byte);
/**
* @brief Send a buffer through the specified channel.
*
* The underlying driver may be blocking or non-blocking depending on the
* MCU driver wired to this channel. When non-blocking (e.g., MCU_UART
* with DMA), the caller must keep the buffer valid until the transfer
* completes.
*
* @param u8Channel Channel index.
* @param pu8Data Pointer to the byte buffer. 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 failure.
*/
STD_tenuResult HAL_COM_enuSendBuffer(u8 u8Channel, const u8 *pu8Data, u16 u16Length);
/**
* @brief Read one byte from the specified channel (non-blocking).
*/
STD_tenuResult HAL_COM_enuReadByte(u8 u8Channel, u8 *pu8Byte);
/**
* @brief Read up to u16MaxLength bytes from the specified channel (non-blocking).
*/
STD_tenuResult HAL_COM_enuReadBuffer(u8 u8Channel, u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read);
/**
* @brief Check if the specified channel has RX data available.
*/
STD_tBool HAL_COM_bIsRxDataAvailable(u8 u8Channel);
#endif /* HAL_COM_H */

View File

@ -1,106 +0,0 @@
/******************************************************************************
* File: HAL_COM_prg.c
* Component: HAL_COM
* Description: Program (implementation) file for the HAL_COM abstraction.
* Dispatches send operations to the MCU-level driver wired to
* each channel via function pointers in HAL_COM_astrChannelConfig.
*
* The dispatch is a single indirect call no if/else chains,
* no transport-specific code. Adding a new transport (SPI, I2C,
* etc.) requires zero changes here only a new config entry.
*
* Layer: HAL
*****************************************************************************/
#include "HAL_COM.h"
#include "HAL_COM_priv.h"
#include "HAL_COM_cfg.h"
/* ========================================================================= */
/* INIT */
/* ========================================================================= */
STD_tenuResult HAL_COM_enuInit(void)
{
STD_tenuResult enuResultLoc = STD_OK;
/* HAL_COM has no internal state to set up yet. The underlying MCU
* drivers are already initialized by SYS_ECU before this function
* is called. When internal queuing or channel-level state is added,
* initialize it here. */
return enuResultLoc;
}
/* ========================================================================= */
/* SEND BYTE */
/* ========================================================================= */
STD_tenuResult HAL_COM_enuSendByte(u8 u8Channel, u8 u8Byte)
{
STD_tenuResult enuResultLoc = STD_OK;
const HAL_COM_tstrChannelConfig *pstrCfgLoc = &HAL_COM_astrChannelConfig[u8Channel];
/* Dispatch through the function pointer configured for this channel.
* The driver's instance index is passed through transparently the
* caller never sees it. */
enuResultLoc = pstrCfgLoc->pfSendByte(pstrCfgLoc->u8Instance, u8Byte);
return enuResultLoc;
}
/* ========================================================================= */
/* SEND BUFFER */
/* ========================================================================= */
STD_tenuResult HAL_COM_enuSendBuffer(u8 u8Channel, const u8 *pu8Data, u16 u16Length)
{
STD_tenuResult enuResultLoc = STD_OK;
const HAL_COM_tstrChannelConfig *pstrCfgLoc = &HAL_COM_astrChannelConfig[u8Channel];
/* Dispatch through the function pointer configured for this channel.
* Null-pointer validation is handled inside the MCU driver, so we
* don't duplicate the check here. */
enuResultLoc = pstrCfgLoc->pfSendBuffer(pstrCfgLoc->u8Instance, pu8Data, u16Length);
return enuResultLoc;
}
/* ========================================================================= */
/* RECEIVE BYTE (BLOCKING) */
/* ========================================================================= */
STD_tenuResult HAL_COM_enuReadByte(u8 u8Channel, u8 *pu8Byte)
{
STD_tenuResult enuResultLoc = STD_OK;
const HAL_COM_tstrChannelConfig *pstrCfgLoc = &HAL_COM_astrChannelConfig[u8Channel];
enuResultLoc = pstrCfgLoc->pfReadByte(pstrCfgLoc->u8Instance, pu8Byte);
return enuResultLoc;
}
/* ========================================================================= */
/* READ BUFFER (NON-BLOCKING) */
/* ========================================================================= */
STD_tenuResult HAL_COM_enuReadBuffer(u8 u8Channel, u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read)
{
STD_tenuResult enuResultLoc = STD_OK;
const HAL_COM_tstrChannelConfig *pstrCfgLoc = &HAL_COM_astrChannelConfig[u8Channel];
enuResultLoc = pstrCfgLoc->pfReadBuffer(pstrCfgLoc->u8Instance, pu8Data, u16MaxLength, pu16Read);
return enuResultLoc;
}
/* ========================================================================= */
/* RX DATA AVAILABLE CHECK */
/* ========================================================================= */
STD_tBool HAL_COM_bIsRxDataAvailable(u8 u8Channel)
{
const HAL_COM_tstrChannelConfig *pstrCfgLoc = &HAL_COM_astrChannelConfig[u8Channel];
return pstrCfgLoc->pfIsRxDataAvailable(pstrCfgLoc->u8Instance);
}

View File

@ -1,32 +0,0 @@
/******************************************************************************
* File: HAL_COM_priv.h
* Component: HAL_COM
* Description: Private header for the HAL_COM abstraction.
* Contains the extern declaration of the channel config array
* (only accessed internally by _prg.c) and any other private
* definitions.
*
* Layer: HAL - internal use only
*****************************************************************************/
#ifndef HAL_COM_PRIV_H
#define HAL_COM_PRIV_H
#include "HAL_COM.h"
#include "HAL_COM_cfg.h"
/* ------------------------------------------------------------------------ */
/* CONFIG ARRAY (EXTERN) */
/* ------------------------------------------------------------------------ */
/**
* @brief Channel configuration array indexed by HAL_COM_tenuChannel.
*
* Defined in HAL_COM_cfg.c. Each entry holds function pointers to an
* MCU-level driver's send functions + the instance index to pass through.
* Only HAL_COM_prg.c accesses this no external component should read
* the raw config.
*/
extern const HAL_COM_tstrChannelConfig HAL_COM_astrChannelConfig[HAL_COM_NUM_CHANNELS];
#endif /* HAL_COM_PRIV_H */

View File

@ -1,19 +0,0 @@
/******************************************************************************
* 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,
},
};

View File

@ -1,46 +0,0 @@
/******************************************************************************
* 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 */

View File

@ -1,70 +0,0 @@
/******************************************************************************
* 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 */

View File

@ -1,117 +0,0 @@
/******************************************************************************
* 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;
}

View File

@ -1,51 +0,0 @@
/******************************************************************************
* 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 */

View File

@ -1,53 +0,0 @@
/******************************************************************************
* 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,
},
};

View File

@ -1,40 +0,0 @@
/******************************************************************************
* 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 */

View File

@ -1,107 +0,0 @@
/******************************************************************************
* 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 */

View File

@ -1,73 +0,0 @@
; ============================================================================
; 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);
}
%}

View File

@ -1,115 +0,0 @@
/******************************************************************************
* 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;
}

View File

@ -1,34 +0,0 @@
/******************************************************************************
* 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 */

View File

@ -1,26 +0,0 @@
/******************************************************************************
* File: MCU_UART_cfg.c
* Component: MCU_UART
* Description: Configuration array definition for the MCU_UART driver.
*
* Layer: MCU (hardware abstraction) - configuration
*****************************************************************************/
#include "MCU_UART.h"
#include "MCU_UART_cfg.h"
const MCU_UART_tstrConfig MCU_UART_astrConfig[MCU_UART_NUM_INSTANCES] =
{
[MCU_UART_INSTANCE_0] =
{
.u8TxPin = MCU_UART_0_TX_PIN,
.u8RxPin = MCU_UART_0_RX_PIN,
.u32BaudRate = MCU_UART_0_BAUD_RATE,
.enuDataBits = MCU_UART_0_DATA_BITS,
.enuStopBits = MCU_UART_0_STOP_BITS,
.enuParity = MCU_UART_0_PARITY,
.enuTxAsyncMode = MCU_UART_0_TX_ASYNC_MODE,
.pfTxCompleteCallback = MCU_UART_0_TX_COMPLETE_CALLBACK,
.enuRxAsyncMode = MCU_UART_0_RX_ASYNC_MODE,
},
};

View File

@ -1,51 +0,0 @@
/******************************************************************************
* File: MCU_UART_cfg.h
* Component: MCU_UART
* Description: Configuration for the MCU_UART driver. Defines instances,
* pin assignments, baud rates, data format, TX/RX async modes,
* and RX buffer sizing.
*
* Layer: MCU (hardware abstraction) - configuration
*****************************************************************************/
#ifndef MCU_UART_CFG_H
#define MCU_UART_CFG_H
#include "STD_TYPES.h"
/* ------------------------------------------------------------------------ */
/* INSTANCE ENUMERATION */
/* ------------------------------------------------------------------------ */
typedef enum
{
MCU_UART_INSTANCE_0 = 0U,
MCU_UART_NUM_INSTANCES
} MCU_UART_tenuInstance;
/* ------------------------------------------------------------------------ */
/* RX RING BUFFER SIZING */
/* ------------------------------------------------------------------------ */
/** @brief Number of address bits for the RX ring buffer (2^N bytes).
* Must be power of 2 for DMA ring wrap.
* 5 = 32, 6 = 64, 7 = 128, 8 = 256. */
#define MCU_UART_RX_BUFFER_SIZE_BITS 6U
#define MCU_UART_RX_BUFFER_SIZE (1U << MCU_UART_RX_BUFFER_SIZE_BITS)
#define MCU_UART_RX_BUFFER_MASK (MCU_UART_RX_BUFFER_SIZE - 1U)
/* ------------------------------------------------------------------------ */
/* INSTANCE 0 CONFIGURATION */
/* ------------------------------------------------------------------------ */
#define MCU_UART_0_TX_PIN 0U
#define MCU_UART_0_RX_PIN 1U
#define MCU_UART_0_BAUD_RATE 115200U
#define MCU_UART_0_DATA_BITS MCU_UART_DATA_BITS_8
#define MCU_UART_0_STOP_BITS MCU_UART_STOP_BITS_1
#define MCU_UART_0_PARITY MCU_UART_PARITY_NONE
#define MCU_UART_0_TX_ASYNC_MODE MCU_UART_ASYNC_DMA
#define MCU_UART_0_TX_COMPLETE_CALLBACK STD_NULL
#define MCU_UART_0_RX_ASYNC_MODE MCU_UART_ASYNC_ISR
#endif /* MCU_UART_CFG_H */

View File

@ -1,124 +0,0 @@
/******************************************************************************
* File: MCU_UART.h
* Component: MCU_UART
* Description: Public interface for the MCU UART driver component.
*
* TX: SendByte (blocking), SendBuffer (non-blocking DMA/ISR),
* SendBufferBlocking.
* RX: background ring buffer filled by ISR or DMA.
* ReadByte / ReadBuffer read from the buffer (non-blocking).
*
* Layer: MCU (hardware abstraction)
*****************************************************************************/
#ifndef MCU_UART_H
#define MCU_UART_H
#include "STD_TYPES.h"
/* ------------------------------------------------------------------------ */
/* CONFIGURATION VALUE TYPES */
/* ------------------------------------------------------------------------ */
typedef enum
{
MCU_UART_PARITY_NONE = 0U,
MCU_UART_PARITY_EVEN,
MCU_UART_PARITY_ODD
} MCU_UART_tenuParity;
typedef enum
{
MCU_UART_DATA_BITS_5 = 5U,
MCU_UART_DATA_BITS_6 = 6U,
MCU_UART_DATA_BITS_7 = 7U,
MCU_UART_DATA_BITS_8 = 8U
} MCU_UART_tenuDataBits;
typedef enum
{
MCU_UART_STOP_BITS_1 = 1U,
MCU_UART_STOP_BITS_2 = 2U
} MCU_UART_tenuStopBits;
typedef enum
{
MCU_UART_ASYNC_DMA = 0U,
MCU_UART_ASYNC_ISR
} MCU_UART_tenuAsyncMode;
/* ------------------------------------------------------------------------ */
/* CONFIGURATION STRUCTURE */
/* ------------------------------------------------------------------------ */
typedef struct
{
u8 u8TxPin;
u8 u8RxPin;
u32 u32BaudRate;
MCU_UART_tenuDataBits enuDataBits;
MCU_UART_tenuStopBits enuStopBits;
MCU_UART_tenuParity enuParity;
MCU_UART_tenuAsyncMode enuTxAsyncMode;
STD_tpfCallbackFunc pfTxCompleteCallback;
MCU_UART_tenuAsyncMode enuRxAsyncMode;
} MCU_UART_tstrConfig;
/* ------------------------------------------------------------------------ */
/* TX PUBLIC API */
/* ------------------------------------------------------------------------ */
STD_tenuResult MCU_UART_enuInit(void);
STD_tenuResult MCU_UART_enuSendByte(u8 u8Instance, u8 u8Byte);
STD_tenuResult MCU_UART_enuSendBuffer(u8 u8Instance, const u8 *pu8Data, u16 u16Length);
STD_tenuResult MCU_UART_enuSendBufferBlocking(u8 u8Instance, const u8 *pu8Data, u16 u16Length);
STD_tBool MCU_UART_bIsTxBusy(u8 u8Instance);
/* ------------------------------------------------------------------------ */
/* RX PUBLIC API */
/* ------------------------------------------------------------------------ */
/**
* @brief Read one byte from the RX ring buffer (non-blocking).
*
* The ring buffer is filled in the background by ISR or DMA.
* Returns immediately STD_OK with the byte, or STD_NOK if empty.
*
* @param u8Instance UART instance index.
* @param pu8Byte Pointer to store the received byte.
* @return STD_OK byte read,
* STD_NULL_POINTER_ERROR if pu8Byte is NULL,
* STD_NOK if ring buffer is empty.
*/
STD_tenuResult MCU_UART_enuReadByte(u8 u8Instance, u8 *pu8Byte);
/**
* @brief Read up to u16MaxLength bytes from the RX ring buffer (non-blocking).
*
* Copies as many bytes as are currently available (up to u16MaxLength)
* into pu8Data. Reports the actual number of bytes read via pu16Read.
* Returns immediately even if fewer than u16MaxLength bytes are available.
*
* @param u8Instance UART instance index.
* @param pu8Data Pointer to output buffer.
* @param u16MaxLength Maximum bytes to read.
* @param pu16Read Pointer to store actual bytes read. Must not be NULL.
* @return STD_OK at least one byte read,
* STD_NULL_POINTER_ERROR if pu8Data or pu16Read is NULL,
* STD_NOK if ring buffer is empty (zero bytes read).
*/
STD_tenuResult MCU_UART_enuReadBuffer(u8 u8Instance, u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read);
/**
* @brief Check if the RX ring buffer has data.
*
* @param u8Instance UART instance index.
* @return STD_TRUE if at least one byte available, STD_FALSE if empty.
*/
STD_tBool MCU_UART_bIsRxDataAvailable(u8 u8Instance);
#endif /* MCU_UART_H */

View File

@ -1,421 +0,0 @@
/******************************************************************************
* File: MCU_UART_prg.c
* Component: MCU_UART
* Description: TX: blocking + non-blocking (DMA or ISR) with callback.
* RX: ISR or DMA fills ring buffer in background. ReadByte
* and ReadBuffer read from the buffer non-blocking.
*
* Layer: MCU (hardware abstraction)
*****************************************************************************/
#include "MCU_UART.h"
#include "MCU_UART_priv.h"
#include "MCU_UART_cfg.h"
#include "hardware/uart.h"
#include "hardware/gpio.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
/* ------------------------------------------------------------------------ */
/* INSTANCE LOOKUP TABLE */
/* ------------------------------------------------------------------------ */
static uart_inst_t * const apstrInstances[] = { uart0, uart1 };
/* ------------------------------------------------------------------------ */
/* RUNTIME STATE */
/* ------------------------------------------------------------------------ */
static MCU_UART_tstrControl strControl;
/* ------------------------------------------------------------------------ */
/* INTERNAL HELPERS */
/* ------------------------------------------------------------------------ */
static void vCallTxCallback(u8 u8Instance)
{
STD_tpfCallbackFunc pfCbLoc = MCU_UART_astrConfig[u8Instance].pfTxCompleteCallback;
if (pfCbLoc != STD_NULL)
{
pfCbLoc();
}
}
/** @brief Get current RX head. DMA mode derives from hw write pointer. */
static u16 u16GetRxHead(u8 u8Instance)
{
u16 u16HeadLoc;
if (MCU_UART_astrConfig[u8Instance].enuRxAsyncMode == MCU_UART_ASYNC_DMA)
{
s8 s8ChLoc = strControl.as8RxDmaChannel[u8Instance];
u32 u32WrLoc = (u32)dma_channel_hw_addr((u32)s8ChLoc)->write_addr;
u32 u32BaseLoc = (u32)(&strControl.aau8RxBuffer[u8Instance][0]);
u16HeadLoc = (u16)((u32WrLoc - u32BaseLoc) & MCU_UART_RX_BUFFER_MASK);
}
else
{
u16HeadLoc = strControl.au16RxHead[u8Instance];
}
return u16HeadLoc;
}
/* ------------------------------------------------------------------------ */
/* TX DMA IRQ HANDLER (INSTANCE 0) */
/* ------------------------------------------------------------------------ */
static void vTxDmaIrqHandler0(void)
{
s8 s8ChLoc = strControl.as8TxDmaChannel[MCU_UART_INSTANCE_0];
u32 u32StatusLoc = dma_channel_get_irq0_status((u32)s8ChLoc);
if (u32StatusLoc != 0U)
{
dma_irqn_acknowledge_channel(0, (u32)s8ChLoc);
strControl.abTxBusy[MCU_UART_INSTANCE_0] = STD_FALSE;
vCallTxCallback((u8)MCU_UART_INSTANCE_0);
}
}
/* ------------------------------------------------------------------------ */
/* UART ISR HANDLER (RX + TX) */
/* ------------------------------------------------------------------------ */
static void vUartIsrHandler(u8 u8Instance)
{
uart_inst_t *pstrUartLoc = apstrInstances[u8Instance];
/* --- RX: drain FIFO into ring buffer --- */
STD_tBool bReadableLoc = (uart_is_readable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
while (bReadableLoc == STD_TRUE)
{
u8 u8ByteLoc = (u8)uart_getc(pstrUartLoc);
u16 u16HeadLoc = strControl.au16RxHead[u8Instance];
strControl.aau8RxBuffer[u8Instance][u16HeadLoc] = u8ByteLoc;
strControl.au16RxHead[u8Instance] = (u16HeadLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
bReadableLoc = (uart_is_readable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
}
/* --- TX: fill FIFO from buffer (if TX ISR active) --- */
STD_tBool bTxBusyLoc = strControl.abTxBusy[u8Instance];
if (bTxBusyLoc == STD_TRUE)
{
STD_tBool bFifoReady = (uart_is_writable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
STD_tBool bDataLeft = (strControl.au16TxIndex[u8Instance] < strControl.au16TxLength[u8Instance]) ? STD_TRUE : STD_FALSE;
while ((bFifoReady == STD_TRUE) && (bDataLeft == STD_TRUE))
{
uart_putc_raw(pstrUartLoc,
strControl.apu8TxBuffer[u8Instance][strControl.au16TxIndex[u8Instance]]);
strControl.au16TxIndex[u8Instance]++;
bFifoReady = (uart_is_writable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
bDataLeft = (strControl.au16TxIndex[u8Instance] < strControl.au16TxLength[u8Instance]) ? STD_TRUE : STD_FALSE;
}
if (bDataLeft == STD_FALSE)
{
uart_set_irqs_enabled(pstrUartLoc, false, true);
strControl.abTxBusy[u8Instance] = STD_FALSE;
vCallTxCallback(u8Instance);
}
}
}
static void vUart0IrqHandler(void)
{
vUartIsrHandler((u8)MCU_UART_INSTANCE_0);
}
/* ========================================================================= */
/* INIT */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuInit(void)
{
STD_tenuResult enuResultLoc = STD_OK;
u8 u8IndexLoc;
for (u8IndexLoc = 0U; u8IndexLoc < (u8)MCU_UART_NUM_INSTANCES; u8IndexLoc++)
{
strControl.apu8TxBuffer[u8IndexLoc] = STD_NULL;
strControl.au16TxLength[u8IndexLoc] = 0U;
strControl.au16TxIndex[u8IndexLoc] = 0U;
strControl.abTxBusy[u8IndexLoc] = STD_FALSE;
strControl.as8TxDmaChannel[u8IndexLoc] = -1;
strControl.au16RxHead[u8IndexLoc] = 0U;
strControl.au16RxTail[u8IndexLoc] = 0U;
strControl.as8RxDmaChannel[u8IndexLoc] = -1;
}
for (u8IndexLoc = 0U; u8IndexLoc < (u8)MCU_UART_NUM_INSTANCES; u8IndexLoc++)
{
const MCU_UART_tstrConfig *pstrCfgLoc = &MCU_UART_astrConfig[u8IndexLoc];
uart_inst_t *pstrUartLoc = apstrInstances[u8IndexLoc];
uart_init(pstrUartLoc, pstrCfgLoc->u32BaudRate);
gpio_set_function(pstrCfgLoc->u8TxPin, GPIO_FUNC_UART);
gpio_set_function(pstrCfgLoc->u8RxPin, GPIO_FUNC_UART);
uart_set_format(pstrUartLoc,
pstrCfgLoc->enuDataBits,
pstrCfgLoc->enuStopBits,
pstrCfgLoc->enuParity);
/* TX async setup */
if (pstrCfgLoc->enuTxAsyncMode == MCU_UART_ASYNC_DMA)
{
s8 s8ChLoc = (s8)dma_claim_unused_channel(true);
strControl.as8TxDmaChannel[u8IndexLoc] = s8ChLoc;
dma_channel_set_irq0_enabled((u32)s8ChLoc, true);
if (u8IndexLoc == (u8)MCU_UART_INSTANCE_0)
{
irq_set_exclusive_handler(DMA_IRQ_0, vTxDmaIrqHandler0);
irq_set_enabled(DMA_IRQ_0, true);
}
}
/* RX async setup */
if (pstrCfgLoc->enuRxAsyncMode == MCU_UART_ASYNC_DMA)
{
s8 s8RxChLoc = (s8)dma_claim_unused_channel(true);
strControl.as8RxDmaChannel[u8IndexLoc] = s8RxChLoc;
dma_channel_config strRxCfgLoc = dma_channel_get_default_config((u32)s8RxChLoc);
channel_config_set_transfer_data_size(&strRxCfgLoc, DMA_SIZE_8);
channel_config_set_read_increment(&strRxCfgLoc, false);
channel_config_set_write_increment(&strRxCfgLoc, true);
channel_config_set_dreq(&strRxCfgLoc, uart_get_dreq(pstrUartLoc, false));
channel_config_set_ring(&strRxCfgLoc, true, MCU_UART_RX_BUFFER_SIZE_BITS);
dma_channel_configure(
(u32)s8RxChLoc,
&strRxCfgLoc,
&strControl.aau8RxBuffer[u8IndexLoc][0],
&uart_get_hw(pstrUartLoc)->dr,
0xFFFFFFFFU,
true
);
}
else
{
if (u8IndexLoc == (u8)MCU_UART_INSTANCE_0)
{
irq_set_exclusive_handler(UART0_IRQ, vUart0IrqHandler);
irq_set_enabled(UART0_IRQ, true);
}
uart_set_irqs_enabled(pstrUartLoc, false, true);
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* SEND BYTE (BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuSendByte(u8 u8Instance, u8 u8Byte)
{
STD_tenuResult enuResultLoc = STD_OK;
uart_putc_raw(apstrInstances[u8Instance], u8Byte);
return enuResultLoc;
}
/* ========================================================================= */
/* SEND BUFFER (NON-BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuSendBuffer(u8 u8Instance, const u8 *pu8Data, u16 u16Length)
{
STD_tenuResult enuResultLoc = STD_OK;
STD_tBool bBusyLoc = strControl.abTxBusy[u8Instance];
MCU_UART_tenuAsyncMode enuModeLoc = MCU_UART_astrConfig[u8Instance].enuTxAsyncMode;
if (pu8Data == STD_NULL)
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else if (bBusyLoc == STD_TRUE)
{
enuResultLoc = STD_NOK;
}
else
{
strControl.apu8TxBuffer[u8Instance] = pu8Data;
strControl.au16TxLength[u8Instance] = u16Length;
strControl.au16TxIndex[u8Instance] = 0U;
strControl.abTxBusy[u8Instance] = STD_TRUE;
if (enuModeLoc == MCU_UART_ASYNC_DMA)
{
s8 s8ChLoc = strControl.as8TxDmaChannel[u8Instance];
uart_inst_t *pstrUartLoc = apstrInstances[u8Instance];
dma_channel_config strCfgLoc = dma_channel_get_default_config((u32)s8ChLoc);
channel_config_set_transfer_data_size(&strCfgLoc, DMA_SIZE_8);
channel_config_set_read_increment(&strCfgLoc, true);
channel_config_set_write_increment(&strCfgLoc, false);
channel_config_set_dreq(&strCfgLoc, uart_get_dreq(pstrUartLoc, true));
dma_channel_configure(
(u32)s8ChLoc, &strCfgLoc,
&uart_get_hw(pstrUartLoc)->dr, pu8Data, u16Length, true);
}
else
{
uart_inst_t *pstrUartLoc = apstrInstances[u8Instance];
STD_tBool bFifoReady = (uart_is_writable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
STD_tBool bDataLeft = (strControl.au16TxIndex[u8Instance] < u16Length) ? STD_TRUE : STD_FALSE;
while ((bFifoReady == STD_TRUE) && (bDataLeft == STD_TRUE))
{
uart_putc_raw(pstrUartLoc, pu8Data[strControl.au16TxIndex[u8Instance]]);
strControl.au16TxIndex[u8Instance]++;
bFifoReady = (uart_is_writable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
bDataLeft = (strControl.au16TxIndex[u8Instance] < u16Length) ? STD_TRUE : STD_FALSE;
}
if (bDataLeft == STD_FALSE)
{
strControl.abTxBusy[u8Instance] = STD_FALSE;
vCallTxCallback(u8Instance);
}
else
{
uart_set_irqs_enabled(pstrUartLoc, true, true);
}
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* SEND BUFFER (BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuSendBufferBlocking(u8 u8Instance, const u8 *pu8Data, u16 u16Length)
{
STD_tenuResult enuResultLoc = STD_OK;
u16 u16IndexLoc;
if (pu8Data == STD_NULL)
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else
{
for (u16IndexLoc = 0U; u16IndexLoc < u16Length; u16IndexLoc++)
{
uart_putc_raw(apstrInstances[u8Instance], pu8Data[u16IndexLoc]);
}
vCallTxCallback(u8Instance);
}
return enuResultLoc;
}
/* ========================================================================= */
/* TX BUSY CHECK */
/* ========================================================================= */
STD_tBool MCU_UART_bIsTxBusy(u8 u8Instance)
{
return strControl.abTxBusy[u8Instance];
}
/* ========================================================================= */
/* READ BYTE (NON-BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuReadByte(u8 u8Instance, u8 *pu8Byte)
{
STD_tenuResult enuResultLoc = STD_OK;
if (pu8Byte == STD_NULL)
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else
{
u16 u16HeadLoc = u16GetRxHead(u8Instance);
u16 u16TailLoc = strControl.au16RxTail[u8Instance];
if (u16HeadLoc == u16TailLoc)
{
enuResultLoc = STD_NOK;
}
else
{
*pu8Byte = strControl.aau8RxBuffer[u8Instance][u16TailLoc];
strControl.au16RxTail[u8Instance] = (u16TailLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* READ BUFFER (NON-BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuReadBuffer(u8 u8Instance, u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read)
{
STD_tenuResult enuResultLoc = STD_OK;
if ((pu8Data == STD_NULL) || (pu16Read == STD_NULL))
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else
{
u16 u16HeadLoc = u16GetRxHead(u8Instance);
u16 u16TailLoc = strControl.au16RxTail[u8Instance];
u16 u16CountLoc = 0U;
while ((u16HeadLoc != u16TailLoc) && (u16CountLoc < u16MaxLength))
{
pu8Data[u16CountLoc] = strControl.aau8RxBuffer[u8Instance][u16TailLoc];
u16TailLoc = (u16TailLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
u16CountLoc++;
}
strControl.au16RxTail[u8Instance] = u16TailLoc;
*pu16Read = u16CountLoc;
if (u16CountLoc == 0U)
{
enuResultLoc = STD_NOK;
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* RX DATA AVAILABLE CHECK */
/* ========================================================================= */
STD_tBool MCU_UART_bIsRxDataAvailable(u8 u8Instance)
{
STD_tBool bResultLoc = STD_FALSE;
u16 u16HeadLoc = u16GetRxHead(u8Instance);
u16 u16TailLoc = strControl.au16RxTail[u8Instance];
if (u16HeadLoc != u16TailLoc)
{
bResultLoc = STD_TRUE;
}
return bResultLoc;
}

View File

@ -1,34 +0,0 @@
/******************************************************************************
* File: MCU_UART_priv.h
* Component: MCU_UART
* Description: Private header control struct and extern config array.
*
* Layer: MCU (hardware abstraction) - internal use only
*****************************************************************************/
#ifndef MCU_UART_PRIV_H
#define MCU_UART_PRIV_H
#include "MCU_UART.h"
#include "MCU_UART_cfg.h"
extern const MCU_UART_tstrConfig MCU_UART_astrConfig[MCU_UART_NUM_INSTANCES];
typedef struct
{
/* TX state */
const u8 *apu8TxBuffer[MCU_UART_NUM_INSTANCES];
u16 au16TxLength[MCU_UART_NUM_INSTANCES];
u16 au16TxIndex[MCU_UART_NUM_INSTANCES];
STD_tBool abTxBusy[MCU_UART_NUM_INSTANCES];
s8 as8TxDmaChannel[MCU_UART_NUM_INSTANCES];
/* RX ring buffer — filled by ISR or DMA in the background */
u8 aau8RxBuffer[MCU_UART_NUM_INSTANCES][MCU_UART_RX_BUFFER_SIZE]
__attribute__((aligned(MCU_UART_RX_BUFFER_SIZE)));
u16 au16RxHead[MCU_UART_NUM_INSTANCES];
u16 au16RxTail[MCU_UART_NUM_INSTANCES];
s8 as8RxDmaChannel[MCU_UART_NUM_INSTANCES];
} MCU_UART_tstrControl;
#endif /* MCU_UART_PRIV_H */

View File

@ -1,14 +0,0 @@
/******************************************************************************
* 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 */

View File

@ -1,86 +0,0 @@
/******************************************************************************
* 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 */

View File

@ -1,68 +0,0 @@
/******************************************************************************
* File: MCU_USB.h
* Component: MCU_USB
* Description: Public interface for the MCU USB-CDC driver.
* TX: SendByte, SendBuffer (both fire-and-forget via putchar_raw).
* RX: background ring buffer drained lazily from the SDK's
* stdio layer. ReadByte / bIsRxDataAvailable read from it.
*
* Layer: MCU (hardware abstraction)
*****************************************************************************/
#ifndef MCU_USB_H
#define MCU_USB_H
#include "STD_TYPES.h"
#define MCU_USB_WAIT_FOR_CONNECTION_DISABLED 0U
#define MCU_USB_WAIT_FOR_CONNECTION_ENABLED 1U
/* ------------------------------------------------------------------------ */
/* TX PUBLIC API */
/* ------------------------------------------------------------------------ */
STD_tenuResult MCU_USB_enuInit(void);
STD_tenuResult MCU_USB_enuSendByte(u8 u8Byte);
STD_tenuResult MCU_USB_enuSendBuffer(const u8 *pu8Data, u16 u16Length);
/* ------------------------------------------------------------------------ */
/* RX PUBLIC API */
/* ------------------------------------------------------------------------ */
/**
* @brief Read one byte from the USB RX ring buffer (non-blocking).
*
* Internally drains any pending bytes from the SDK's stdio layer into
* the ring buffer before checking. Returns immediately.
*
* @param pu8Byte Pointer to store the received byte.
* @return STD_OK byte read,
* STD_NULL_POINTER_ERROR if pu8Byte is NULL,
* STD_NOK if no data available.
*/
STD_tenuResult MCU_USB_enuReadByte(u8 *pu8Byte);
/**
* @brief Read up to u16MaxLength bytes from the USB RX ring buffer.
*
* @param pu8Data Output buffer.
* @param u16MaxLength Maximum bytes to read.
* @param pu16Read Actual bytes read.
* @return STD_OK at least one byte read,
* STD_NULL_POINTER_ERROR if pu8Data or pu16Read is NULL,
* STD_NOK if no data available.
*/
STD_tenuResult MCU_USB_enuReadBuffer(u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read);
/**
* @brief Check if USB RX data is available.
*
* Drains pending bytes from the SDK first, then checks the ring buffer.
*
* @return STD_TRUE if data available, STD_FALSE if empty.
*/
STD_tBool MCU_USB_bIsRxDataAvailable(void);
#endif /* MCU_USB_H */

View File

@ -1,229 +0,0 @@
/******************************************************************************
* File: MCU_USB_prg.c
* Component: MCU_USB
* Description: USB-CDC driver. TX via putchar_raw (fire-and-forget).
* RX via internal ring buffer, lazily drained from the SDK's
* stdio layer on every ReadByte / ReadBuffer / IsDataAvailable
* call. No separate RX task or interrupt needed the SDK's
* TinyUSB background task fills stdio internally, and we pull
* from stdio into our ring buffer on demand.
*
* Layer: MCU (hardware abstraction)
*****************************************************************************/
#include "STD_TYPES.h"
#include "pico/stdio_usb.h"
#include "pico/stdio.h"
#include "pico/time.h"
#include "MCU_USB.h"
#include "MCU_USB_priv.h"
#include "MCU_USB_cfg.h"
/* ------------------------------------------------------------------------ */
/* RX RING BUFFER */
/* ------------------------------------------------------------------------ */
/** @brief RX ring buffer size — must be power of 2. */
#define USB_RX_BUFFER_SIZE_BITS 6U
#define USB_RX_BUFFER_SIZE (1U << USB_RX_BUFFER_SIZE_BITS)
#define USB_RX_BUFFER_MASK (USB_RX_BUFFER_SIZE - 1U)
static u8 au8RxBuffer[USB_RX_BUFFER_SIZE];
static u16 u16RxHead = 0U;
static u16 u16RxTail = 0U;
/**
* @brief Drain any available bytes from the SDK's stdio into our ring buffer.
*
* Called lazily from ReadByte, ReadBuffer, and bIsRxDataAvailable.
* getchar_timeout_us(0) is non-blocking returns PICO_ERROR_TIMEOUT (-1)
* immediately if no data. We keep pulling until the SDK has nothing left
* or our ring buffer is full.
*/
static void vDrainStdio(void)
{
s32 s32ByteLoc;
u16 u16NextHeadLoc;
s32ByteLoc = (s32)getchar_timeout_us(0);
while (s32ByteLoc >= 0)
{
u16NextHeadLoc = (u16RxHead + 1U) & USB_RX_BUFFER_MASK;
/* If the ring buffer is full, stop draining (oldest data preserved,
* newest data from SDK is lost). Caller should read faster. */
if (u16NextHeadLoc == u16RxTail)
{
/* Buffer full — can't store this byte. Break out. */
break;
}
au8RxBuffer[u16RxHead] = (u8)s32ByteLoc;
u16RxHead = u16NextHeadLoc;
s32ByteLoc = (s32)getchar_timeout_us(0);
}
}
/* ========================================================================= */
/* INIT */
/* ========================================================================= */
STD_tenuResult MCU_USB_enuInit(void)
{
STD_tenuResult enuResultLoc = STD_OK;
STD_tBool bSdkInitSuccess = STD_FALSE;
bSdkInitSuccess = (stdio_usb_init() != 0) ? STD_TRUE : STD_FALSE;
if (bSdkInitSuccess == STD_FALSE)
{
enuResultLoc = STD_NOK;
}
else
{
#if MCU_USB_WAIT_FOR_CONNECTION == MCU_USB_WAIT_FOR_CONNECTION_ENABLED
absolute_time_t absTimeout = make_timeout_time_ms(MCU_USB_CONNECTION_TIMEOUT_MS);
STD_tBool bHostOpen = STD_FALSE;
STD_tBool bTimeoutReached = STD_FALSE;
do
{
sleep_ms(10);
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 (bHostOpen == STD_FALSE)
{
enuResultLoc = STD_NOK;
}
#endif
}
return enuResultLoc;
}
/* ========================================================================= */
/* SEND BYTE */
/* ========================================================================= */
STD_tenuResult MCU_USB_enuSendByte(u8 u8Byte)
{
STD_tenuResult enuResultLoc = STD_OK;
putchar_raw(u8Byte);
return enuResultLoc;
}
/* ========================================================================= */
/* SEND BUFFER */
/* ========================================================================= */
STD_tenuResult MCU_USB_enuSendBuffer(const u8 *pu8Data, u16 u16Length)
{
STD_tenuResult enuResultLoc = STD_OK;
u16 u16IndexLoc;
if (pu8Data == STD_NULL)
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else
{
for (u16IndexLoc = 0U; u16IndexLoc < u16Length; u16IndexLoc++)
{
putchar_raw(pu8Data[u16IndexLoc]);
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* READ BYTE (NON-BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_USB_enuReadByte(u8 *pu8Byte)
{
STD_tenuResult enuResultLoc = STD_OK;
if (pu8Byte == STD_NULL)
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else
{
/* Pull any pending data from SDK into our ring buffer */
vDrainStdio();
if (u16RxHead == u16RxTail)
{
enuResultLoc = STD_NOK;
}
else
{
*pu8Byte = au8RxBuffer[u16RxTail];
u16RxTail = (u16RxTail + 1U) & USB_RX_BUFFER_MASK;
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* READ BUFFER (NON-BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_USB_enuReadBuffer(u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read)
{
STD_tenuResult enuResultLoc = STD_OK;
if ((pu8Data == STD_NULL) || (pu16Read == STD_NULL))
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else
{
vDrainStdio();
u16 u16CountLoc = 0U;
while ((u16RxHead != u16RxTail) && (u16CountLoc < u16MaxLength))
{
pu8Data[u16CountLoc] = au8RxBuffer[u16RxTail];
u16RxTail = (u16RxTail + 1U) & USB_RX_BUFFER_MASK;
u16CountLoc++;
}
*pu16Read = u16CountLoc;
if (u16CountLoc == 0U)
{
enuResultLoc = STD_NOK;
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* RX DATA AVAILABLE CHECK */
/* ========================================================================= */
STD_tBool MCU_USB_bIsRxDataAvailable(void)
{
STD_tBool bResultLoc = STD_FALSE;
vDrainStdio();
if (u16RxHead != u16RxTail)
{
bResultLoc = STD_TRUE;
}
return bResultLoc;
}

View File

@ -1,18 +0,0 @@
/******************************************************************************
* 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 */

View File

@ -1,288 +0,0 @@
/******************************************************************************
* 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 */

1
common/HAL_COM Submodule

@ -0,0 +1 @@
Subproject commit 1cda81a2dd2938598a8049808ab197ef8c1af45d

1
common/HAL_LED Submodule

@ -0,0 +1 @@
Subproject commit b767b3c5b9cd4313f89e2184955b28e877431f8c

1
common/MCU_PIO Submodule

@ -0,0 +1 @@
Subproject commit 48cd1588449581892e60dde3e002a2649fe3f4f2

1
common/MCU_UART Submodule

@ -0,0 +1 @@
Subproject commit 5313ee98d21ca70d98ea6592961c2f6419d07b59

1
common/MCU_USB Submodule

@ -0,0 +1 @@
Subproject commit 9cb8f895714ebaaff1a6548c71d9dffbcab3ce65

1
common/STD_TYPES Submodule

@ -0,0 +1 @@
Subproject commit dadad59143bb6c8287b65d2841583f60f221fcc3