hubmartin / WS2812B_STM32F3

WS2812 DMA library with low RAM needs. Up to 16 paralel outputs and thousands of LEDs on each of them
MIT License
60 stars 15 forks source link

Glitches: experiencing shifted pixels (using htim4 and other dma channel) #6

Closed domingguss closed 4 years ago

domingguss commented 4 years ago

We are using strand of WS2812 pixels on a STM32F303RC, and I am using your library, but slightly modified to work with a different timer/DMA channels, due to conflicts in CubeMX, because we use communication on UART3, SPI communication with other peripherals and more.

Unfortunately, sometimes (once every minute) a LED glitch occurs: that is - the pixels are shifted like 8/9 pixels... Although this only happens once, i think it shouldn't happen at all...

I am using a 160 pixel strip, and writing out the rgb values every ±180ms. I also put the visualisation code in the same file for simplicity's sake.

Could the APB1 bus be overused?

ws2812b.h

/*

  WS2812B CPU and memory efficient library

  Date: 28.9.2016

  Author: Martin Hubacek
          http://www.martinhubacek.cz
          @hubmartin

  Licence: MIT License

*/

#ifndef WS2812B_H_
#define WS2812B_H_

#if defined(IS_JUNIOR_APPLICATION)

#include "stm32f3xx_hal.h"

#include <stdint.h>

// GPIO enable command
#define WS2812B_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
// LED output port
#define WS2812B_PORT GPIOC
// LED output pins
#define WS2812B_PINS (GPIO_PIN_7)// | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3)
// How many LEDs are in the series
#define WS2812B_NUMBER_OF_LEDS 160

// Number of output LED strips. Each has its own buffer.
#define WS2812_BUFFER_COUNT 1

// Choose one of the bit-juggling setpixel implementation
// *******************************************************
//#define SETPIX_1  // For loop, works everywhere, slow
//#define SETPIX_2  // Bit band in a loop
//#define SETPIX_3  // Like SETPIX_1 but with unrolled loop
#define SETPIX_4    // Fastest copying using bit-banding

// DEBUG OUTPUT
// ********************
#define LED4_PORT GPIOC
#define LED4_PIN GPIO_PIN_10

#define LED5_PORT GPIOC
#define LED5_PIN GPIO_PIN_10

#ifdef __cplusplus
extern "C"{
#endif

uint8_t frameBuffer[3*WS2812B_NUMBER_OF_LEDS];

void visInit();
void visHandle();
void ws2812_TimerHandler();
void ws2812_SetColor(uint16_t index, uint32_t color);

#ifdef __cplusplus
}
#endif

// Public functions
// ****************

void ws2812b_init();
void ws2812b_handle();

// Library structures
// ******************
// This value sets number of periods to generate 50uS Treset signal
#define WS2812_RESET_PERIOD 12

typedef struct WS2812_BufferItem {
    uint8_t* frameBufferPointer;
    uint32_t frameBufferSize;
    uint32_t frameBufferCounter;
    uint8_t channel;    // digital output pin/channel
} WS2812_BufferItem;

typedef struct WS2812_Struct
{
    WS2812_BufferItem item[WS2812_BUFFER_COUNT];
    uint8_t transferComplete;
    uint8_t startTransfer;
    uint32_t timerPeriodCounter;
    uint32_t repeatCounter;
} WS2812_Struct;

static WS2812_Struct ws2812b;

// Bit band stuff
#define RAM_BASE 0x20000000
#define RAM_BB_BASE 0x22000000
#define Var_ResetBit_BB(VarAddr, BitNumber) (*(volatile uint32_t *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 0)
#define Var_SetBit_BB(VarAddr, BitNumber) (*(volatile uint32_t *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 1)
#define Var_GetBit_BB(VarAddr, BitNumber) (*(volatile uint32_t *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)))
#define BITBAND_SRAM(address, bit) ( (__IO uint32_t *) (RAM_BB_BASE + (((uint32_t)address) - RAM_BASE) * 32 + (bit) * 4))

