Implement HAL_COM with function-pointer transport dispatch

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.
This commit is contained in:
Mohamed Salem 2026-04-12 23:26:21 +02:00
parent f50e877ffc
commit 77152a0718
6 changed files with 233 additions and 64 deletions

View File

@ -36,5 +36,5 @@ void APP_CLSW_vRunnable(void)
* transport-agnostic HAL_COM layer. This will be replaced with real * transport-agnostic HAL_COM layer. This will be replaced with real
* color-switching command logic once the communication stack is * color-switching command logic once the communication stack is
* verified end-to-end. */ * 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);
} }

View File

@ -2,13 +2,83 @@
* File: HAL_COM_cfg.c * File: HAL_COM_cfg.c
* Component: HAL_COM * Component: HAL_COM
* Description: Configuration implementation for the HAL_COM abstraction. * Description: Configuration implementation for the HAL_COM abstraction.
* Holds the actual configuration values (transport selection * Defines the channel config array that wires each HAL_COM
* tables, timeout constants, buffer sizes) consumed by * channel to a specific MCU-level transport driver via function
* HAL_COM_prg.c. * 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 * Layer: HAL - configuration
*****************************************************************************/ *****************************************************************************/
#include "HAL_COM.h"
#include "HAL_COM_cfg.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,
},
};

View File

@ -2,10 +2,10 @@
* File: HAL_COM_cfg.h * File: HAL_COM_cfg.h
* Component: HAL_COM * Component: HAL_COM
* Description: Configuration header for the HAL_COM abstraction. * Description: Configuration header for the HAL_COM abstraction.
* Selects which physical transport(s) HAL_COM should route * Defines the communication channels and which MCU-level
* data through: MCU_UART, MCU_USB, or both (mirrored output). * transport each one routes through via function pointers.
* Also exposes any timeout / buffer sizing parameters used by * Adding a new channel or swapping a transport is a config-only
* the dispatch logic. * change no code in HAL_COM_prg.c needs to be modified.
* *
* Layer: HAL - configuration * Layer: HAL - configuration
*****************************************************************************/ *****************************************************************************/
@ -13,6 +13,27 @@
#ifndef HAL_COM_CFG_H #ifndef HAL_COM_CFG_H
#define 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 */ #endif /* HAL_COM_CFG_H */

View File

@ -2,14 +2,16 @@
* File: HAL_COM.h * File: HAL_COM.h
* Component: HAL_COM * Component: HAL_COM
* Description: Public interface for the HAL communication abstraction layer. * Description: Public interface for the HAL communication abstraction layer.
* Provides a transport-agnostic API for sending and receiving * Provides a transport-agnostic, multi-channel API for sending
* bytes to/from a host computer. Underneath, HAL_COM dispatches * and receiving bytes. Each channel is independently configured
* to one or more MCU-level drivers (MCU_UART for the hardware * with function pointers to an MCU-level driver (USB, UART,
* UART peripheral, MCU_USB for USB-CDC virtual serial) based on * SPI, I2C, or any future transport that matches the function
* the configuration in HAL_COM_cfg.h. * signatures).
* *
* Higher layers (e.g. APP_CLSW) should call only HAL_COM_* and * Higher layers (e.g. APP_CLSW) call HAL_COM_* with a channel
* stay unaware of which physical transport is in use. * 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) * Layer: HAL (hardware abstraction, one level above MCU drivers)
*****************************************************************************/ *****************************************************************************/
@ -17,9 +19,61 @@
#ifndef HAL_COM_H #ifndef HAL_COM_H
#define 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 "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 */ /* PUBLIC API */
@ -28,39 +82,37 @@
/** /**
* @brief Initialize the HAL communication layer's own internal state. * @brief Initialize the HAL communication layer's own internal state.
* *
* Does NOT initialize the underlying MCU drivers (MCU_USB, MCU_UART) - * Does NOT initialize the underlying MCU drivers SYS_ECU owns the init
* SYS_ECU owns the init sequence and calls each driver's enuInit() * sequence and calls each driver's enuInit() before calling this.
* separately in the correct dependency order before calling this.
* *
* @return STD_OK on success, * @return STD_OK on success, STD_NOK on failure.
* STD_NOK on failure.
*/ */
STD_tenuResult HAL_COM_enuInit(void); 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) * @param u8Channel Channel index (from HAL_COM_cfg.h channel enum).
* 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 failure.
* @param u8Byte The byte to transmit.
* @return STD_OK on success,
* STD_NOK on transmit 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) * The underlying driver may be blocking or non-blocking depending on the
* depending on the transport selection configured in HAL_COM_cfg.h. * 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. * @param u16Length Number of bytes to transmit.
* @return STD_OK on success, * @return STD_OK on success,
* STD_NULL_POINTER_ERROR if pu8Data is NULL, * 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 */ #endif /* HAL_COM_H */

