mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2025-12-15 23:13:07 +01:00
Delete FastLED and implement DMA to SPI for all RGB leds
This commit is contained in:
232
lib/obp60task/LedSpiTask.cpp
Normal file
232
lib/obp60task/LedSpiTask.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
#include <FreeRTOS.h>
|
||||
#include "LedSpiTask.h"
|
||||
#include "GwHardware.h"
|
||||
#include "GwApi.h"
|
||||
#include <driver/spi_master.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_rom_gpio.h>
|
||||
#include <soc/spi_periph.h>
|
||||
#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<numLeds;i++){
|
||||
colorCompTo3Byte(leds[i].g,p);
|
||||
p+=3;
|
||||
colorCompTo3Byte(leds[i].r,p);
|
||||
p+=3;
|
||||
colorCompTo3Byte(leds[i].b,p);
|
||||
p+=3;
|
||||
}
|
||||
return p-buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare a GPIO pin to be used as the data line for an led stripe
|
||||
*/
|
||||
bool prepareGpio(GwLog *logger, uint8_t pin){
|
||||
esp_err_t err=gpio_set_direction((gpio_num_t)pin,GPIO_MODE_OUTPUT);
|
||||
if (err != ESP_OK){
|
||||
LOG_DEBUG(GwLog::ERROR,"unable to set gpio mode for %d: %d",pin,(int)err);
|
||||
return false;
|
||||
}
|
||||
err=gpio_set_level((gpio_num_t)pin,0);
|
||||
if (err != ESP_OK){
|
||||
LOG_DEBUG(GwLog::ERROR,"unable to set gpio level for %d: %d",pin,(int)err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* initialize the SPI bus and add a device for the LED output
|
||||
* it still does not attach any PINs to the bus
|
||||
* this will be done later when sending out
|
||||
* this way we can use one hardware SPI for multiple led stripes
|
||||
* @param bus : the SPI bus
|
||||
* @param device: <out> 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;i<zeroprefix;i++)buffer[i]=0;
|
||||
ledsToBuffer(numLeds, leds, buffer+zeroprefix);
|
||||
struct spi_transaction_t ta = {
|
||||
.flags = 0,
|
||||
.cmd = 0,
|
||||
.addr = 0,
|
||||
.length = bufferSize * 8,
|
||||
.rxlength = 0,
|
||||
.tx_buffer = buffer};
|
||||
int64_t now = esp_timer_get_time();
|
||||
esp_rom_gpio_connect_out_signal(pin, spi_periph_signal[bus].spid_out, false, false);
|
||||
esp_err_t ret = spi_device_transmit(device, &ta);
|
||||
esp_rom_gpio_connect_out_signal(pin, SIG_GPIO_OUT_IDX, false, false);
|
||||
int64_t end = esp_timer_get_time();
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
LOG_DEBUG(GwLog::ERROR, "unable to send led data: %d", (int)ret);
|
||||
rv = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG(GwLog::DEBUG, "successfully send led data for %d leds, %lld us", numLeds, end - now);
|
||||
}
|
||||
if (ownsBuffer)
|
||||
{
|
||||
heap_caps_free(buffer);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
#define EXIT_TASK delay(50);vTaskDelete(NULL);return;
|
||||
void handleSpiLeds(void *param){
|
||||
LedTaskData *taskData=(LedTaskData*)param;
|
||||
GwLog *logger=taskData->api->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);
|
||||
}
|
||||
Reference in New Issue
Block a user