#define varSetBit(var,bit) (Var_SetBit_BB((uint32_t)&var,bit))
#define varResetBit(var,bit) (Var_ResetBit_BB((uint32_t)&var,bit))
#define varGetBit(var,bit) (Var_GetBit_BB((uint32_t)&var,bit))

static void ws2812b_set_pixel(uint8_t row, uint16_t column, uint8_t red, uint8_t green, uint8_t blue);

void DMA_TransferCompleteHandler(DMA_HandleTypeDef *DmaHandle);
void DMA_TransferHalfHandler(DMA_HandleTypeDef *DmaHandle);

#endif // #if defined(IS_JUNIOR_APPLICATION)

#endif /* WS2812B_H_ */

Here is ws2812b.c

/*

  WS2812B CPU and memory efficient library

  Date: 28.9.2016

  Author: Martin Hubacek
          http://www.martinhubacek.cz
          @hubmartin

  Licence: MIT License

*/

#include <string.h>
#include <stdint.h>
#include <stdlib.h>

#include "ws2812b.h"

#if defined(IS_JUNIOR_APPLICATION)

// RGB Framebuffers
uint8_t frameBuffer2[3*20];

// Helper defines
#define newColor(r, g, b) (((uint32_t)(r) << 16) | ((uint32_t)(g) <<  8) | (b))
#define Red(c) ((uint8_t)((c >> 16) & 0xFF))
#define Green(c) ((uint8_t)((c >> 8) & 0xFF))
#define Blue(c) ((uint8_t)(c & 0xFF))

extern WS2812_Struct ws2812b;

// Define source arrays for my DMAs
uint32_t WS2812_IO_High[] =  { WS2812B_PINS };
uint32_t WS2812_IO_Low[] = {WS2812B_PINS << 16};

// WS2812 framebuffer - buffer for 2 LEDs - two times 24 bits
uint16_t ws2812bDmaBitBuffer[24 * 2];

// Gamma correction table
const uint8_t gammaTable[] = {
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,
    2,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  5,  5,  5,
    5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9, 10,
   10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
   17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
   25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
   37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
   51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
   69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
   90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
  115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
  144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
  177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
  215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };

void visInit()
{
    printf("visinit\n");
    // Set output channel/pin, GPIO_PIN_0 = 0, for GPIO_PIN_5 = 5 - this has to correspond to WS2812B_PINS
    ws2812b.item[0].channel = 7;
    // Your RGB framebuffer
    ws2812b.item[0].frameBufferPointer = frameBuffer;
    // RAW size of framebuffer
    ws2812b.item[0].frameBufferSize = sizeof(frameBuffer);

//  // If you need more parallel LED strips, increase the WS2812_BUFFER_COUNT value
//  ws2812b.item[1].channel = 3;
//  ws2812b.item[1].frameBufferPointer = frameBuffer2;
//  ws2812b.item[1].frameBufferSize = sizeof(frameBuffer2);

    ws2812b_init();
}

void visHandle()
{
    if(ws2812b.transferComplete)
    {
        // Update your framebuffer here or swap buffers
        //visHandle2();

        // Signal that buffer is changed and transfer new data
        ws2812b.startTransfer = 1;
        ws2812b_handle();
    }
}

static void ws2812b_gpio_init(void)
{
    // WS2812B outputs
    WS2812B_GPIO_CLK_ENABLE();
    GPIO_InitTypeDef  GPIO_InitStruct;
    GPIO_InitStruct.Pin       = WS2812B_PINS;
    GPIO_InitStruct.Mode      = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull      = GPIO_NOPULL;
    GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(WS2812B_PORT, &GPIO_InitStruct);

    // Enable output pins for debuging to see DMA Full and Half transfer interrupts
    #if defined(LED4_PORT) && defined(LED5_PORT)
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

        GPIO_InitStruct.Pin = LED4_PIN;
        HAL_GPIO_Init(LED4_PORT, &GPIO_InitStruct);
        GPIO_InitStruct.Pin = LED5_PIN;
        HAL_GPIO_Init(LED5_PORT, &GPIO_InitStruct);
    #endif
}