View File

@ -2,13 +2,12 @@
* File: HAL_COM_prg.c * File: HAL_COM_prg.c
* Component: HAL_COM * Component: HAL_COM
* Description: Program (implementation) file for the HAL_COM abstraction. * Description: Program (implementation) file for the HAL_COM abstraction.
* Implements the public functions declared in HAL_COM.h by * Dispatches send operations to the MCU-level driver wired to
* dispatching to the MCU-level transport drivers (MCU_UART * each channel via function pointers in HAL_COM_astrChannelConfig.
* and/or MCU_USB) according to the active configuration.
* *
* Currently hardwired to MCU_USB. When MCU_UART is implemented, * The dispatch is a single indirect call no if/else chains,
* the dispatch will be driven by a transport-selection macro * no transport-specific code. Adding a new transport (SPI, I2C,
* in HAL_COM_cfg.h. * etc.) requires zero changes here only a new config entry.
* *
* Layer: HAL * Layer: HAL
*****************************************************************************/ *****************************************************************************/
@ -17,40 +16,52 @@
#include "HAL_COM_priv.h" #include "HAL_COM_priv.h"
#include "HAL_COM_cfg.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. */ /* INIT */
#include "MCU_USB.h" /* ========================================================================= */
STD_tenuResult HAL_COM_enuInit(void) STD_tenuResult HAL_COM_enuInit(void)
{ {
STD_tenuResult enuResultLoc = STD_OK; STD_tenuResult enuResultLoc = STD_OK;
/* HAL_COM has no internal state to set up yet. When transport /* HAL_COM has no internal state to set up yet. The underlying MCU
* selection logic or internal buffers are added, initialize them here. * drivers are already initialized by SYS_ECU before this function
* The underlying MCU drivers are already initialized by SYS_ECU * is called. When internal queuing or channel-level state is added,
* before this function is called. */ * initialize it here. */
return enuResultLoc; 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; STD_tenuResult enuResultLoc = STD_OK;
const HAL_COM_tstrChannelConfig *pstrCfgLoc = &HAL_COM_astrChannelConfig[u8Channel];
/* Dispatch to the active transport driver */ /* Dispatch through the function pointer configured for this channel.
enuResultLoc = MCU_USB_enuSendByte(u8Byte); * The driver's instance index is passed through transparently the
* caller never sees it. */
enuResultLoc = pstrCfgLoc->pfSendByte(pstrCfgLoc->u8Instance, u8Byte);
return enuResultLoc; 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; STD_tenuResult enuResultLoc = STD_OK;
const HAL_COM_tstrChannelConfig *pstrCfgLoc = &HAL_COM_astrChannelConfig[u8Channel];
/* Dispatch to the active transport driver. /* Dispatch through the function pointer configured for this channel.
* Null-pointer validation is handled inside MCU_USB_enuSendBuffer * Null-pointer validation is handled inside the MCU driver, so we
* so we don't duplicate the check here. */ * don't duplicate the check here. */
enuResultLoc = MCU_USB_enuSendBuffer(pu8Data, u16Length); enuResultLoc = pstrCfgLoc->pfSendBuffer(pstrCfgLoc->u8Instance, pu8Data, u16Length);
return enuResultLoc; return enuResultLoc;
} }

View File

@ -2,9 +2,9 @@
* File: HAL_COM_priv.h * File: HAL_COM_priv.h
* Component: HAL_COM * Component: HAL_COM
* Description: Private header for the HAL_COM abstraction. * Description: Private header for the HAL_COM abstraction.
* Contains internal macros, helper declarations, and any * Contains the extern declaration of the channel config array
* state/definitions that are only used inside the component. * (only accessed internally by _prg.c) and any other private
* Nothing declared here is exposed to external components. * definitions.
* *
* Layer: HAL - internal use only * Layer: HAL - internal use only
*****************************************************************************/ *****************************************************************************/
@ -12,6 +12,21 @@
#ifndef HAL_COM_PRIV_H #ifndef HAL_COM_PRIV_H
#define 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 */