// NeoPixel Driver using Direct Memory Access // // This implementation is based on the one by Alan Hawse of Elkhorn Creek, // documented at https://iotexpert.com/2019/01/08/psoc-6-dma-ws2812-leds/. // We are grateful to Mr. Hawse for sharing this. #include "KTag.h" #define NEOPIXEL_ZOFFSET (1) #define NEOPIXEL_ONE3 (0b110<<24) #define NEOPIXEL_ZERO3 (0b100<<24) #define NEOPIXEL_SPI_BIT_PER_BIT (3) #define NEOPIXEL_COLOR_PER_PIXEL (3) #define NEOPIXEL_BYTES_PER_PIXEL (NEOPIXEL_SPI_BIT_PER_BIT * NEOPIXEL_COLOR_PER_PIXEL) #define FRAME_BUFFER_SIZE (NEOPIXEL_ZOFFSET + (CONFIG_KTAG_MAX_NEOPIXELS_PER_CHANNEL * NEOPIXEL_BYTES_PER_PIXEL)) #if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1) static uint8_t NeoPixel_Barrel_Channel_Frame_Buffer[FRAME_BUFFER_SIZE]; #elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4) static uint8_t NeoPixel_Barrel_Channel_Frame_Buffer[FRAME_BUFFER_SIZE]; static uint8_t NeoPixel_Receiver_Channel_Frame_Buffer[FRAME_BUFFER_SIZE]; static uint8_t NeoPixel_Display_Channel_Frame_Buffer[FRAME_BUFFER_SIZE]; static uint8_t NeoPixel_Effects_Channel_Frame_Buffer[FRAME_BUFFER_SIZE]; #else #error "Unsupported number of NeoPixel channels defined. Supported configurations are 1 and 4." #endif static uint8_t* NeoPixel_Frame_Buffers[CONFIG_KTAG_N_NEOPIXEL_CHANNELS] = { #if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1) NeoPixel_Barrel_Channel_Frame_Buffer #elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4) NeoPixel_Barrel_Channel_Frame_Buffer, NeoPixel_Receiver_Channel_Frame_Buffer, NeoPixel_Display_Channel_Frame_Buffer, NeoPixel_Effects_Channel_Frame_Buffer #else #error "Unsupported number of NeoPixel channels defined. Supported configurations are 1 and 4." #endif }; ColorOrder_T ColorOrderByChannel[CONFIG_KTAG_N_NEOPIXEL_CHANNELS]; // Since the descriptors are (or should be) set to "trigger on descriptor completion" (`.interruptType = CY_DMA_DESCR`), // this ISR is called after each channel has been written. static void NeoPixel_DMA_Complete(void) { static BaseType_t xHigherPriorityTaskWoken = pdFALSE; Cy_DMA_Channel_ClearInterrupt(DMA_NeoPixel_HW, DMA_NeoPixel_DW_CHANNEL); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } #define NEOPIXEL_N_DESCRIPTORS CONFIG_KTAG_N_NEOPIXEL_CHANNELS static cy_stc_dma_descriptor_t NeoPixel_Descriptors[NEOPIXEL_N_DESCRIPTORS]; static void NeoPixel_Configure_DMA(void) { // I [AH] copied this structure from the PSoC Creator Component configuration // in the generated source. const cy_stc_dma_descriptor_config_t NeoPixel_DMA_Descriptor_Config = { .retrigger = CY_DMA_RETRIG_IM, .interruptType = CY_DMA_DESCR, .triggerOutType = CY_DMA_1ELEMENT, .channelState = CY_DMA_CHANNEL_ENABLED, .triggerInType = CY_DMA_1ELEMENT, .dataSize = CY_DMA_BYTE, .srcTransferSize = CY_DMA_TRANSFER_SIZE_DATA, .dstTransferSize = CY_DMA_TRANSFER_SIZE_WORD, .descriptorType = CY_DMA_1D_TRANSFER, .srcAddress = NULL, .dstAddress = NULL, .srcXincrement = 1L, .dstXincrement = 0L, .xCount = 256UL, .srcYincrement = 0L, .dstYincrement = 0L, .yCount = 1UL, .nextDescriptor = NULL }; for (uint_fast8_t i=0; i < NEOPIXEL_N_DESCRIPTORS; i++) { Cy_DMA_Descriptor_Init(&NeoPixel_Descriptors[i], &NeoPixel_DMA_Descriptor_Config); Cy_DMA_Descriptor_SetSrcAddress(&NeoPixel_Descriptors[i], (uint8_t *)&NeoPixel_Frame_Buffers[i][0]); Cy_DMA_Descriptor_SetDstAddress(&NeoPixel_Descriptors[i], (void *)&SPI_NeoPixel_HW->TX_FIFO_WR); Cy_DMA_Descriptor_SetXloopDataCount(&NeoPixel_Descriptors[i], FRAME_BUFFER_SIZE); } // Initialize and enable the interrupt from DMA_NeoPixel_HW. Cy_SysInt_Init(&DMA_NeoPixel_Int_cfg, &NeoPixel_DMA_Complete); NVIC_EnableIRQ(DMA_NeoPixel_Int_cfg.intrSrc); Cy_DMA_Channel_SetInterruptMask(DMA_NeoPixel_HW, DMA_NeoPixel_DW_CHANNEL, DMA_NeoPixel_INTR_MASK); Cy_DMA_Enable(DMA_NeoPixel_HW); } // Function: NeoPixel_Trigger_DMA // This function sets up the channel... then enables it to dump the frameBuffer to pixels. void NeoPixel_Trigger_DMA(uint_fast8_t channel) { cy_stc_dma_channel_config_t channel_config; channel_config.descriptor = &NeoPixel_Descriptors[channel]; channel_config.preemptable = DMA_NeoPixel_PREEMPTABLE; channel_config.priority = DMA_NeoPixel_PRIORITY; channel_config.enable = false; Cy_DMA_Channel_Init(DMA_NeoPixel_HW, DMA_NeoPixel_DW_CHANNEL, &channel_config); Cy_DMA_Channel_Enable(DMA_NeoPixel_HW, DMA_NeoPixel_DW_CHANNEL); } //! Takes an 8-bit value representing a color level and turns it into a WS2812 bit code... /*! * ...where 1=110 and 0=011 * One input byte turns into three output bytes of a uint32_t. */ uint32_t NeoPixel_ConvertTo3Code(uint8_t input) { uint32_t rval=0; for (uint_fast8_t i=0; i < 8; i++) { if (input % 2) { rval |= NEOPIXEL_ONE3; } else { rval |= NEOPIXEL_ZERO3; } rval = rval >> 3; input = input >> 1; } return rval; } //! Takes a position and a three byte RGB value and updates the corresponding NeoPixel_Frame_Buffer with the correct nine bytes. SystemKResult_T HW_NeoPixels_Set_RGB(NeoPixelsChannel_T channel, uint8_t position, uint8_t red, uint8_t green, uint8_t blue) { typedef union { uint8_t bytes[4]; uint32_t word; } NeoPixel_ColorByNumber; NeoPixel_ColorByNumber color; ColorOrder_T order = ColorOrderByChannel[channel]; if (order == COLOR_ORDER_RGB) { color.word = NeoPixel_ConvertTo3Code(red); NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+0+NEOPIXEL_ZOFFSET] = color.bytes[2]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+1+NEOPIXEL_ZOFFSET] = color.bytes[1]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+2+NEOPIXEL_ZOFFSET] = color.bytes[0]; color.word = NeoPixel_ConvertTo3Code(green); NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+3+NEOPIXEL_ZOFFSET] = color.bytes[2]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+4+NEOPIXEL_ZOFFSET] = color.bytes[1]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+5+NEOPIXEL_ZOFFSET] = color.bytes[0]; color.word = NeoPixel_ConvertTo3Code(blue); NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+6+NEOPIXEL_ZOFFSET] = color.bytes[2]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+7+NEOPIXEL_ZOFFSET] = color.bytes[1]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+8+NEOPIXEL_ZOFFSET] = color.bytes[0]; } else if (order == COLOR_ORDER_GRB) { color.word = NeoPixel_ConvertTo3Code(green); NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+0+NEOPIXEL_ZOFFSET] = color.bytes[2]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+1+NEOPIXEL_ZOFFSET] = color.bytes[1]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+2+NEOPIXEL_ZOFFSET] = color.bytes[0]; color.word = NeoPixel_ConvertTo3Code(red); NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+3+NEOPIXEL_ZOFFSET] = color.bytes[2]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+4+NEOPIXEL_ZOFFSET] = color.bytes[1]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+5+NEOPIXEL_ZOFFSET] = color.bytes[0]; color.word = NeoPixel_ConvertTo3Code(blue); NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+6+NEOPIXEL_ZOFFSET] = color.bytes[2]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+7+NEOPIXEL_ZOFFSET] = color.bytes[1]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+8+NEOPIXEL_ZOFFSET] = color.bytes[0]; } else { // Color order is not handled--log this and use RGB. { static bool error_logged = false; if (error_logged == false) { COMM_Console_Print_String("Color order "); COMM_Console_Print_UInt8(order); COMM_Console_Print_String(" not yet supported!"); error_logged = true; } } color.word = NeoPixel_ConvertTo3Code(red); NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+0+NEOPIXEL_ZOFFSET] = color.bytes[2]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+1+NEOPIXEL_ZOFFSET] = color.bytes[1]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+2+NEOPIXEL_ZOFFSET] = color.bytes[0]; color.word = NeoPixel_ConvertTo3Code(green); NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+3+NEOPIXEL_ZOFFSET] = color.bytes[2]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+4+NEOPIXEL_ZOFFSET] = color.bytes[1]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+5+NEOPIXEL_ZOFFSET] = color.bytes[0]; color.word = NeoPixel_ConvertTo3Code(blue); NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+6+NEOPIXEL_ZOFFSET] = color.bytes[2]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+7+NEOPIXEL_ZOFFSET] = color.bytes[1]; NeoPixel_Frame_Buffers[channel][position*NEOPIXEL_BYTES_PER_PIXEL+8+NEOPIXEL_ZOFFSET] = color.bytes[0]; } return SYSTEMK_RESULT_SUCCESS; } //! Initializes the hardware. SystemKResult_T HW_NeoPixels_Init(void) { Cy_SCB_SPI_Init(SPI_NeoPixel_HW, &SPI_NeoPixel_config, &SPI_NeoPixel_context); Cy_SCB_SPI_Enable(SPI_NeoPixel_HW); NeoPixel_Configure_DMA(); #if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1) ColorOrderByChannel[NEOPIXEL_CHANNEL_BARREL] = NVM_BARREL_COLOR_ORDER; #elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4) ColorOrderByChannel[NEOPIXEL_CHANNEL_BARREL] = NVM_BARREL_COLOR_ORDER; ColorOrderByChannel[NEOPIXEL_CHANNEL_RECEIVER] = NVM_RECEIVER_COLOR_ORDER; ColorOrderByChannel[NEOPIXEL_CHANNEL_DISPLAY] = NVM_DISPLAY_COLOR_ORDER; ColorOrderByChannel[NEOPIXEL_CHANNEL_EFFECTS] = NVM_EFFECTS_COLOR_ORDER; #else #error "Unsupported number of NeoPixel channels defined. Supported configurations are 1 and 4." #endif return SYSTEMK_RESULT_SUCCESS; } #if (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 1) static inline __attribute__((always_inline)) void NeoPixels_Set_Color_On_All_Channels(uint8_t position, color_t color) { HW_NeoPixels_Set_RGB(NEOPIXEL_CHANNEL_BARREL, position, Gamma8[Red(color)], Gamma8[Green(color)], Gamma8[Blue(color)]); } static inline __attribute__((always_inline)) void NeoPixel_Disable_All_Channels() { // Nothing to do. } static inline __attribute__((always_inline)) void NeoPixel_Enable_Channel(uint_fast8_t __attribute__ ((unused)) channel) { // Nothing to do. } static inline __attribute__((always_inline)) void NeoPixel_Disable_Channel(uint_fast8_t __attribute__ ((unused)) channel) { // Nothing to do. } #elif (CONFIG_KTAG_N_NEOPIXEL_CHANNELS == 4) static inline __attribute__((always_inline)) void NeoPixel_Enable_Barrel_Channel() { Cy_GPIO_Write(Pin_NeoPixel_Select_0_PORT, Pin_NeoPixel_Select_0_NUM, 1); } static inline __attribute__((always_inline)) void NeoPixel_Disable_Barrel_Channel() { Cy_GPIO_Write(Pin_NeoPixel_Select_0_PORT, Pin_NeoPixel_Select_0_NUM, 0); } static inline __attribute__((always_inline)) void NeoPixel_Enable_Receiver_Channel() { Cy_GPIO_Write(Pin_NeoPixel_Select_1_PORT, Pin_NeoPixel_Select_1_NUM, 1); } static inline __attribute__((always_inline)) void NeoPixel_Disable_Receiver_Channel() { Cy_GPIO_Write(Pin_NeoPixel_Select_1_PORT, Pin_NeoPixel_Select_1_NUM, 0); } static inline __attribute__((always_inline)) void NeoPixel_Enable_Display_Channel() { Cy_GPIO_Write(Pin_NeoPixel_Select_2_PORT, Pin_NeoPixel_Select_2_NUM, 1); } static inline __attribute__((always_inline)) void NeoPixel_Disable_Display_Channel() { Cy_GPIO_Write(Pin_NeoPixel_Select_2_PORT, Pin_NeoPixel_Select_2_NUM, 0); } static inline __attribute__((always_inline)) void NeoPixel_Enable_Effects_Channel() { Cy_GPIO_Write(Pin_NeoPixel_Select_3_PORT, Pin_NeoPixel_Select_3_NUM, 1); } static inline __attribute__((always_inline)) void NeoPixel_Disable_Effects_Channel() { Cy_GPIO_Write(Pin_NeoPixel_Select_3_PORT, Pin_NeoPixel_Select_3_NUM, 0); } static inline __attribute__((always_inline)) void NeoPixel_Enable_All_Channels() { NeoPixel_Enable_Barrel_Channel(); NeoPixel_Enable_Receiver_Channel(); NeoPixel_Enable_Display_Channel(); NeoPixel_Enable_Effects_Channel(); } static inline __attribute__((always_inline)) void NeoPixel_Disable_All_Channels() { NeoPixel_Disable_Barrel_Channel(); NeoPixel_Disable_Receiver_Channel(); NeoPixel_Disable_Display_Channel(); NeoPixel_Disable_Effects_Channel(); } static inline __attribute__((always_inline)) void NeoPixel_Enable_Channel(uint_fast8_t channel) { switch (channel) { case NEOPIXEL_CHANNEL_BARREL: NeoPixel_Enable_Barrel_Channel(); break; case NEOPIXEL_CHANNEL_RECEIVER: NeoPixel_Enable_Receiver_Channel(); break; case NEOPIXEL_CHANNEL_DISPLAY: NeoPixel_Enable_Display_Channel(); break; case NEOPIXEL_CHANNEL_EFFECTS: NeoPixel_Enable_Effects_Channel(); break; default: // Do nothing. break; } } static inline __attribute__((always_inline)) void NeoPixel_Disable_Channel(uint_fast8_t channel) { switch (channel) { case NEOPIXEL_CHANNEL_BARREL: NeoPixel_Disable_Barrel_Channel(); break; case NEOPIXEL_CHANNEL_RECEIVER: NeoPixel_Disable_Receiver_Channel(); break; case NEOPIXEL_CHANNEL_DISPLAY: NeoPixel_Disable_Display_Channel(); break; case NEOPIXEL_CHANNEL_EFFECTS: NeoPixel_Disable_Effects_Channel(); break; default: // Do nothing. break; } } #else #error "Unsupported number of NeoPixel channels defined. Supported configurations are 1 and 4." #endif SystemKResult_T HW_NeoPixels_Publish(void) { // Update the NeoPixels using DMA. for (uint_fast8_t Current_NeoPixel_Channel = 0; Current_NeoPixel_Channel < CONFIG_KTAG_N_NEOPIXEL_CHANNELS; Current_NeoPixel_Channel++) { xSemaphoreTake(NeoPixels_Semaphore, portMAX_DELAY); NeoPixel_Enable_Channel(Current_NeoPixel_Channel); NeoPixel_Trigger_DMA(Current_NeoPixel_Channel); // Allow time for the DMA transfer to go out on the wire. vTaskDelay(portTICK_PERIOD_MS); NeoPixel_Disable_Channel(Current_NeoPixel_Channel); xSemaphoreGive(NeoPixels_Semaphore); } return SYSTEMK_RESULT_SUCCESS; } //! \todo Refactor this somehow...it doesn't belong here. color_t HW_NeoPixels_Get_My_Color(void) { return PROTOCOLS_GetColor(GetWeaponFromID(NVM_WEAPON_ID).Protocol, NVM_TEAM_ID, NVM_PLAYER_ID); }