TIM_HandleTypeDef    htim4;
TIM_OC_InitTypeDef tim4OChannel2;
TIM_OC_InitTypeDef tim4OChannel3;

uint32_t tim_period;
static void TIM4_init(void)
{
    // TIM4 Periph clock enable
    __HAL_RCC_TIM4_CLK_ENABLE();

    // This computation of pulse length should work ok,
    // at some slower core speeds it needs some tuning.
    tim_period =  SystemCoreClock / 800000; // 0,125us period (10 times lower the 1,25us period to have fixed math below)
    uint32_t cc2 = (10 * tim_period) / 36;
    uint32_t cc3 = (10 * tim_period) / 15;

    htim4.Instance = TIM4;

    htim4.Init.Period            = tim_period;
    htim4.Init.RepetitionCounter = 0;
    htim4.Init.Prescaler         = 0;
    htim4.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
    htim4.Init.CounterMode       = TIM_COUNTERMODE_UP;
    HAL_TIM_PWM_Init(&htim4);

    HAL_NVIC_SetPriority(TIM4_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM4_IRQn);

    tim4OChannel2.OCMode       = TIM_OCMODE_PWM1;
    tim4OChannel2.OCPolarity   = TIM_OCPOLARITY_HIGH;
    tim4OChannel2.Pulse        = cc2;
    tim4OChannel2.OCNPolarity  = TIM_OCNPOLARITY_HIGH;
    tim4OChannel2.OCFastMode   = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim4, &tim4OChannel2, TIM_CHANNEL_2);

    tim4OChannel3.OCMode       = TIM_OCMODE_PWM1;
    tim4OChannel3.OCPolarity   = TIM_OCPOLARITY_HIGH;
    tim4OChannel3.Pulse        = cc3;
    tim4OChannel3.OCNPolarity  = TIM_OCNPOLARITY_HIGH;
    tim4OChannel3.OCFastMode   = TIM_OCFAST_DISABLE;
    tim4OChannel3.OCIdleState  = TIM_OCIDLESTATE_RESET;
    tim4OChannel3.OCNIdleState = TIM_OCNIDLESTATE_RESET;
    HAL_TIM_PWM_ConfigChannel(&htim4, &tim4OChannel3, TIM_CHANNEL_3);

    HAL_TIM_Base_Start(&htim4);
    HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);

}

DMA_HandleTypeDef     dmaUpdate;
DMA_HandleTypeDef     dmaCC2;
DMA_HandleTypeDef     dmaCC3;
#define BUFFER_SIZE     (sizeof(ws2812bDmaBitBuffer)/sizeof(uint16_t))

