Redesign RX to non-blocking ring buffer model
UART/USB now receive in the background and store into ring buffers. Callers read from the buffer via non-blocking ReadByte/ReadBuffer. Removed blocking ReceiveByte, async ReceiveBuffer with request state and callbacks. MCU_USB uses lazy drain from SDK stdio into its own ring buffer. MCU_UART ring buffer unchanged (ISR/DMA). HAL_COM updated with ReadByte/ReadBuffer function pointer dispatch. APP_CLSW updated to use new ReadByte API.
This commit is contained in:
parent
863da8f75c
commit
3d5e63c790
@ -144,7 +144,7 @@ void APP_CLSW_vRunnable(void)
|
||||
while (bDataAvailableLoc == STD_TRUE)
|
||||
{
|
||||
/* Read one byte */
|
||||
enuRxResultLoc = HAL_COM_enuReceiveByte(APP_CLSW_COM_CHANNEL, &u8ByteLoc);
|
||||
enuRxResultLoc = HAL_COM_enuReadByte(APP_CLSW_COM_CHANNEL, &u8ByteLoc);
|
||||
|
||||
if (enuRxResultLoc == STD_OK)
|
||||
{
|
||||
|
||||
@ -52,20 +52,20 @@ static STD_tenuResult vUsbSendBuffer(u8 u8Instance, const u8 *pu8Data, u16 u16Le
|
||||
* (u8 u8Instance as the first parameter), so no wrapper is needed
|
||||
* for TX or RX. Its functions can be assigned directly. */
|
||||
|
||||
/* --- USB RX wrappers (same rationale as TX — normalize missing instance) --- */
|
||||
/* --- USB RX wrappers (normalize missing instance parameter) --- */
|
||||
|
||||
/**
|
||||
* @brief Wrapper for MCU_USB_enuReceiveByte to match HAL_COM_tpfReceiveByte.
|
||||
*/
|
||||
static STD_tenuResult vUsbReceiveByte(u8 u8Instance, u8 *pu8Byte)
|
||||
static STD_tenuResult vUsbReadByte(u8 u8Instance, u8 *pu8Byte)
|
||||
{
|
||||
(void)u8Instance;
|
||||
return MCU_USB_enuReceiveByte(pu8Byte);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wrapper for MCU_USB_bIsRxDataAvailable to match HAL_COM_tpfIsRxDataAvailable.
|
||||
*/
|
||||
static STD_tBool vUsbIsRxDataAvailable(u8 u8Instance)
|
||||
{
|
||||
(void)u8Instance;
|
||||
@ -98,7 +98,8 @@ const HAL_COM_tstrChannelConfig HAL_COM_astrChannelConfig[HAL_COM_NUM_CHANNELS]
|
||||
{
|
||||
.pfSendByte = vUsbSendByte,
|
||||
.pfSendBuffer = vUsbSendBuffer,
|
||||
.pfReceiveByte = vUsbReceiveByte,
|
||||
.pfReadByte = vUsbReadByte,
|
||||
.pfReadBuffer = vUsbReadBuffer,
|
||||
.pfIsRxDataAvailable = vUsbIsRxDataAvailable,
|
||||
.u8Instance = 0U,
|
||||
},
|
||||
|
||||
@ -54,19 +54,17 @@ typedef STD_tenuResult (*HAL_COM_tpfSendByte)(u8 u8Instance, u8 u8Byte);
|
||||
typedef STD_tenuResult (*HAL_COM_tpfSendBuffer)(u8 u8Instance, const u8 *pu8Data, u16 u16Length);
|
||||
|
||||
/**
|
||||
* @brief Generic receive-byte function pointer type (blocking).
|
||||
*
|
||||
* @param u8Instance Peripheral instance index.
|
||||
* @param pu8Byte Pointer to store the received byte.
|
||||
* @return STD_tenuResult
|
||||
* @brief Generic read-byte function pointer type (non-blocking).
|
||||
*/
|
||||
typedef STD_tenuResult (*HAL_COM_tpfReceiveByte)(u8 u8Instance, u8 *pu8Byte);
|
||||
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.
|
||||
*
|
||||
* @param u8Instance Peripheral instance index.
|
||||
* @return STD_tBool
|
||||
*/
|
||||
typedef STD_tBool (*HAL_COM_tpfIsRxDataAvailable)(u8 u8Instance);
|
||||
|
||||
@ -89,7 +87,8 @@ typedef struct
|
||||
{
|
||||
HAL_COM_tpfSendByte pfSendByte; /**< Driver's send-byte function */
|
||||
HAL_COM_tpfSendBuffer pfSendBuffer; /**< Driver's send-buffer function */
|
||||
HAL_COM_tpfReceiveByte pfReceiveByte; /**< Driver's receive-byte 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;
|
||||
@ -135,21 +134,17 @@ STD_tenuResult HAL_COM_enuSendByte(u8 u8Channel, u8 u8Byte);
|
||||
STD_tenuResult HAL_COM_enuSendBuffer(u8 u8Channel, const u8 *pu8Data, u16 u16Length);
|
||||
|
||||
/**
|
||||
* @brief Receive one byte through the specified channel (blocking).
|
||||
*
|
||||
* @param u8Channel Channel index.
|
||||
* @param pu8Byte Pointer to store the received byte. Must not be NULL.
|
||||
* @return STD_OK byte received,
|
||||
* STD_NULL_POINTER_ERROR if pu8Byte is NULL.
|
||||
* @brief Read one byte from the specified channel (non-blocking).
|
||||
*/
|
||||
STD_tenuResult HAL_COM_enuReceiveByte(u8 u8Channel, u8 *pu8Byte);
|
||||
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.
|
||||
*
|
||||
* @param u8Channel Channel index.
|
||||
* @return STD_TRUE if at least one byte is available,
|
||||
* STD_FALSE if no data waiting.
|
||||
*/
|
||||
STD_tBool HAL_COM_bIsRxDataAvailable(u8 u8Channel);
|
||||
|
||||
|
||||
@ -70,12 +70,26 @@ STD_tenuResult HAL_COM_enuSendBuffer(u8 u8Channel, const u8 *pu8Data, u16 u16Len
|
||||
/* RECEIVE BYTE (BLOCKING) */
|
||||
/* ========================================================================= */
|
||||
|
||||
STD_tenuResult HAL_COM_enuReceiveByte(u8 u8Channel, u8 *pu8Byte)
|
||||
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->pfReceiveByte(pstrCfgLoc->u8Instance, pu8Byte);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1,35 +1,14 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_UART_cfg.c
|
||||
* Component: MCU_UART
|
||||
* Description: Configuration implementation for the MCU_UART driver.
|
||||
* Defines the MCU_UART_astrConfig[] array that holds the actual
|
||||
* per-instance UART settings. Each entry is initialized using
|
||||
* the named macros from MCU_UART_cfg.h so that no magic numbers
|
||||
* appear in the initializer.
|
||||
* Description: Configuration array definition for the MCU_UART driver.
|
||||
*
|
||||
* Layer: MCU (hardware abstraction) - configuration
|
||||
*****************************************************************************/
|
||||
|
||||
/* MCU_UART.h is included first because it defines the struct type
|
||||
* (MCU_UART_tstrConfig) and the enum types (MCU_UART_tenuDataBits, etc.)
|
||||
* that are used in the array definition below. */
|
||||
#include "MCU_UART.h"
|
||||
#include "MCU_UART_cfg.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* CONFIGURATION ARRAY DEFINITION */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Per-instance UART configuration, indexed by MCU_UART_tenuInstance.
|
||||
*
|
||||
* [MCU_UART_INSTANCE_0] = uart0 on the RP2040. Configured for standard
|
||||
* 8-N-1 at 115200 baud on GP0 (TX) / GP1 (RX), with DMA-based async TX
|
||||
* and no TX-complete callback.
|
||||
*
|
||||
* To add uart1: add a [MCU_UART_INSTANCE_1] entry here with the
|
||||
* corresponding MCU_UART_1_* macros defined in MCU_UART_cfg.h.
|
||||
*/
|
||||
const MCU_UART_tstrConfig MCU_UART_astrConfig[MCU_UART_NUM_INSTANCES] =
|
||||
{
|
||||
[MCU_UART_INSTANCE_0] =
|
||||
@ -43,6 +22,5 @@ const MCU_UART_tstrConfig MCU_UART_astrConfig[MCU_UART_NUM_INSTANCES] =
|
||||
.enuTxAsyncMode = MCU_UART_0_TX_ASYNC_MODE,
|
||||
.pfTxCompleteCallback = MCU_UART_0_TX_COMPLETE_CALLBACK,
|
||||
.enuRxAsyncMode = MCU_UART_0_RX_ASYNC_MODE,
|
||||
.pfRxCallback = MCU_UART_0_RX_CALLBACK,
|
||||
},
|
||||
};
|
||||
@ -1,10 +1,9 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_UART_cfg.h
|
||||
* Component: MCU_UART
|
||||
* Description: Configuration header for the MCU_UART driver.
|
||||
* Defines which UART instances are active, their GPIO pin
|
||||
* assignments, baud rates, data format, TX/RX async mechanisms,
|
||||
* callbacks, and RX buffer sizing.
|
||||
* 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
|
||||
*****************************************************************************/
|
||||
@ -28,54 +27,25 @@ typedef enum
|
||||
/* RX RING BUFFER SIZING */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/** @brief Number of address bits for the RX ring buffer. The actual buffer
|
||||
* size is 2^N bytes. Must be a power of 2 because DMA ring mode
|
||||
* wraps the write address at a 2^N boundary. Applies to all
|
||||
* instances. Change this single value to resize the buffer:
|
||||
* 5 = 32 bytes, 6 = 64 bytes, 7 = 128 bytes, 8 = 256 bytes. */
|
||||
/** @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
|
||||
|
||||
/** @brief Derived buffer size in bytes. Do not edit — change
|
||||
* MCU_UART_RX_BUFFER_SIZE_BITS instead. */
|
||||
#define MCU_UART_RX_BUFFER_SIZE (1U << MCU_UART_RX_BUFFER_SIZE_BITS)
|
||||
|
||||
/** @brief Bitmask for ring buffer index wrapping. */
|
||||
#define MCU_UART_RX_BUFFER_MASK (MCU_UART_RX_BUFFER_SIZE - 1U)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* INSTANCE 0 CONFIGURATION */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/** @brief GPIO pin for UART0 TX. GP0 is the default for uart0. */
|
||||
#define MCU_UART_0_TX_PIN 0U
|
||||
|
||||
/** @brief GPIO pin for UART0 RX. GP1 is the default for uart0. */
|
||||
#define MCU_UART_0_RX_PIN 1U
|
||||
|
||||
/** @brief Baud rate for UART0 in bits per second. */
|
||||
#define MCU_UART_0_BAUD_RATE 115200U
|
||||
|
||||
/** @brief Data bits per frame for UART0. */
|
||||
#define MCU_UART_0_DATA_BITS MCU_UART_DATA_BITS_8
|
||||
|
||||
/** @brief Stop bits per frame for UART0. */
|
||||
#define MCU_UART_0_STOP_BITS MCU_UART_STOP_BITS_1
|
||||
|
||||
/** @brief Parity mode for UART0. */
|
||||
#define MCU_UART_0_PARITY MCU_UART_PARITY_NONE
|
||||
|
||||
/** @brief Async TX mechanism for UART0 (DMA or ISR). */
|
||||
#define MCU_UART_0_TX_ASYNC_MODE MCU_UART_ASYNC_DMA
|
||||
|
||||
/** @brief TX-complete callback for UART0. STD_NULL to disable. */
|
||||
#define MCU_UART_0_TX_COMPLETE_CALLBACK STD_NULL
|
||||
|
||||
/** @brief Async RX mechanism for UART0.
|
||||
* DMA: hardware fills ring buffer, polling only (callback ignored).
|
||||
* ISR: interrupt-driven, pfRxCallback fires per byte. */
|
||||
#define MCU_UART_0_RX_ASYNC_MODE MCU_UART_ASYNC_ISR
|
||||
|
||||
/** @brief RX callback for UART0. Per-byte, ISR mode only. STD_NULL to disable. */
|
||||
#define MCU_UART_0_RX_CALLBACK STD_NULL
|
||||
|
||||
#endif /* MCU_UART_CFG_H */
|
||||
@ -2,20 +2,11 @@
|
||||
* File: MCU_UART.h
|
||||
* Component: MCU_UART
|
||||
* Description: Public interface for the MCU UART driver component.
|
||||
* This header exposes the functions, types, and configuration
|
||||
* value enumerations that other components are allowed to use
|
||||
* when interacting with the hardware UART peripheral(s) on the
|
||||
* RP2040 microcontroller.
|
||||
*
|
||||
* Two send-buffer modes are provided:
|
||||
* - SendBuffer (default, non-blocking): starts an async
|
||||
* transfer via DMA or ISR and returns immediately.
|
||||
* - SendBufferBlocking: loops byte-by-byte and returns
|
||||
* only after all data is transmitted.
|
||||
* Both invoke the TX-complete callback (if configured).
|
||||
*
|
||||
* All public functions take a u8 instance parameter so the
|
||||
* caller selects which UART peripheral to operate on.
|
||||
* 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)
|
||||
*****************************************************************************/
|
||||
@ -23,27 +14,19 @@
|
||||
#ifndef MCU_UART_H
|
||||
#define MCU_UART_H
|
||||
|
||||
/* STD_TYPES provides fixed-width typedefs (u8, u16, u32), STD_tenuResult,
|
||||
* STD_tBool, STD_tpfCallbackFunc, and STD_NULL. */
|
||||
#include "STD_TYPES.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* CONFIGURATION VALUE TYPES */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Parity mode options for UART data framing.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
MCU_UART_PARITY_NONE = 0U, /**< No parity bit transmitted */
|
||||
MCU_UART_PARITY_EVEN, /**< Even parity */
|
||||
MCU_UART_PARITY_ODD /**< Odd parity */
|
||||
MCU_UART_PARITY_NONE = 0U,
|
||||
MCU_UART_PARITY_EVEN,
|
||||
MCU_UART_PARITY_ODD
|
||||
} MCU_UART_tenuParity;
|
||||
|
||||
/**
|
||||
* @brief Number of data bits per UART frame.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
MCU_UART_DATA_BITS_5 = 5U,
|
||||
@ -52,184 +35,90 @@ typedef enum
|
||||
MCU_UART_DATA_BITS_8 = 8U
|
||||
} MCU_UART_tenuDataBits;
|
||||
|
||||
/**
|
||||
* @brief Number of stop bits per UART frame.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
MCU_UART_STOP_BITS_1 = 1U,
|
||||
MCU_UART_STOP_BITS_2 = 2U
|
||||
} MCU_UART_tenuStopBits;
|
||||
|
||||
/**
|
||||
* @brief Async transmit mechanism selection.
|
||||
*
|
||||
* DMA: hardware DMA channel moves bytes autonomously. Zero CPU during
|
||||
* transfer. Best for large buffers.
|
||||
* ISR: UART TX interrupt fires per byte. Uses CPU per-byte but does not
|
||||
* consume a DMA channel.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
MCU_UART_ASYNC_DMA = 0U, /**< Use DMA for non-blocking TX */
|
||||
MCU_UART_ASYNC_ISR /**< Use UART TX interrupt for non-blocking TX */
|
||||
MCU_UART_ASYNC_DMA = 0U,
|
||||
MCU_UART_ASYNC_ISR
|
||||
} MCU_UART_tenuAsyncMode;
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* CONFIGURATION STRUCTURE */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Per-instance UART configuration.
|
||||
*
|
||||
* One entry per hardware UART instance, stored in MCU_UART_astrConfig[].
|
||||
* The array index IS the UART instance number (0 = uart0, 1 = uart1).
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
u8 u8TxPin; /**< GPIO number for TX */
|
||||
u8 u8RxPin; /**< GPIO number for RX */
|
||||
u32 u32BaudRate; /**< Baud rate in bps */
|
||||
MCU_UART_tenuDataBits enuDataBits; /**< Data bits per frame */
|
||||
MCU_UART_tenuStopBits enuStopBits; /**< Stop bits per frame */
|
||||
MCU_UART_tenuParity enuParity; /**< Parity mode */
|
||||
MCU_UART_tenuAsyncMode enuTxAsyncMode; /**< DMA or ISR for non-blocking TX */
|
||||
STD_tpfCallbackFunc pfTxCompleteCallback; /**< Called when TX finishes. STD_NULL to ignore. */
|
||||
MCU_UART_tenuAsyncMode enuRxAsyncMode; /**< DMA or ISR for RX data capture */
|
||||
STD_tpfCallbackFunc pfRxCallback; /**< Called per byte received (ISR mode only).
|
||||
DMA mode = polling only, callback ignored.
|
||||
STD_NULL to ignore. */
|
||||
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;
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* PUBLIC API */
|
||||
/* 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 Initialize all configured UART instances.
|
||||
* @brief Read one byte from the RX ring buffer (non-blocking).
|
||||
*
|
||||
* Iterates MCU_UART_astrConfig[] and for each entry: sets baud rate,
|
||||
* assigns GPIO pins, configures data format, claims DMA channel (if DMA
|
||||
* mode), and sets up the appropriate IRQ handler.
|
||||
*
|
||||
* @return STD_OK on success, STD_NOK if any instance fails.
|
||||
*/
|
||||
STD_tenuResult MCU_UART_enuInit(void);
|
||||
|
||||
/**
|
||||
* @brief Send a single byte (blocking).
|
||||
*
|
||||
* @param u8Instance UART instance index (0 = uart0, 1 = uart1).
|
||||
* @param u8Byte The byte to transmit.
|
||||
* @return STD_OK on success, STD_NOK on failure.
|
||||
*/
|
||||
STD_tenuResult MCU_UART_enuSendByte(u8 u8Instance, u8 u8Byte);
|
||||
|
||||
/**
|
||||
* @brief Send a buffer of bytes (non-blocking, default).
|
||||
*
|
||||
* Starts an async transfer via DMA or UART TX ISR (per config). Returns
|
||||
* immediately. The caller MUST keep the buffer valid until the callback
|
||||
* fires or MCU_UART_bIsTxBusy() returns STD_FALSE.
|
||||
*
|
||||
* The TX-complete callback is invoked from interrupt context.
|
||||
* 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 pu8Data Pointer to buffer. Must not be NULL. Must stay valid.
|
||||
* @param u16Length Number of bytes to transmit.
|
||||
* @return STD_OK transfer started,
|
||||
* STD_NULL_POINTER_ERROR if pu8Data is NULL,
|
||||
* STD_NOK if a transfer is already in progress.
|
||||
* @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_enuSendBuffer(u8 u8Instance, const u8 *pu8Data, u16 u16Length);
|
||||
STD_tenuResult MCU_UART_enuReadByte(u8 u8Instance, u8 *pu8Byte);
|
||||
|
||||
/**
|
||||
* @brief Send a buffer of bytes (blocking).
|
||||
* @brief Read up to u16MaxLength bytes from the RX ring buffer (non-blocking).
|
||||
*
|
||||
* Blocks until all bytes are transmitted. Calls the TX-complete callback
|
||||
* synchronously at the end (if configured, not NULL).
|
||||
* 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 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.
|
||||
* @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_enuSendBufferBlocking(u8 u8Instance, const u8 *pu8Data, u16 u16Length);
|
||||
STD_tenuResult MCU_UART_enuReadBuffer(u8 u8Instance, u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read);
|
||||
|
||||
/**
|
||||
* @brief Check if an async TX transfer is in progress.
|
||||
* @brief Check if the RX ring buffer has data.
|
||||
*
|
||||
* @param u8Instance UART instance index.
|
||||
* @return STD_TRUE if a non-blocking SendBuffer is still transmitting,
|
||||
* STD_FALSE if the driver is idle and ready for a new transfer.
|
||||
*/
|
||||
STD_tBool MCU_UART_bIsTxBusy(u8 u8Instance);
|
||||
|
||||
/**
|
||||
* @brief Receive one byte (blocking).
|
||||
*
|
||||
* Waits until a byte is available in the internal ring buffer (filled
|
||||
* by RX ISR or DMA in the background), then returns it.
|
||||
*
|
||||
* @param u8Instance UART instance index.
|
||||
* @param pu8Byte Pointer to store the received byte. Must not be NULL.
|
||||
* @return STD_OK byte received,
|
||||
* STD_NULL_POINTER_ERROR if pu8Byte is NULL.
|
||||
*/
|
||||
STD_tenuResult MCU_UART_enuReceiveByte(u8 u8Instance, u8 *pu8Byte);
|
||||
|
||||
/**
|
||||
* @brief Receive a buffer of bytes (non-blocking, default).
|
||||
*
|
||||
* Registers a request for u16Length bytes. As bytes arrive in the ring
|
||||
* buffer, they are copied to pu8Data. When all requested bytes have been
|
||||
* collected, the RX callback fires (if configured, not STD_NULL).
|
||||
* Returns immediately after registering the request.
|
||||
*
|
||||
* The caller MUST keep the buffer valid until the callback fires or
|
||||
* MCU_UART_bIsRxBusy() returns STD_FALSE.
|
||||
*
|
||||
* @param u8Instance UART instance index.
|
||||
* @param pu8Data Pointer to buffer to fill. Must not be NULL.
|
||||
* @param u16Length Number of bytes to receive.
|
||||
* @return STD_OK request registered,
|
||||
* STD_NULL_POINTER_ERROR if pu8Data is NULL,
|
||||
* STD_NOK if a receive request is already active.
|
||||
*/
|
||||
STD_tenuResult MCU_UART_enuReceiveBuffer(u8 u8Instance, u8 *pu8Data, u16 u16Length);
|
||||
|
||||
/**
|
||||
* @brief Receive a buffer of bytes (blocking).
|
||||
*
|
||||
* Blocks until u16Length bytes have been received from the ring buffer.
|
||||
* Calls the RX callback at the end (if configured).
|
||||
*
|
||||
* @param u8Instance UART instance index.
|
||||
* @param pu8Data Pointer to buffer to fill. Must not be NULL.
|
||||
* @param u16Length Number of bytes to receive.
|
||||
* @return STD_OK all bytes received,
|
||||
* STD_NULL_POINTER_ERROR if pu8Data is NULL.
|
||||
*/
|
||||
STD_tenuResult MCU_UART_enuReceiveBufferBlocking(u8 u8Instance, u8 *pu8Data, u16 u16Length);
|
||||
|
||||
/**
|
||||
* @brief Check if the RX ring buffer has data waiting.
|
||||
*
|
||||
* @param u8Instance UART instance index.
|
||||
* @return STD_TRUE if at least one byte is available,
|
||||
* STD_FALSE if the ring buffer is empty.
|
||||
* @return STD_TRUE if at least one byte available, STD_FALSE if empty.
|
||||
*/
|
||||
STD_tBool MCU_UART_bIsRxDataAvailable(u8 u8Instance);
|
||||
|
||||
/**
|
||||
* @brief Check if an async ReceiveBuffer request is in progress.
|
||||
*
|
||||
* @param u8Instance UART instance index.
|
||||
* @return STD_TRUE if a non-blocking ReceiveBuffer is still collecting,
|
||||
* STD_FALSE if idle.
|
||||
*/
|
||||
STD_tBool MCU_UART_bIsRxBusy(u8 u8Instance);
|
||||
|
||||
#endif /* MCU_UART_H */
|
||||
@ -1,13 +1,9 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_UART_prg.c
|
||||
* Component: MCU_UART
|
||||
* Description: Program (implementation) file for the MCU_UART driver.
|
||||
* TX: blocking + non-blocking (DMA or ISR) with callback.
|
||||
* RX: ISR or DMA fills a ring buffer in the background.
|
||||
* ReceiveByte (blocking) reads from the ring buffer.
|
||||
* ReceiveBuffer (non-blocking) registers a request — bytes
|
||||
* are copied from ring buffer to caller's buffer as they
|
||||
* arrive. ReceiveBufferBlocking waits until N bytes collected.
|
||||
* 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)
|
||||
*****************************************************************************/
|
||||
@ -25,11 +21,7 @@
|
||||
/* INSTANCE LOOKUP TABLE */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static uart_inst_t * const apstrInstances[] =
|
||||
{
|
||||
uart0,
|
||||
uart1,
|
||||
};
|
||||
static uart_inst_t * const apstrInstances[] = { uart0, uart1 };
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* RUNTIME STATE */
|
||||
@ -51,31 +43,17 @@ static void vCallTxCallback(u8 u8Instance)
|
||||
}
|
||||
}
|
||||
|
||||
static void vCallRxCallback(u8 u8Instance)
|
||||
{
|
||||
STD_tpfCallbackFunc pfCbLoc = MCU_UART_astrConfig[u8Instance].pfRxCallback;
|
||||
|
||||
if (pfCbLoc != STD_NULL)
|
||||
{
|
||||
pfCbLoc();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the current RX ring buffer head position.
|
||||
* In DMA mode, derived from the DMA write pointer.
|
||||
* In ISR mode, read directly from the control struct.
|
||||
*/
|
||||
/** @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 s8RxChLoc = strControl.as8RxDmaChannel[u8Instance];
|
||||
u32 u32WriteAddrLoc = (u32)dma_channel_hw_addr((u32)s8RxChLoc)->write_addr;
|
||||
u32 u32BufferBaseLoc = (u32)(&strControl.aau8RxBuffer[u8Instance][0]);
|
||||
u16HeadLoc = (u16)((u32WriteAddrLoc - u32BufferBaseLoc) & MCU_UART_RX_BUFFER_MASK);
|
||||
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
|
||||
{
|
||||
@ -85,52 +63,18 @@ static u16 u16GetRxHead(u8 u8Instance)
|
||||
return u16HeadLoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to fulfill an active async ReceiveBuffer request by copying
|
||||
* available bytes from the ring buffer to the caller's buffer.
|
||||
* Called from the RX ISR after new bytes land in the ring buffer,
|
||||
* or from polling contexts.
|
||||
*/
|
||||
static void vFulfillRxRequest(u8 u8Instance)
|
||||
{
|
||||
u16 u16HeadLoc = u16GetRxHead(u8Instance);
|
||||
u16 u16TailLoc = strControl.au16RxTail[u8Instance];
|
||||
|
||||
/* Copy available bytes from ring buffer to request buffer */
|
||||
while ((u16HeadLoc != u16TailLoc) &&
|
||||
(strControl.au16RxReqIndex[u8Instance] < strControl.au16RxReqLength[u8Instance]))
|
||||
{
|
||||
strControl.apu8RxReqBuffer[u8Instance][strControl.au16RxReqIndex[u8Instance]] =
|
||||
strControl.aau8RxBuffer[u8Instance][u16TailLoc];
|
||||
strControl.au16RxReqIndex[u8Instance]++;
|
||||
u16TailLoc = (u16TailLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
|
||||
}
|
||||
|
||||
/* Update tail — bytes have been consumed */
|
||||
strControl.au16RxTail[u8Instance] = u16TailLoc;
|
||||
|
||||
/* Check if request is fully fulfilled */
|
||||
if (strControl.au16RxReqIndex[u8Instance] >= strControl.au16RxReqLength[u8Instance])
|
||||
{
|
||||
strControl.abRxReqActive[u8Instance] = STD_FALSE;
|
||||
vCallRxCallback(u8Instance);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* TX DMA IRQ HANDLER (INSTANCE 0) */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void vTxDmaIrqHandler0(void)
|
||||
{
|
||||
s8 s8ChannelLoc = strControl.as8TxDmaChannel[MCU_UART_INSTANCE_0];
|
||||
u32 u32StatusLoc;
|
||||
|
||||
u32StatusLoc = dma_channel_get_irq0_status((u32)s8ChannelLoc);
|
||||
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)s8ChannelLoc);
|
||||
dma_irqn_acknowledge_channel(0, (u32)s8ChLoc);
|
||||
strControl.abTxBusy[MCU_UART_INSTANCE_0] = STD_FALSE;
|
||||
vCallTxCallback((u8)MCU_UART_INSTANCE_0);
|
||||
}
|
||||
@ -140,53 +84,31 @@ static void vTxDmaIrqHandler0(void)
|
||||
/* UART ISR HANDLER (RX + TX) */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Combined UART interrupt handler for RX and TX.
|
||||
*
|
||||
* RX: drains the RX FIFO into the ring buffer. If an async ReceiveBuffer
|
||||
* request is active, copies bytes from ring buffer to the request buffer.
|
||||
* Calls per-byte RX callback (ISR mode only).
|
||||
*
|
||||
* TX: fills the TX FIFO from the send buffer. When all bytes sent, disables
|
||||
* TX interrupt and calls TX callback.
|
||||
*/
|
||||
static void vUartIsrHandler(u8 u8Instance)
|
||||
{
|
||||
uart_inst_t *pstrUartLoc = apstrInstances[u8Instance];
|
||||
|
||||
/* --- RX: drain FIFO into ring buffer --- */
|
||||
STD_tBool bRxReadable = STD_FALSE;
|
||||
STD_tBool bReadableLoc = (uart_is_readable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
|
||||
|
||||
bRxReadable = (uart_is_readable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
|
||||
|
||||
while (bRxReadable == STD_TRUE)
|
||||
while (bReadableLoc == STD_TRUE)
|
||||
{
|
||||
u8 u8ByteLoc = (u8)uart_getc(pstrUartLoc);
|
||||
u16 u16HeadLoc = strControl.au16RxHead[u8Instance];
|
||||
|
||||
/* Push byte into ring buffer */
|
||||
strControl.aau8RxBuffer[u8Instance][u16HeadLoc] = u8ByteLoc;
|
||||
strControl.au16RxHead[u8Instance] = (u16HeadLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
|
||||
|
||||
bRxReadable = (uart_is_readable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
|
||||
bReadableLoc = (uart_is_readable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
|
||||
}
|
||||
|
||||
/* If an async ReceiveBuffer request is active, try to fulfill it */
|
||||
if (strControl.abRxReqActive[u8Instance] == STD_TRUE)
|
||||
{
|
||||
vFulfillRxRequest(u8Instance);
|
||||
}
|
||||
|
||||
/* --- TX: fill FIFO from buffer (if TX is active in ISR mode) --- */
|
||||
/* --- TX: fill FIFO from buffer (if TX ISR active) --- */
|
||||
STD_tBool bTxBusyLoc = strControl.abTxBusy[u8Instance];
|
||||
|
||||
if (bTxBusyLoc == STD_TRUE)
|
||||
{
|
||||
STD_tBool bFifoReady = STD_FALSE;
|
||||
STD_tBool bDataLeft = STD_FALSE;
|
||||
|
||||
bFifoReady = (uart_is_writable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
|
||||
bDataLeft = (strControl.au16TxIndex[u8Instance] < strControl.au16TxLength[u8Instance]) ? STD_TRUE : STD_FALSE;
|
||||
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))
|
||||
{
|
||||
@ -200,7 +122,6 @@ static void vUartIsrHandler(u8 u8Instance)
|
||||
|
||||
if (bDataLeft == STD_FALSE)
|
||||
{
|
||||
/* Disable TX interrupt, keep RX interrupt enabled */
|
||||
uart_set_irqs_enabled(pstrUartLoc, false, true);
|
||||
strControl.abTxBusy[u8Instance] = STD_FALSE;
|
||||
vCallTxCallback(u8Instance);
|
||||
@ -222,35 +143,23 @@ STD_tenuResult MCU_UART_enuInit(void)
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
u8 u8IndexLoc;
|
||||
|
||||
/* Zero-initialize all control state */
|
||||
for (u8IndexLoc = 0U; u8IndexLoc < (u8)MCU_UART_NUM_INSTANCES; u8IndexLoc++)
|
||||
{
|
||||
/* TX */
|
||||
strControl.apu8TxBuffer[u8IndexLoc] = STD_NULL;
|
||||
strControl.au16TxLength[u8IndexLoc] = 0U;
|
||||
strControl.au16TxIndex[u8IndexLoc] = 0U;
|
||||
strControl.abTxBusy[u8IndexLoc] = STD_FALSE;
|
||||
strControl.as8TxDmaChannel[u8IndexLoc] = -1;
|
||||
|
||||
/* RX */
|
||||
strControl.au16RxHead[u8IndexLoc] = 0U;
|
||||
strControl.au16RxTail[u8IndexLoc] = 0U;
|
||||
strControl.as8RxDmaChannel[u8IndexLoc] = -1;
|
||||
|
||||
/* RX request */
|
||||
strControl.apu8RxReqBuffer[u8IndexLoc] = STD_NULL;
|
||||
strControl.au16RxReqLength[u8IndexLoc] = 0U;
|
||||
strControl.au16RxReqIndex[u8IndexLoc] = 0U;
|
||||
strControl.abRxReqActive[u8IndexLoc] = STD_FALSE;
|
||||
}
|
||||
|
||||
/* Configure each UART instance */
|
||||
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];
|
||||
|
||||
/* Basic peripheral setup */
|
||||
uart_init(pstrUartLoc, pstrCfgLoc->u32BaudRate);
|
||||
gpio_set_function(pstrCfgLoc->u8TxPin, GPIO_FUNC_UART);
|
||||
gpio_set_function(pstrCfgLoc->u8RxPin, GPIO_FUNC_UART);
|
||||
@ -259,13 +168,12 @@ STD_tenuResult MCU_UART_enuInit(void)
|
||||
pstrCfgLoc->enuStopBits,
|
||||
pstrCfgLoc->enuParity);
|
||||
|
||||
/* --- TX async setup --- */
|
||||
/* TX async setup */
|
||||
if (pstrCfgLoc->enuTxAsyncMode == MCU_UART_ASYNC_DMA)
|
||||
{
|
||||
s8 s8ChannelLoc = (s8)dma_claim_unused_channel(true);
|
||||
strControl.as8TxDmaChannel[u8IndexLoc] = s8ChannelLoc;
|
||||
|
||||
dma_channel_set_irq0_enabled((u32)s8ChannelLoc, true);
|
||||
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)
|
||||
{
|
||||
@ -274,13 +182,9 @@ STD_tenuResult MCU_UART_enuInit(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* --- RX async setup --- */
|
||||
/* RX async setup */
|
||||
if (pstrCfgLoc->enuRxAsyncMode == MCU_UART_ASYNC_DMA)
|
||||
{
|
||||
/* DMA RX: continuously fills ring buffer using DMA ring wrap.
|
||||
* The write address wraps at the buffer size boundary, so the
|
||||
* DMA writes in circles. Application reads from the tail and
|
||||
* derives the head from the DMA write pointer. */
|
||||
s8 s8RxChLoc = (s8)dma_claim_unused_channel(true);
|
||||
strControl.as8RxDmaChannel[u8IndexLoc] = s8RxChLoc;
|
||||
|
||||
@ -302,16 +206,11 @@ STD_tenuResult MCU_UART_enuInit(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ISR RX: enable UART RX interrupt. The combined handler
|
||||
* (vUartIsrHandler) drains the FIFO into the ring buffer. */
|
||||
if (u8IndexLoc == (u8)MCU_UART_INSTANCE_0)
|
||||
{
|
||||
irq_set_exclusive_handler(UART0_IRQ, vUart0IrqHandler);
|
||||
irq_set_enabled(UART0_IRQ, true);
|
||||
}
|
||||
|
||||
/* Enable RX interrupt (second param). TX stays off until
|
||||
* SendBuffer is called. */
|
||||
uart_set_irqs_enabled(pstrUartLoc, false, true);
|
||||
}
|
||||
}
|
||||
@ -333,7 +232,7 @@ STD_tenuResult MCU_UART_enuSendByte(u8 u8Instance, u8 u8Byte)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* SEND BUFFER (NON-BLOCKING, DEFAULT) */
|
||||
/* SEND BUFFER (NON-BLOCKING) */
|
||||
/* ========================================================================= */
|
||||
|
||||
STD_tenuResult MCU_UART_enuSendBuffer(u8 u8Instance, const u8 *pu8Data, u16 u16Length)
|
||||
@ -359,40 +258,29 @@ STD_tenuResult MCU_UART_enuSendBuffer(u8 u8Instance, const u8 *pu8Data, u16 u16L
|
||||
|
||||
if (enuModeLoc == MCU_UART_ASYNC_DMA)
|
||||
{
|
||||
s8 s8ChannelLoc = strControl.as8TxDmaChannel[u8Instance];
|
||||
s8 s8ChLoc = strControl.as8TxDmaChannel[u8Instance];
|
||||
uart_inst_t *pstrUartLoc = apstrInstances[u8Instance];
|
||||
|
||||
dma_channel_config strDmaCfgLoc = dma_channel_get_default_config((u32)s8ChannelLoc);
|
||||
channel_config_set_transfer_data_size(&strDmaCfgLoc, DMA_SIZE_8);
|
||||
channel_config_set_read_increment(&strDmaCfgLoc, true);
|
||||
channel_config_set_write_increment(&strDmaCfgLoc, false);
|
||||
channel_config_set_dreq(&strDmaCfgLoc, uart_get_dreq(pstrUartLoc, true));
|
||||
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)s8ChannelLoc,
|
||||
&strDmaCfgLoc,
|
||||
&uart_get_hw(pstrUartLoc)->dr,
|
||||
pu8Data,
|
||||
u16Length,
|
||||
true
|
||||
);
|
||||
(u32)s8ChLoc, &strCfgLoc,
|
||||
&uart_get_hw(pstrUartLoc)->dr, pu8Data, u16Length, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ISR mode: kick-start by filling the FIFO */
|
||||
uart_inst_t *pstrUartLoc = apstrInstances[u8Instance];
|
||||
STD_tBool bFifoReady = STD_FALSE;
|
||||
STD_tBool bDataLeft = STD_FALSE;
|
||||
|
||||
bFifoReady = (uart_is_writable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
|
||||
bDataLeft = (strControl.au16TxIndex[u8Instance] < u16Length) ? STD_TRUE : STD_FALSE;
|
||||
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]]);
|
||||
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;
|
||||
}
|
||||
@ -404,7 +292,6 @@ STD_tenuResult MCU_UART_enuSendBuffer(u8 u8Instance, const u8 *pu8Data, u16 u16L
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Enable TX interrupt for remaining bytes. Keep RX on. */
|
||||
uart_set_irqs_enabled(pstrUartLoc, true, true);
|
||||
}
|
||||
}
|
||||
@ -432,7 +319,6 @@ STD_tenuResult MCU_UART_enuSendBufferBlocking(u8 u8Instance, const u8 *pu8Data,
|
||||
{
|
||||
uart_putc_raw(apstrInstances[u8Instance], pu8Data[u16IndexLoc]);
|
||||
}
|
||||
|
||||
vCallTxCallback(u8Instance);
|
||||
}
|
||||
|
||||
@ -449,10 +335,10 @@ STD_tBool MCU_UART_bIsTxBusy(u8 u8Instance)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* RECEIVE BYTE (BLOCKING) */
|
||||
/* READ BYTE (NON-BLOCKING) */
|
||||
/* ========================================================================= */
|
||||
|
||||
STD_tenuResult MCU_UART_enuReceiveByte(u8 u8Instance, u8 *pu8Byte)
|
||||
STD_tenuResult MCU_UART_enuReadByte(u8 u8Instance, u8 *pu8Byte)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
|
||||
@ -462,96 +348,55 @@ STD_tenuResult MCU_UART_enuReceiveByte(u8 u8Instance, u8 *pu8Byte)
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 u16HeadLoc;
|
||||
u16 u16TailLoc;
|
||||
u16 u16HeadLoc = u16GetRxHead(u8Instance);
|
||||
u16 u16TailLoc = strControl.au16RxTail[u8Instance];
|
||||
|
||||
/* Spin-wait until the ring buffer has at least one byte.
|
||||
* The ring buffer is filled in the background by the RX ISR or DMA,
|
||||
* so this loop will unblock as soon as a byte arrives on the wire. */
|
||||
do
|
||||
if (u16HeadLoc == u16TailLoc)
|
||||
{
|
||||
u16HeadLoc = u16GetRxHead(u8Instance);
|
||||
u16TailLoc = strControl.au16RxTail[u8Instance];
|
||||
} while (u16HeadLoc == u16TailLoc);
|
||||
|
||||
/* Read one byte from the tail and advance */
|
||||
*pu8Byte = strControl.aau8RxBuffer[u8Instance][u16TailLoc];
|
||||
strControl.au16RxTail[u8Instance] = (u16TailLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
|
||||
}
|
||||
|
||||
return enuResultLoc;
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* RECEIVE BUFFER (NON-BLOCKING, DEFAULT) */
|
||||
/* ========================================================================= */
|
||||
|
||||
STD_tenuResult MCU_UART_enuReceiveBuffer(u8 u8Instance, u8 *pu8Data, u16 u16Length)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
STD_tBool bActiveLocl = strControl.abRxReqActive[u8Instance];
|
||||
|
||||
if (pu8Data == STD_NULL)
|
||||
{
|
||||
enuResultLoc = STD_NULL_POINTER_ERROR;
|
||||
}
|
||||
else if (bActiveLocl == STD_TRUE)
|
||||
{
|
||||
/* A previous receive request is still pending */
|
||||
enuResultLoc = STD_NOK;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Register the request */
|
||||
strControl.apu8RxReqBuffer[u8Instance] = pu8Data;
|
||||
strControl.au16RxReqLength[u8Instance] = u16Length;
|
||||
strControl.au16RxReqIndex[u8Instance] = 0U;
|
||||
strControl.abRxReqActive[u8Instance] = STD_TRUE;
|
||||
|
||||
/* Try to fulfill immediately from bytes already in the ring buffer.
|
||||
* If enough bytes are available, the request completes synchronously
|
||||
* and the callback fires here. Otherwise it completes later when
|
||||
* the ISR pushes more bytes (ISR mode) or when the application
|
||||
* polls again (DMA mode). */
|
||||
vFulfillRxRequest(u8Instance);
|
||||
*pu8Byte = strControl.aau8RxBuffer[u8Instance][u16TailLoc];
|
||||
strControl.au16RxTail[u8Instance] = (u16TailLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
return enuResultLoc;
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* RECEIVE BUFFER (BLOCKING) */
|
||||
/* READ BUFFER (NON-BLOCKING) */
|
||||
/* ========================================================================= */
|
||||
|
||||
STD_tenuResult MCU_UART_enuReceiveBufferBlocking(u8 u8Instance, u8 *pu8Data, u16 u16Length)
|
||||
STD_tenuResult MCU_UART_enuReadBuffer(u8 u8Instance, u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
u16 u16IndexLoc = 0U;
|
||||
|
||||
if (pu8Data == STD_NULL)
|
||||
if ((pu8Data == STD_NULL) || (pu16Read == STD_NULL))
|
||||
{
|
||||
enuResultLoc = STD_NULL_POINTER_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Read N bytes from the ring buffer, blocking until each is available */
|
||||
while (u16IndexLoc < u16Length)
|
||||
{
|
||||
u16 u16HeadLoc = u16GetRxHead(u8Instance);
|
||||
u16 u16TailLoc = strControl.au16RxTail[u8Instance];
|
||||
u16 u16CountLoc = 0U;
|
||||
|
||||
/* Copy all currently available bytes */
|
||||
while ((u16HeadLoc != u16TailLoc) && (u16IndexLoc < u16Length))
|
||||
while ((u16HeadLoc != u16TailLoc) && (u16CountLoc < u16MaxLength))
|
||||
{
|
||||
pu8Data[u16IndexLoc] = strControl.aau8RxBuffer[u8Instance][u16TailLoc];
|
||||
pu8Data[u16CountLoc] = strControl.aau8RxBuffer[u8Instance][u16TailLoc];
|
||||
u16TailLoc = (u16TailLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
|
||||
u16IndexLoc++;
|
||||
u16CountLoc++;
|
||||
}
|
||||
|
||||
strControl.au16RxTail[u8Instance] = u16TailLoc;
|
||||
}
|
||||
*pu16Read = u16CountLoc;
|
||||
|
||||
vCallRxCallback(u8Instance);
|
||||
if (u16CountLoc == 0U)
|
||||
{
|
||||
enuResultLoc = STD_NOK;
|
||||
}
|
||||
}
|
||||
|
||||
return enuResultLoc;
|
||||
@ -574,12 +419,3 @@ STD_tBool MCU_UART_bIsRxDataAvailable(u8 u8Instance)
|
||||
|
||||
return bResultLoc;
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* RX BUSY CHECK */
|
||||
/* ========================================================================= */
|
||||
|
||||
STD_tBool MCU_UART_bIsRxBusy(u8 u8Instance)
|
||||
{
|
||||
return strControl.abRxReqActive[u8Instance];
|
||||
}
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_UART_priv.h
|
||||
* Component: MCU_UART
|
||||
* Description: Private header for the MCU_UART driver.
|
||||
* Contains the internal control structure for per-instance
|
||||
* runtime state (TX async progress, RX ring buffer, DMA
|
||||
* channels), and the extern config array declaration.
|
||||
* Description: Private header — control struct and extern config array.
|
||||
*
|
||||
* Layer: MCU (hardware abstraction) - internal use only
|
||||
*****************************************************************************/
|
||||
@ -15,35 +12,8 @@
|
||||
#include "MCU_UART.h"
|
||||
#include "MCU_UART_cfg.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* CONFIG ARRAY (EXTERN) */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
extern const MCU_UART_tstrConfig MCU_UART_astrConfig[MCU_UART_NUM_INSTANCES];
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* RUNTIME CONTROL STRUCTURE */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Internal runtime state for all UART instances.
|
||||
*
|
||||
* Each field is an array indexed by instance number (struct-of-arrays).
|
||||
*
|
||||
* TX fields:
|
||||
* apu8TxBuffer — caller's buffer being transmitted (non-blocking)
|
||||
* au16TxLength — total bytes to transmit
|
||||
* au16TxIndex — bytes transmitted so far (ISR mode)
|
||||
* abTxBusy — STD_TRUE while async TX is in progress
|
||||
* as8TxDmaChannel — DMA channel for TX (-1 if ISR mode)
|
||||
*
|
||||
* RX fields:
|
||||
* aau8RxBuffer — per-instance ring buffer for received bytes.
|
||||
* Aligned to buffer size for DMA ring mode compatibility.
|
||||
* au16RxHead — write index (ISR writes here, DMA updates via hw pointer)
|
||||
* au16RxTail — read index (application reads from here)
|
||||
* as8RxDmaChannel — DMA channel for RX (-1 if ISR mode)
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/* TX state */
|
||||
@ -53,18 +23,12 @@ typedef struct
|
||||
STD_tBool abTxBusy[MCU_UART_NUM_INSTANCES];
|
||||
s8 as8TxDmaChannel[MCU_UART_NUM_INSTANCES];
|
||||
|
||||
/* RX ring buffer state */
|
||||
/* 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];
|
||||
|
||||
/* RX async request state (for non-blocking ReceiveBuffer) */
|
||||
u8 *apu8RxReqBuffer[MCU_UART_NUM_INSTANCES]; /**< caller's buffer */
|
||||
u16 au16RxReqLength[MCU_UART_NUM_INSTANCES]; /**< total bytes requested */
|
||||
u16 au16RxReqIndex[MCU_UART_NUM_INSTANCES]; /**< bytes fulfilled so far */
|
||||
STD_tBool abRxReqActive[MCU_UART_NUM_INSTANCES]; /**< is a request pending? */
|
||||
} MCU_UART_tstrControl;
|
||||
|
||||
#endif /* MCU_UART_PRIV_H */
|
||||
@ -1,12 +1,10 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_USB.h
|
||||
* Component: MCU_USB
|
||||
* Description: Public interface for the MCU USB driver component.
|
||||
* This header exposes the functions and types that other
|
||||
* components are allowed to use to send and receive data over
|
||||
* the RP2040 USB-CDC (virtual serial port) interface. From the
|
||||
* host computer's perspective, the Pico appears as a regular
|
||||
* serial device (/dev/tty.usbmodem* on macOS, COMx on Windows).
|
||||
* 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)
|
||||
*****************************************************************************/
|
||||
@ -14,75 +12,56 @@
|
||||
#ifndef MCU_USB_H
|
||||
#define MCU_USB_H
|
||||
|
||||
/* STD_TYPES brings in the fixed-width integer typedefs (u8, u32) and the
|
||||
* STD_tenuResult enum used to report success/failure from every function. */
|
||||
#include "STD_TYPES.h"
|
||||
|
||||
#define MCU_USB_WAIT_FOR_CONNECTION_DISABLED 0U
|
||||
#define MCU_USB_WAIT_FOR_CONNECTION_ENABLED 1U
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* PUBLIC API */
|
||||
/* TX PUBLIC API */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Initialize the USB-CDC interface.
|
||||
*
|
||||
* Sets up the RP2040 USB peripheral and the TinyUSB CDC device so the
|
||||
* board enumerates as a virtual serial port on the host. If the config
|
||||
* macro MCU_USB_WAIT_FOR_CONNECTION is MCU_USB_WAIT_FOR_CONNECTION_ENABLED,
|
||||
* this function blocks until the host opens the port (so early bytes are
|
||||
* not lost), subject to MCU_USB_CONNECTION_TIMEOUT_MS in MCU_USB_cfg.h.
|
||||
* If the timeout elapses before the host connects, returns STD_NOK.
|
||||
*
|
||||
* Must be called exactly once, before any Send/Receive function.
|
||||
*
|
||||
* @return STD_OK on success (USB-CDC initialized, host connected if wait enabled),
|
||||
* STD_NOK on init failure or connection timeout.
|
||||
*/
|
||||
STD_tenuResult MCU_USB_enuInit(void);
|
||||
|
||||
/**
|
||||
* @brief Send a single byte over USB-CDC.
|
||||
*
|
||||
* Blocks until the byte has been handed off to the USB stack or the
|
||||
* transmit timeout (MCU_USB_TRANSMIT_TIMEOUT_MS) elapses.
|
||||
*
|
||||
* @param u8Byte The byte to transmit.
|
||||
* @return STD_OK on success,
|
||||
* STD_NOK on transmit failure or timeout.
|
||||
*/
|
||||
STD_tenuResult MCU_USB_enuSendByte(u8 u8Byte);
|
||||
|
||||
/**
|
||||
* @brief Send a buffer of bytes over USB-CDC.
|
||||
*
|
||||
* Transmits u32Length bytes starting at pu8Data. Blocks until all bytes
|
||||
* are sent or the transmit timeout elapses. The buffer is not modified.
|
||||
*
|
||||
* @param pu8Data Pointer to the byte buffer to transmit. Must not be NULL.
|
||||
* @param u16Length Number of bytes to transmit.
|
||||
* @return STD_OK on success,
|
||||
* STD_NULL_POINTER_ERROR if pu8Data is NULL,
|
||||
* STD_NOK on transmit failure or timeout.
|
||||
*/
|
||||
STD_tenuResult MCU_USB_enuSendBuffer(const u8 *pu8Data, u16 u16Length);
|
||||
|
||||
/**
|
||||
* @brief Receive one byte over USB-CDC (blocking).
|
||||
*
|
||||
* Blocks until a byte arrives from the host serial monitor.
|
||||
*
|
||||
* @param pu8Byte Pointer to store the received byte. Must not be NULL.
|
||||
* @return STD_OK byte received,
|
||||
* STD_NULL_POINTER_ERROR if pu8Byte is NULL.
|
||||
*/
|
||||
STD_tenuResult MCU_USB_enuReceiveByte(u8 *pu8Byte);
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* RX PUBLIC API */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Check if USB-CDC has data waiting to be read.
|
||||
* @brief Read one byte from the USB RX ring buffer (non-blocking).
|
||||
*
|
||||
* @return STD_TRUE if at least one byte is available,
|
||||
* STD_FALSE if no data waiting.
|
||||
* 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);
|
||||
|
||||
|
||||
@ -1,114 +1,139 @@
|
||||
/******************************************************************************
|
||||
* File: MCU_USB_prg.c
|
||||
* Component: MCU_USB
|
||||
* Description: Program (implementation) file for the MCU_USB driver.
|
||||
* Contains the actual implementations of the public functions
|
||||
* declared in MCU_USB.h. Wraps the Pico SDK's USB-CDC / stdio
|
||||
* facilities to provide a simple send/receive API for the
|
||||
* virtual serial port exposed over the Pico's USB connection.
|
||||
* 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" /* stdio_usb_init(), stdio_usb_connected() */
|
||||
#include "pico/stdio.h" /* putchar_raw() - writes one byte into the stdio driver chain */
|
||||
#include "pico/time.h" /* absolute_time_t, make_timeout_time_ms(), time_reached() */
|
||||
#include "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;
|
||||
|
||||
/* Call the Pico SDK's USB-only stdio init. This brings up the TinyUSB
|
||||
* device stack, registers the USB-CDC stdio driver, and starts the
|
||||
* background task that services USB events. Returns true on success. */
|
||||
bSdkInitSuccess = (stdio_usb_init() != 0) ? STD_TRUE : STD_FALSE;
|
||||
|
||||
if (bSdkInitSuccess == STD_FALSE)
|
||||
{
|
||||
enuResultLoc = STD_NOK; /* Initialization failed */
|
||||
}else
|
||||
enuResultLoc = STD_NOK;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if MCU_USB_WAIT_FOR_CONNECTION == MCU_USB_WAIT_FOR_CONNECTION_ENABLED
|
||||
/* Wait for the host to open the CDC port, with a timeout. */
|
||||
absolute_time_t absTimeout = make_timeout_time_ms(MCU_USB_CONNECTION_TIMEOUT_MS);
|
||||
STD_tBool bHostOpen = STD_FALSE;
|
||||
STD_tBool bTimeoutReached = STD_FALSE;
|
||||
do
|
||||
{
|
||||
/* Yield for 10 ms between checks. This serves two purposes:
|
||||
* 1. Avoids burning 100% CPU on a busy-wait spin loop
|
||||
* 2. Gives the TinyUSB background task time to process USB
|
||||
* enumeration events — without yielding, the USB stack
|
||||
* may not advance and the host connection is delayed.
|
||||
* 10 ms matches the interval used by the Pico SDK's own
|
||||
* stdio_usb_init() connection-wait implementation. */
|
||||
sleep_ms(10);
|
||||
|
||||
/* Update status variables — avoid function calls in the
|
||||
* while condition for readability and debuggability */
|
||||
bHostOpen = (stdio_usb_connected() != 0) ? STD_TRUE : STD_FALSE;
|
||||
bTimeoutReached = (time_reached(absTimeout) != 0) ? STD_TRUE : STD_FALSE;
|
||||
} while ((bHostOpen == STD_FALSE) && (bTimeoutReached == STD_FALSE));
|
||||
|
||||
/* If we exited the loop because of timeout rather than a successful
|
||||
* connection, report failure so the caller knows the host never
|
||||
* opened the port within the configured window. */
|
||||
if (bHostOpen == STD_FALSE)
|
||||
{
|
||||
enuResultLoc = STD_NOK;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return enuResultLoc; /* Return the result */
|
||||
|
||||
return enuResultLoc;
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* SEND BYTE */
|
||||
/* ========================================================================= */
|
||||
|
||||
STD_tenuResult MCU_USB_enuSendByte(u8 u8Byte)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
|
||||
/* putchar_raw is the stdio framework's "push one byte into the driver
|
||||
* chain" primitive. It is declared as `int putchar_raw(int c)` because
|
||||
* the C stdio family uses EOF (-1) as a sentinel return value. Passing
|
||||
* u8Byte directly relies on the implicit widening conversion u8 -> int,
|
||||
* which is always safe (every u8 value fits in an int) and deliberately
|
||||
* keeps native C type names out of our code.
|
||||
*
|
||||
* Note on semantics: putchar_raw is fire-and-forget at this layer - it
|
||||
* queues the byte into the USB stdio driver and returns immediately.
|
||||
* The actual USB transfer happens in the TinyUSB background task. There
|
||||
* is no way to detect a transmit failure from this call, so we always
|
||||
* return STD_OK. When we need real delivery guarantees, we will upgrade
|
||||
* this to tud_cdc_write_char + tud_cdc_write_flush. */
|
||||
putchar_raw(u8Byte);
|
||||
|
||||
return enuResultLoc;
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* SEND BUFFER */
|
||||
/* ========================================================================= */
|
||||
|
||||
STD_tenuResult MCU_USB_enuSendBuffer(const u8 *pu8Data, u16 u16Length)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
u16 u16IndexLoc;
|
||||
|
||||
/* Guard against null pointer dereference. On the RP2040 (Cortex-M0+),
|
||||
* reading address 0x00000000 does NOT fault — it silently reads from
|
||||
* the beginning of flash (the vector table), which means the firmware
|
||||
* would send garbage bytes over USB instead of crashing. The explicit
|
||||
* check catches the mistake at the source with a clear error code. */
|
||||
if (pu8Data == STD_NULL)
|
||||
{
|
||||
enuResultLoc = STD_NULL_POINTER_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Send each byte individually via putchar_raw. Same fire-and-forget
|
||||
* semantics as MCU_USB_enuSendByte — bytes are queued into the USB
|
||||
* stdio driver and transmitted by the TinyUSB background task.
|
||||
* No per-byte error detection is possible at this layer. */
|
||||
for (u16IndexLoc = 0U; u16IndexLoc < u16Length; u16IndexLoc++)
|
||||
{
|
||||
putchar_raw(pu8Data[u16IndexLoc]);
|
||||
@ -119,21 +144,10 @@ STD_tenuResult MCU_USB_enuSendBuffer(const u8 *pu8Data, u16 u16Length)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* RECEIVE BYTE (BLOCKING) */
|
||||
/* READ BYTE (NON-BLOCKING) */
|
||||
/* ========================================================================= */
|
||||
|
||||
/**
|
||||
* @brief Internal cached-byte state for bIsRxDataAvailable.
|
||||
*
|
||||
* The Pico SDK has no "peek" function for USB-CDC — the only way to check
|
||||
* if data is available is to try reading. If a read succeeds during
|
||||
* bIsRxDataAvailable, the byte is cached here so ReceiveByte can return it
|
||||
* without a second read. bHasCachedByte tracks whether the cache is valid.
|
||||
*/
|
||||
static STD_tBool bHasCachedByte = STD_FALSE;
|
||||
static u8 u8CachedByte = 0U;
|
||||
|
||||
STD_tenuResult MCU_USB_enuReceiveByte(u8 *pu8Byte)
|
||||
STD_tenuResult MCU_USB_enuReadByte(u8 *pu8Byte)
|
||||
{
|
||||
STD_tenuResult enuResultLoc = STD_OK;
|
||||
|
||||
@ -143,25 +157,53 @@ STD_tenuResult MCU_USB_enuReceiveByte(u8 *pu8Byte)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check if bIsRxDataAvailable already cached a byte */
|
||||
if (bHasCachedByte == STD_TRUE)
|
||||
/* Pull any pending data from SDK into our ring buffer */
|
||||
vDrainStdio();
|
||||
|
||||
if (u16RxHead == u16RxTail)
|
||||
{
|
||||
*pu8Byte = u8CachedByte;
|
||||
bHasCachedByte = STD_FALSE;
|
||||
enuResultLoc = STD_NOK;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Block until a byte arrives. getchar_timeout_us with 0 is
|
||||
* non-blocking — loop until we get a real byte. PICO_ERROR_TIMEOUT
|
||||
* is returned as a negative value when no data is available. */
|
||||
s32 s32ResultLoc;
|
||||
*pu8Byte = au8RxBuffer[u16RxTail];
|
||||
u16RxTail = (u16RxTail + 1U) & USB_RX_BUFFER_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
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))
|
||||
{
|
||||
s32ResultLoc = (s32)getchar_timeout_us(0);
|
||||
} while (s32ResultLoc < 0);
|
||||
enuResultLoc = STD_NULL_POINTER_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
vDrainStdio();
|
||||
|
||||
*pu8Byte = (u8)s32ResultLoc;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,24 +218,12 @@ STD_tBool MCU_USB_bIsRxDataAvailable(void)
|
||||
{
|
||||
STD_tBool bResultLoc = STD_FALSE;
|
||||
|
||||
if (bHasCachedByte == STD_TRUE)
|
||||
{
|
||||
/* Already have a cached byte from a previous check */
|
||||
bResultLoc = STD_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Try a non-blocking read. If successful, cache the byte so
|
||||
* the next ReceiveByte call can return it immediately. */
|
||||
s32 s32ResultLoc = (s32)getchar_timeout_us(0);
|
||||
vDrainStdio();
|
||||
|
||||
if (s32ResultLoc >= 0)
|
||||
if (u16RxHead != u16RxTail)
|
||||
{
|
||||
u8CachedByte = (u8)s32ResultLoc;
|
||||
bHasCachedByte = STD_TRUE;
|
||||
bResultLoc = STD_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return bResultLoc;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user