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