#include #include "LedSpiTask.h" #include "GwHardware.h" #include "GwApi.h" #include #include #include #include #include "ColorTo3Byte.h" /* controlling some WS2812 using SPI https://controllerstech.com/ws2812-leds-using-spi/ */ static uint8_t mulcolor(uint8_t f1, uint8_t f2){ uint16_t rt=f1; rt*=(uint16_t)f2; return rt >> 8; } Color setBrightness(const Color &color,uint8_t brightness){ uint16_t br255=brightness*255; br255=br255/100; //very simple for now Color rt=color; rt.g=mulcolor(rt.g,br255); rt.b=mulcolor(rt.b,br255); rt.r=mulcolor(rt.r,br255); return rt; } static void colorCompTo3Byte(uint8_t comp,uint8_t *buffer){ for (int i=0;i<3;i++){ *(buffer+i)=colorTo3Byte[comp][i]; } } //depending on LED strip - handle color order static size_t ledsToBuffer(int numLeds,const Color *leds,uint8_t *buffer){ uint8_t *p=buffer; for (int i=0;i the device handle being filled * @return false on error */ bool prepareSpi(GwLog *logger,spi_host_device_t bus,spi_device_handle_t *device){ spi_bus_config_t buscfg = { .mosi_io_num = -1, .miso_io_num = -1, .sclk_io_num = -1, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = 0, .flags=SPICOMMON_BUSFLAG_GPIO_PINS }; esp_err_t err=spi_bus_initialize(bus,&buscfg,SPI_DMA_CH_AUTO); if (err != ESP_OK){ LOG_DEBUG(GwLog::ERROR,"unable to initialize SPI bus %d,mosi=%d, error=%d", (int)bus,-1,(int)err); return false; } spi_device_interface_config_t devcfg = { .command_bits = 0, .address_bits = 0, .dummy_bits = 0, .mode = 0, .duty_cycle_pos = 128, .cs_ena_pretrans = 0, .cs_ena_posttrans =0, .clock_speed_hz = 2500000, //2.5 Mhz .input_delay_ns =0, .spics_io_num = -1, //CS pin .queue_size = 1 //see https://github.com/espressif/esp-idf/issues/9450 }; err=spi_bus_add_device(bus,&devcfg,device); if (err != ESP_OK){ LOG_DEBUG(GwLog::ERROR,"unable to add device to SPI bus %d,mosi=%d, error=%d", (int)bus,-1,(int)err); return false; } //slightly speed up the transactions //as we are the only ones using the bus we can safely acquire it forever err=spi_device_acquire_bus(*device,portMAX_DELAY); if (err != ESP_OK){ LOG_DEBUG(GwLog::ERROR,"unable to acquire SPI bus %d,mosi=%d, error=%d", (int)bus,-1,(int)err); return false; } return true; } /** * send out a set of Color values to a connected led stripe * this method will block until sen dis complete * But as the transfer is using DMA the CPU is not busy during the wait time * @param pin: the IO pin to be used. Will be attached to the SPI device before and deattached after * @param numLeds: the number of Color values * @param leds: pointer to the first Color value * @param bus: the SPI bus * @param device: the SPI device handle **/ bool sendToLeds(GwLog *logger, uint8_t pin, int numLeds, Color *leds, spi_host_device_t bus, spi_device_handle_t &device, uint8_t *buffer = NULL) { //need to send a long reset before //as on S3 MOSI is high on idle on older frameworks //see https://github.com/espressif/esp-idf/issues/13974 const int zeroprefix=80; //3.2us per byte bool ownsBuffer = false; size_t bufferSize = numLeds * 3 * 3+zeroprefix; if (buffer == NULL) { ownsBuffer = true; buffer = (uint8_t *)heap_caps_malloc(bufferSize, MALLOC_CAP_DMA|MALLOC_CAP_32BIT); if (!buffer) { LOG_DEBUG(GwLog::ERROR, "unable to allocate %d bytes of DMA buffer", (int)bufferSize); return false; } } bool rv = true; for (int i=0;iapi->getLogger(); LOG_DEBUG(GwLog::ERROR,"spi led task initialized"); spi_host_device_t bus=SPI3_HOST; bool spiValid=false; LOG_DEBUG(GwLog::ERROR,"SpiLed task started"); if (! prepareGpio(logger,OBP_FLASH_LED)){ EXIT_TASK; } if (! prepareGpio(logger,OBP_BACKLIGHT_LED)){ EXIT_TASK; } spi_device_handle_t device; if (! prepareSpi(logger,bus,&device)){ EXIT_TASK; } bool first=true; LedInterface current; while (true) { LedInterface newLeds=taskData->getLedData(); if (first || current.backlightChanged(newLeds) || current.flasChanged(newLeds)){ first=false; LOG_DEBUG(GwLog::ERROR,"handle SPI leds"); if (current.backlightChanged(newLeds) || first){ LOG_DEBUG(GwLog::ERROR,"setting backlight r=%02d,g=%02d,b=%02d", newLeds.backlight[0].r,newLeds.backlight[0].g,newLeds.backlight[0].b); sendToLeds(logger,OBP_BACKLIGHT_LED,newLeds.backlightLen(),newLeds.backlight,bus,device); } if (current.flasChanged(newLeds) || first){ LOG_DEBUG(GwLog::ERROR,"setting flashr=%02d,g=%02d,b=%02d", newLeds.flash[0].r,newLeds.flash[0].g,newLeds.flash[0].b); sendToLeds(logger,OBP_FLASH_LED,newLeds.flashLen(),newLeds.flash,bus,device); } current=newLeds; } delay(50); } vTaskDelete(NULL); } void createSpiLedTask(LedTaskData *param){ xTaskCreate(handleSpiLeds,"handleLeds",4000,param,3,NULL); }