static void DMA_init(void)
{
    // TIM4 Update event
    __HAL_RCC_DMA1_CLK_ENABLE();
    dmaUpdate.Init.Direction = DMA_MEMORY_TO_PERIPH;
    dmaUpdate.Init.PeriphInc = DMA_PINC_DISABLE;
    dmaUpdate.Init.MemInc = DMA_MINC_DISABLE;
    dmaUpdate.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dmaUpdate.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    dmaUpdate.Init.Mode = DMA_CIRCULAR;
    dmaUpdate.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    dmaUpdate.Instance = DMA1_Channel7;

    HAL_DMA_Init(&dmaUpdate);
    HAL_DMA_Start(&dmaUpdate, (uint32_t)WS2812_IO_High, (uint32_t)&WS2812B_PORT->BSRR, BUFFER_SIZE);

    // TIM4 CC2 event
    dmaCC2.Init.Direction = DMA_MEMORY_TO_PERIPH;
    dmaCC2.Init.PeriphInc = DMA_PINC_DISABLE;
    dmaCC2.Init.MemInc = DMA_MINC_ENABLE;
    dmaCC2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dmaCC2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    dmaCC2.Init.Mode = DMA_CIRCULAR;
    dmaCC2.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    dmaCC2.Instance = DMA1_Channel4;

    HAL_DMA_Init(&dmaCC2);
    HAL_DMA_Start(&dmaCC2, (uint32_t)ws2812bDmaBitBuffer, (uint32_t)&WS2812B_PORT->BRR, BUFFER_SIZE);

    // TIM4 CC3 event
    dmaCC3.Init.Direction = DMA_MEMORY_TO_PERIPH;
    dmaCC3.Init.PeriphInc = DMA_PINC_DISABLE;
    dmaCC3.Init.MemInc = DMA_MINC_DISABLE;
    dmaCC3.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dmaCC3.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    dmaCC3.Init.Mode = DMA_CIRCULAR;
    dmaCC3.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    dmaCC3.Instance = DMA1_Channel5;
    //dmaUpdate.XferErrorCallback = TransferError;
    HAL_DMA_Init(&dmaCC3);

    dmaCC3.XferCpltCallback  = DMA_TransferCompleteHandler;
    dmaCC3.XferHalfCpltCallback = DMA_TransferHalfHandler;

    HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
    HAL_DMA_Start_IT(&dmaCC3, (uint32_t)WS2812_IO_Low, (uint32_t)&WS2812B_PORT->BSRR, BUFFER_SIZE);

}

void DMA1_Channel5_IRQHandler(void)
{
  // Check the interrupt and clear flag
  HAL_DMA_IRQHandler(&dmaCC3);
}

static void loadNextFramebufferData(WS2812_BufferItem *bItem, uint32_t row)
{

    uint32_t r = bItem->frameBufferPointer[bItem->frameBufferCounter++];
    uint32_t g = bItem->frameBufferPointer[bItem->frameBufferCounter++];
    uint32_t b = bItem->frameBufferPointer[bItem->frameBufferCounter++];

    if(bItem->frameBufferCounter == bItem->frameBufferSize)
        bItem->frameBufferCounter = 0;

    ws2812b_set_pixel(bItem->channel, row, r, g, b);
}

// Transmit the framebuffer
static void WS2812_sendbuf()
{
    // transmission complete flag
    ws2812b.transferComplete = 0;

    uint32_t i;

    for( i = 0; i < WS2812_BUFFER_COUNT; i++ )
    {
        ws2812b.item[i].frameBufferCounter = 0;

        loadNextFramebufferData(&ws2812b.item[i], 0); // ROW 0
        loadNextFramebufferData(&ws2812b.item[i], 1); // ROW 0
    }

    // clear all DMA flags
    __HAL_DMA_CLEAR_FLAG(&dmaUpdate, DMA_FLAG_TC2 | DMA_FLAG_HT2 | DMA_FLAG_TE2);
    __HAL_DMA_CLEAR_FLAG(&dmaCC2, DMA_FLAG_TC5 | DMA_FLAG_HT5 | DMA_FLAG_TE5);
    __HAL_DMA_CLEAR_FLAG(&dmaCC3, DMA_FLAG_TC7 | DMA_FLAG_HT7 | DMA_FLAG_TE7);

    // configure the number of bytes to be transferred by the DMA controller
    dmaUpdate.Instance->CNDTR = BUFFER_SIZE;
    dmaCC2.Instance->CNDTR = BUFFER_SIZE;
    dmaCC3.Instance->CNDTR = BUFFER_SIZE;

    // clear all TIM4 flags
    __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE | TIM_FLAG_CC1 | TIM_FLAG_CC2 | TIM_FLAG_CC3 | TIM_FLAG_CC4);

    //printf(" ------------------------------- DMA start!\n");
    // enable DMA channels
    __HAL_DMA_ENABLE(&dmaUpdate);
    __HAL_DMA_ENABLE(&dmaCC2);
    __HAL_DMA_ENABLE(&dmaCC3);

    // IMPORTANT: enable the TIM4 DMA requests AFTER enabling the DMA channels!
    __HAL_TIM_ENABLE_DMA(&htim4, TIM_DMA_UPDATE);
    __HAL_TIM_ENABLE_DMA(&htim4, TIM_DMA_CC2);
    __HAL_TIM_ENABLE_DMA(&htim4, TIM_DMA_CC3);

    TIM4->CNT = tim_period-1;

    // start TIM4
    __HAL_TIM_ENABLE(&htim4);
}

