From 77152a0718d71232e30f9f7ee0c489db906ecd56 Mon Sep 17 00:00:00 2001 From: Mohamed Salem Date: Sun, 12 Apr 2026 23:26:21 +0200 Subject: [PATCH] Implement HAL_COM with function-pointer transport dispatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multi-channel communication abstraction where each channel is wired to an MCU driver via function pointers in the config array. Adding a new transport (SPI, I2C, etc.) requires only a config entry — zero changes to HAL_COM_prg.c. Channel 0 defaults to USB-CDC via thin wrappers that normalize MCU_USB's signature. HAL_COM.h includes HAL_COM_cfg.h so callers can reference channel names. APP_CLSW updated to pass channel parameter. --- src/APP_CLSW/prg/APP_CLSW_prg.c | 2 +- src/HAL_COM/cfg/HAL_COM_cfg.c | 78 +++++++++++++++++++++-- src/HAL_COM/cfg/HAL_COM_cfg.h | 31 +++++++-- src/HAL_COM/inc/HAL_COM.h | 108 +++++++++++++++++++++++--------- src/HAL_COM/prg/HAL_COM_prg.c | 53 +++++++++------- src/HAL_COM/prg/HAL_COM_priv.h | 25 ++++++-- 6 files changed, 233 insertions(+), 64 deletions(-) diff --git a/src/APP_CLSW/prg/APP_CLSW_prg.c b/src/APP_CLSW/prg/APP_CLSW_prg.c index 739e50b..a6ab98d 100644 --- a/src/APP_CLSW/prg/APP_CLSW_prg.c +++ b/src/APP_CLSW/prg/APP_CLSW_prg.c @@ -36,5 +36,5 @@ void APP_CLSW_vRunnable(void) * transport-agnostic HAL_COM layer. This will be replaced with real * color-switching command logic once the communication stack is * verified end-to-end. */ - HAL_COM_enuSendBuffer((const u8 *)"this is a color switcher application!\r\n", 40U); + HAL_COM_enuSendBuffer((u8)HAL_COM_CHANNEL_0, (const u8 *)"this is a color switcher application!\r\n", 40U); } \ No newline at end of file diff --git a/src/HAL_COM/cfg/HAL_COM_cfg.c b/src/HAL_COM/cfg/HAL_COM_cfg.c index d5b3295..e4799cb 100644 --- a/src/HAL_COM/cfg/HAL_COM_cfg.c +++ b/src/HAL_COM/cfg/HAL_COM_cfg.c @@ -2,13 +2,83 @@ * File: HAL_COM_cfg.c * Component: HAL_COM * Description: Configuration implementation for the HAL_COM abstraction. - * Holds the actual configuration values (transport selection - * tables, timeout constants, buffer sizes) consumed by - * HAL_COM_prg.c. + * 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" -/* Configuration definitions will go here */ +/* 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. + * Its functions can be assigned directly to the config struct. */ + +/* ------------------------------------------------------------------------ */ +/* 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: + * [HAL_COM_CHANNEL_1] = { + * .pfSendByte = MCU_UART_enuSendByte, + * .pfSendBuffer = MCU_UART_enuSendBuffer, + * .u8Instance = 0U, + * }, + * + * To add SPI, I2C, or any other transport: create a matching MCU driver + * with the same function signature, or add a wrapper here like vUsbSendByte. + */ +const HAL_COM_tstrChannelConfig HAL_COM_astrChannelConfig[HAL_COM_NUM_CHANNELS] = +{ + [HAL_COM_CHANNEL_0] = + { + .pfSendByte = vUsbSendByte, + .pfSendBuffer = vUsbSendBuffer, + .u8Instance = 0U, + }, +}; diff --git a/src/HAL_COM/cfg/HAL_COM_cfg.h b/src/HAL_COM/cfg/HAL_COM_cfg.h index 28f1aa7..8af7c77 100644 --- a/src/HAL_COM/cfg/HAL_COM_cfg.h +++ b/src/HAL_COM/cfg/HAL_COM_cfg.h @@ -2,10 +2,10 @@ * File: HAL_COM_cfg.h * Component: HAL_COM * Description: Configuration header for the HAL_COM abstraction. - * Selects which physical transport(s) HAL_COM should route - * data through: MCU_UART, MCU_USB, or both (mirrored output). - * Also exposes any timeout / buffer sizing parameters used by - * the dispatch logic. + * 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 *****************************************************************************/ @@ -13,6 +13,27 @@ #ifndef HAL_COM_CFG_H #define HAL_COM_CFG_H -/* Configuration constants and transport selection macros will go here */ +#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 */ diff --git a/src/HAL_COM/inc/HAL_COM.h b/src/HAL_COM/inc/HAL_COM.h index cc5fc0c..c28b032 100644 --- a/src/HAL_COM/inc/HAL_COM.h +++ b/src/HAL_COM/inc/HAL_COM.h @@ -2,14 +2,16 @@ * File: HAL_COM.h * Component: HAL_COM * Description: Public interface for the HAL communication abstraction layer. - * Provides a transport-agnostic API for sending and receiving - * bytes to/from a host computer. Underneath, HAL_COM dispatches - * to one or more MCU-level drivers (MCU_UART for the hardware - * UART peripheral, MCU_USB for USB-CDC virtual serial) based on - * the configuration in HAL_COM_cfg.h. + * 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) should call only HAL_COM_* and - * stay unaware of which physical transport is in use. + * 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) *****************************************************************************/ @@ -17,9 +19,61 @@ #ifndef HAL_COM_H #define HAL_COM_H -/* STD_TYPES provides fixed-width typedefs (u8, u32) and the STD_tenuResult - * enum used to report success/failure from every function. */ #include "STD_TYPES.h" +#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); + +/* ------------------------------------------------------------------------ */ +/* 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 */ + u8 u8Instance; /**< Peripheral instance to pass through */ +} HAL_COM_tstrChannelConfig; /* ------------------------------------------------------------------------ */ /* PUBLIC API */ @@ -28,39 +82,37 @@ /** * @brief Initialize the HAL communication layer's own internal state. * - * Does NOT initialize the underlying MCU drivers (MCU_USB, MCU_UART) - - * SYS_ECU owns the init sequence and calls each driver's enuInit() - * separately in the correct dependency order before calling this. + * 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. + * @return STD_OK on success, STD_NOK on failure. */ STD_tenuResult HAL_COM_enuInit(void); /** - * @brief Send a single byte through the active transport. + * @brief Send a single byte through the specified channel. * - * Dispatches to MCU_USB_enuSendByte or MCU_UART_enuSendByte (or both) - * depending on the transport selection configured in HAL_COM_cfg.h. - * - * @param u8Byte The byte to transmit. - * @return STD_OK on success, - * STD_NOK on transmit failure. + * @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 u8Byte); +STD_tenuResult HAL_COM_enuSendByte(u8 u8Channel, u8 u8Byte); /** - * @brief Send a buffer of bytes through the active transport. + * @brief Send a buffer through the specified channel. * - * Dispatches to MCU_USB_enuSendBuffer or MCU_UART_enuSendBuffer (or both) - * depending on the transport selection configured in HAL_COM_cfg.h. + * 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 pu8Data Pointer to the byte buffer to transmit. Must not be NULL. + * @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 transmit failure. + * STD_NOK on failure. */ -STD_tenuResult HAL_COM_enuSendBuffer(const u8 *pu8Data, u16 u16Length); +STD_tenuResult HAL_COM_enuSendBuffer(u8 u8Channel, const u8 *pu8Data, u16 u16Length); #endif /* HAL_COM_H */ \ No newline at end of file diff --git a/src/HAL_COM/prg/HAL_COM_prg.c b/src/HAL_COM/prg/HAL_COM_prg.c index f0c2fd1..53b0dfc 100644 --- a/src/HAL_COM/prg/HAL_COM_prg.c +++ b/src/HAL_COM/prg/HAL_COM_prg.c @@ -2,13 +2,12 @@ * File: HAL_COM_prg.c * Component: HAL_COM * Description: Program (implementation) file for the HAL_COM abstraction. - * Implements the public functions declared in HAL_COM.h by - * dispatching to the MCU-level transport drivers (MCU_UART - * and/or MCU_USB) according to the active configuration. + * Dispatches send operations to the MCU-level driver wired to + * each channel via function pointers in HAL_COM_astrChannelConfig. * - * Currently hardwired to MCU_USB. When MCU_UART is implemented, - * the dispatch will be driven by a transport-selection macro - * in HAL_COM_cfg.h. + * 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 *****************************************************************************/ @@ -17,40 +16,52 @@ #include "HAL_COM_priv.h" #include "HAL_COM_cfg.h" -/* MCU_USB is the active transport for now. When MCU_UART is ready, - * a config macro will select which driver(s) to call here. */ -#include "MCU_USB.h" +/* ========================================================================= */ +/* INIT */ +/* ========================================================================= */ STD_tenuResult HAL_COM_enuInit(void) { STD_tenuResult enuResultLoc = STD_OK; - /* HAL_COM has no internal state to set up yet. When transport - * selection logic or internal buffers are added, initialize them here. - * The underlying MCU drivers are already initialized by SYS_ECU - * before this function is called. */ + /* 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; } -STD_tenuResult HAL_COM_enuSendByte(u8 u8Byte) +/* ========================================================================= */ +/* 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 to the active transport driver */ - enuResultLoc = MCU_USB_enuSendByte(u8Byte); + /* 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; } -STD_tenuResult HAL_COM_enuSendBuffer(const u8 *pu8Data, u16 u16Length) +/* ========================================================================= */ +/* 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 to the active transport driver. - * Null-pointer validation is handled inside MCU_USB_enuSendBuffer - * so we don't duplicate the check here. */ - enuResultLoc = MCU_USB_enuSendBuffer(pu8Data, u16Length); + /* 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; } diff --git a/src/HAL_COM/prg/HAL_COM_priv.h b/src/HAL_COM/prg/HAL_COM_priv.h index e1a69a8..f6f4fce 100644 --- a/src/HAL_COM/prg/HAL_COM_priv.h +++ b/src/HAL_COM/prg/HAL_COM_priv.h @@ -2,9 +2,9 @@ * File: HAL_COM_priv.h * Component: HAL_COM * Description: Private header for the HAL_COM abstraction. - * Contains internal macros, helper declarations, and any - * state/definitions that are only used inside the component. - * Nothing declared here is exposed to external components. + * 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 *****************************************************************************/ @@ -12,6 +12,21 @@ #ifndef HAL_COM_PRIV_H #define HAL_COM_PRIV_H -/* Private declarations, internal macros and helpers will go here */ +#include "HAL_COM.h" +#include "HAL_COM_cfg.h" -#endif /* HAL_COM_PRIV_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 */ \ No newline at end of file