/****************************************************************************** * File: HAL_LED_prg.c * Component: HAL_LED * Description: LED abstraction implementation. Manages a pixel buffer with * intensity scaling and pushes color data to the hardware via * MCU_PIO. SetColor updates one LED and immediately pushes the * entire strip — no separate Update call needed. * * Layer: HAL *****************************************************************************/ #include "HAL_LED.h" #include "HAL_LED_priv.h" #include "HAL_LED_cfg.h" #include "MCU_PIO.h" /* ------------------------------------------------------------------------ */ /* RUNTIME STATE */ /* ------------------------------------------------------------------------ */ static HAL_LED_tstrControl strControl; /* ------------------------------------------------------------------------ */ /* INTERNAL HELPERS */ /* ------------------------------------------------------------------------ */ /** * @brief Push the entire pixel buffer for one instance to the PIO FIFO. * * Iterates through the pixel buffer, packs each pixel into a 32-bit * GRB word (left-justified: G in [31:24], R in [23:16], B in [15:8], * bits [7:0] unused), and sends it via MCU_PIO_vPutBlocking. * * @param u8Instance HAL_LED instance index. */ static void vPushStrip(u8 u8Instance) { u8 u8PioInstLoc = HAL_LED_astrConfig[u8Instance].u8PioInstance; u8 u8NumLedsLoc = HAL_LED_astrConfig[u8Instance].u8NumLeds; u8 u8LedLoc; for (u8LedLoc = 0U; u8LedLoc < u8NumLedsLoc; u8LedLoc++) { HAL_LED_tstrPixel *pstrPixLoc = &strControl.astrPixels[u8Instance][u8LedLoc]; /* Pack RGB into bits [31:8] of the 32-bit word. * The WS2812 variant on the RP2040-Zero uses RGB byte order * (red first, green second, blue third) rather than the standard * GRB. Bits [7:0] are padding (shifted out but ignored). */ u32 u32RgbLoc = ((u32)pstrPixLoc->u8Red << 24U) | ((u32)pstrPixLoc->u8Green << 16U) | ((u32)pstrPixLoc->u8Blue << 8U); MCU_PIO_vPutBlocking(u8PioInstLoc, u32RgbLoc); } } /* ========================================================================= */ /* INIT */ /* ========================================================================= */ STD_tenuResult HAL_LED_enuInit(void) { STD_tenuResult enuResultLoc = STD_OK; u8 u8InstLoc; u8 u8LedLoc; /* Clear all pixels to off (black) */ for (u8InstLoc = 0U; u8InstLoc < (u8)HAL_LED_NUM_INSTANCES; u8InstLoc++) { for (u8LedLoc = 0U; u8LedLoc < HAL_LED_astrConfig[u8InstLoc].u8NumLeds; u8LedLoc++) { strControl.astrPixels[u8InstLoc][u8LedLoc].u8Green = 0U; strControl.astrPixels[u8InstLoc][u8LedLoc].u8Red = 0U; strControl.astrPixels[u8InstLoc][u8LedLoc].u8Blue = 0U; } } return enuResultLoc; } /* ========================================================================= */ /* SET COLOR */ /* ========================================================================= */ STD_tenuResult HAL_LED_enuSetColor(u8 u8Instance, u8 u8LedIndex, u8 u8Red, u8 u8Green, u8 u8Blue, u8 u8Intensity) { STD_tenuResult enuResultLoc = STD_OK; u8 u8NumLedsLoc = HAL_LED_astrConfig[u8Instance].u8NumLeds; if (u8LedIndex >= u8NumLedsLoc) { enuResultLoc = STD_INDEX_OUT_OF_RANGE_ERROR; } else { /* Scale each channel by intensity: (channel * intensity) / 255. * Use u16 intermediate to avoid overflow (255 * 255 = 65025, * which fits in u16 but not u8). */ u8 u8ScaledRLoc = (u8)(((u16)u8Red * (u16)u8Intensity) / 255U); u8 u8ScaledGLoc = (u8)(((u16)u8Green * (u16)u8Intensity) / 255U); u8 u8ScaledBLoc = (u8)(((u16)u8Blue * (u16)u8Intensity) / 255U); /* Store the scaled pixel in the buffer */ strControl.astrPixels[u8Instance][u8LedIndex].u8Red = u8ScaledRLoc; strControl.astrPixels[u8Instance][u8LedIndex].u8Green = u8ScaledGLoc; strControl.astrPixels[u8Instance][u8LedIndex].u8Blue = u8ScaledBLoc; /* Immediately push the entire strip to the hardware */ vPushStrip(u8Instance); } return enuResultLoc; }