void DMA_TransferHalfHandler(DMA_HandleTypeDef *DmaHandle)
{
    #if defined(LED4_PORT)
        LED4_PORT->BSRR = LED4_PIN;
    #endif

    // Is this the last LED?
    if(ws2812b.repeatCounter != (WS2812B_NUMBER_OF_LEDS / 2 - 1))
    {
        uint32_t i;

        for( i = 0; i < WS2812_BUFFER_COUNT; i++ )
        {
            loadNextFramebufferData(&ws2812b.item[i], 0);
        }

    } else {
        // If this is the last pixel, set the next pixel value to zeros, because
        // the DMA would not stop exactly at the last bit.
        ws2812b_set_pixel(0, 0, 0, 0, 0);
    }

    #if defined(LED4_PORT)
        LED4_PORT->BRR = LED4_PIN;
    #endif
}

void DMA_TransferCompleteHandler(DMA_HandleTypeDef *DmaHandle)
{
    #if defined(LED5_PORT)
        LED5_PORT->BSRR = LED5_PIN;
    #endif

    ws2812b.repeatCounter++;

    if(ws2812b.repeatCounter == WS2812B_NUMBER_OF_LEDS / 2)
    {
        // Transfer of all LEDs is done, disable DMA but enable tiemr update IRQ to stop the 50us pulse
        ws2812b.repeatCounter = 0;

        // Enable TIM4 Update interrupt for 50us Treset signal
        __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);
        // Disable DMA
        __HAL_DMA_DISABLE(&dmaUpdate);
        __HAL_DMA_DISABLE(&dmaCC2);
        __HAL_DMA_DISABLE(&dmaCC3);

        // Disable the DMA requests
        __HAL_TIM_DISABLE_DMA(&htim4, TIM_DMA_UPDATE);
        __HAL_TIM_DISABLE_DMA(&htim4, TIM_DMA_CC2);
        __HAL_TIM_DISABLE_DMA(&htim4, TIM_DMA_CC3);

        // Manually set outputs to low to generate 50us reset impulse
        WS2812B_PORT->BSRR = WS2812_IO_Low[0];
    } else {

        // Load bitbuffer with next RGB LED values
        uint32_t i;
        for( i = 0; i < WS2812_BUFFER_COUNT; i++ )
        {
            loadNextFramebufferData(&ws2812b.item[i], 1);
        }

    }

    #if defined(LED5_PORT)
        LED5_PORT->BRR = LED5_PIN;
    #endif
}

void TIM4_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim4);
}

// TIM4 Interrupt Handler gets executed on every TIM4 Update if enabled
void ws2812_TimerHandler()
{
    // I have to wait 50us to generate Treset signal
    if (ws2812b.timerPeriodCounter < (uint8_t)WS2812_RESET_PERIOD)
    {
        // count the number of timer periods
        ws2812b.timerPeriodCounter++;
    }
    else
    {
        ws2812b.timerPeriodCounter = 0;
        __HAL_TIM_DISABLE(&htim4);
        TIM4->CR1 = 0; // disable timer

        // disable the TIM4 Update
        __HAL_TIM_DISABLE_IT(&htim4, TIM_IT_UPDATE);
        // set TransferComplete flag
        ws2812b.transferComplete = 1;

        //printf(" ------------------------------- DMA COMPLETE!\n");
    }

}

static void ws2812b_set_pixel(uint8_t row, uint16_t column, uint8_t red, uint8_t green, uint8_t blue)
{

    // Apply gamma
    red = gammaTable[red];
    green = gammaTable[green];
    blue = gammaTable[blue];

    uint32_t calcCol = (column*24);
    uint32_t invRed = ~red;
    uint32_t invGreen = ~green;
    uint32_t invBlue = ~blue;

#if defined(SETPIX_1)
    uint8_t i;
    uint32_t calcClearRow = ~(0x01<<row);
    for (i = 0; i < 8; i++)
    {
        // clear the data for pixel

        ws2812bDmaBitBuffer[(calcCol+i)] &= calcClearRow;
        ws2812bDmaBitBuffer[(calcCol+8+i)] &= calcClearRow;
        ws2812bDmaBitBuffer[(calcCol+16+i)] &= calcClearRow;

        // write new data for pixel
        ws2812bDmaBitBuffer[(calcCol+i)] |= (((((invGreen)<<i) & 0x80)>>7)<<row);
        ws2812bDmaBitBuffer[(calcCol+8+i)] |= (((((invRed)<<i) & 0x80)>>7)<<row);
        ws2812bDmaBitBuffer[(calcCol+16+i)] |= (((((invBlue)<<i) & 0x80)>>7)<<row);
    }
#elif defined(SETPIX_2)
    uint8_t i;
    for (i = 0; i < 8; i++)
    {
        // Set or clear the data for the pixel

        if(((invGreen)<<i) & 0x80)
            varSetBit(ws2812bDmaBitBuffer[(calcCol+i)], row);
        else
            varResetBit(ws2812bDmaBitBuffer[(calcCol+i)], row);

        if(((invRed)<<i) & 0x80)
            varSetBit(ws2812bDmaBitBuffer[(calcCol+8+i)], row);
        else
            varResetBit(ws2812bDmaBitBuffer[(calcCol+8+i)], row);

        if(((invBlue)<<i) & 0x80)
            varSetBit(ws2812bDmaBitBuffer[(calcCol+16+i)], row);
        else
            varResetBit(ws2812bDmaBitBuffer[(calcCol+16+i)], row);

    }
#elif defined(SETPIX_3)
    ws2812bDmaBitBuffer[(calcCol+0)] |= (((((invGreen)<<0) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+8+0)] |= (((((invRed)<<0) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+16+0)] |= (((((invBlue)<<0) & 0x80)>>7)<<row);

    ws2812bDmaBitBuffer[(calcCol+1)] |= (((((invGreen)<<1) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+8+1)] |= (((((invRed)<<1) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+16+1)] |= (((((invBlue)<<1) & 0x80)>>7)<<row);

    ws2812bDmaBitBuffer[(calcCol+2)] |= (((((invGreen)<<2) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+8+2)] |= (((((invRed)<<2) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+16+2)] |= (((((invBlue)<<2) & 0x80)>>7)<<row);

    ws2812bDmaBitBuffer[(calcCol+3)] |= (((((invGreen)<<3) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+8+3)] |= (((((invRed)<<3) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+16+3)] |= (((((invBlue)<<3) & 0x80)>>7)<<row);

    ws2812bDmaBitBuffer[(calcCol+4)] |= (((((invGreen)<<4) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+8+4)] |= (((((invRed)<<4) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+16+4)] |= (((((invBlue)<<4) & 0x80)>>7)<<row);

    ws2812bDmaBitBuffer[(calcCol+5)] |= (((((invGreen)<<5) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+8+5)] |= (((((invRed)<<5) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+16+5)] |= (((((invBlue)<<5) & 0x80)>>7)<<row);

    ws2812bDmaBitBuffer[(calcCol+6)] |= (((((invGreen)<<6) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+8+6)] |= (((((invRed)<<6) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+16+6)] |= (((((invBlue)<<6) & 0x80)>>7)<<row);

    ws2812bDmaBitBuffer[(calcCol+7)] |= (((((invGreen)<<7) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+8+7)] |= (((((invRed)<<7) & 0x80)>>7)<<row);
    ws2812bDmaBitBuffer[(calcCol+16+7)] |= (((((invBlue)<<7) & 0x80)>>7)<<row);
#elif defined(SETPIX_4)

    // Bitband optimizations with pure increments, 5us interrupts
    uint32_t *bitBand = BITBAND_SRAM(&ws2812bDmaBitBuffer[(calcCol)], row);

    *bitBand =  (invGreen >> 7);
    bitBand+=16;

    *bitBand = (invGreen >> 6);
    bitBand+=16;

    *bitBand = (invGreen >> 5);
    bitBand+=16;

    *bitBand = (invGreen >> 4);
    bitBand+=16;

    *bitBand = (invGreen >> 3);
    bitBand+=16;

    *bitBand = (invGreen >> 2);
    bitBand+=16;

    *bitBand = (invGreen >> 1);
    bitBand+=16;

    *bitBand = (invGreen >> 0);
    bitBand+=16;

    // RED
    *bitBand =  (invRed >> 7);
    bitBand+=16;

    *bitBand = (invRed >> 6);
    bitBand+=16;

    *bitBand = (invRed >> 5);
    bitBand+=16;

    *bitBand = (invRed >> 4);
    bitBand+=16;

    *bitBand = (invRed >> 3);
    bitBand+=16;

    *bitBand = (invRed >> 2);
    bitBand+=16;

    *bitBand = (invRed >> 1);
    bitBand+=16;

    *bitBand = (invRed >> 0);
    bitBand+=16;

    // BLUE
    *bitBand =  (invBlue >> 7);
    bitBand+=16;

    *bitBand = (invBlue >> 6);
    bitBand+=16;

    *bitBand = (invBlue >> 5);
    bitBand+=16;

    *bitBand = (invBlue >> 4);
    bitBand+=16;

    *bitBand = (invBlue >> 3);
    bitBand+=16;

    *bitBand = (invBlue >> 2);
    bitBand+=16;

    *bitBand = (invBlue >> 1);
    bitBand+=16;

    *bitBand = (invBlue >> 0);
    bitBand+=16;

#endif
}

void ws2812b_init()
{
    ws2812b_gpio_init();
    DMA_init();
    TIM4_init();

    // Need to start the first transfer
    ws2812b.transferComplete = 1;

    // clear led strip initially
    memset(frameBuffer, 0, sizeof(frameBuffer));
    WS2812_sendbuf();
}

void ws2812_SetColor(uint16_t index, uint32_t color)
{
    if (index < (3 * WS2812B_NUMBER_OF_LEDS))
    {
        frameBuffer[index * 3 + 0] = (color >> 16);// & 0xFF;
        frameBuffer[index * 3 + 1] = (color >>  8);// & 0xFF;
        frameBuffer[index * 3 + 2] = (color >>  0);// & 0xFF;
    }
}

void ws2812b_handle()
{
    if(ws2812b.startTransfer) {
        ws2812b.startTransfer = 0;
        WS2812_sendbuf();
    }

}

#endif // #if defined(IS_JUNIOR_APPLICATION)
hubmartin commented 4 years ago

Hi, I'll try to look at the code later. I would probably need whole project to try to find anything suspicious. This is what comes to my mind when you described your issues:

domingguss commented 4 years ago

ah great, lowering the other DMA channel priorities seemed to have done the trick - CubeMX by default sets all DMA interrupt priorities at 0 (highest).

(UART3 is using the DMA as well)

Thanks!

hubmartin commented 4 years ago

Great! Can you share some information about your project? Or pictures. How many outputs and LEDs are you using? Thanks

domingguss commented 4 years ago

we have a STM32F3 that is constantly reading 8 RFID readers (MFRC522), as well the UART3 listening and responding to serial communication, and an array of 8*20 WS2812b LEDs to display the status of each RFID.

Untitled

Can't really say more about the project right now...🤐

I was actually also checking out driving the WS2812b through SPI like mtien888's example here, but I stumbled upon some SYSCLK differences (48hz vs 72hz) between STMF0 and STMF3. Fortunately your solution already worked out fine :)

hubmartin commented 4 years ago

Thanks for details. Nice hardware. I'm always curious why people choose my lib :)