Search code examples
cstm32dmaneopixelstm32-hal

Problems controlling SK6812RGBW/Neopixel with STM32H7


I'm trying to control a strip of 9 SK6812RGBW LED's with an STM32H757XIH6 microcontroller. I've been through a couple of tutorials on how to use DMA to control the PWM, and I've gone with this tutorial as a base.

I'm fairly new to this level of microcontroller programming with timers, DMA, etc.

These are the timings for SK6812RBGW: SK6812RBGW timings

The clock is configured as so: Clock config

The APB1 Timer clock is at 240mhz

I'm using TIM3 configured as shown: Timer settings Timer settings Timer settings

Now I've tried a few different configs of the DMA stream, currently I'm trying with half word as TIM3 is a 16 bit timer.

The LED strip is connected to PC7 via a 1.3kohm pullup resistor to 5V.

Here's the code I'm using, mostly following the digikey tutorial code:

/*
 * led.h
 *
 *  Created on: Jan 27, 2025
 *      Author: Graeme
 */

#ifndef APPLICATION_USER_CORE_DASH_APP_LED_LED_H_
#define APPLICATION_USER_CORE_DASH_APP_LED_LED_H_

#include "header.h"
#include "tim.h"

#define NEOPIXEL_ZERO 72
#define NEOPIXEL_ONE 144

#define NUM_PIXELS 9
#define DMA_BUFF_SIZE (NUM_PIXELS * 32) + 64

typedef union
{
  struct
  {
    uint8_t w;
    uint8_t b; //switch coz some reason
    uint8_t g;
    uint8_t r;
  } color;
  uint32_t data;
} PixelRGBW_t;

extern void led_test();

#endif /* APPLICATION_USER_CORE_DASH_APP_LED_LED_H_ */

/*
 * led.c
 *
 *  Created on: Jan 27, 2025
 *      Author: Graeme
 */

#include "led.h"

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {
    //HAL_TIM_PWM_Stop_DMA(htim, TIM_CHANNEL_2);
}

void led_test() {

    PixelRGBW_t pixel[NUM_PIXELS] = { 0 };
    uint32_t dmaBuffer[DMA_BUFF_SIZE]__attribute__((aligned(4))) = { 0 };
    uint32_t *pBuff;



    for (int i = 0; i < NUM_PIXELS; i++) {
        pixel[i].color.r = 3;
        pixel[i].color.g = 7;
        pixel[i].color.b = 15;
        pixel[i].color.w = 31;
    }

    pBuff = dmaBuffer;
    for (int i = 0; i < NUM_PIXELS; i++) {
        for (int j = 31; j >= 0; j--) {
            if ((pixel[i].data >> j) & 0x01) {
                *pBuff = NEOPIXEL_ONE;
            } else {
                *pBuff = NEOPIXEL_ZERO;
            }
            pBuff++;
        }
    }


    for (int i = 0; i < 64; i++) {
        dmaBuffer[DMA_BUFF_SIZE - 65 + i] = 0;
    }

//      dmaBuffer[DMA_BUFF_SIZE - 1] = 0; // last element must be 0!

        HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_2, dmaBuffer, DMA_BUFF_SIZE);
        //HAL_Delay(10);
        //HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_2);


}

(I have commented out the DMA_Stop as I was trying to use circular mode (??))

When ran, the LED's illuminate, but randomly, and random colors.

Inspecting the dmaBuffer shows the bits in the correct positions for the SK6812RGBW encoding: dma buffer inspect SK6812 bit order

I have some confusion around the ARR setting and the CCR values, as they don't seem to line up with what should and shouldn't work. With ARR set to 299, CCR for high of 144 and CCR for low of 72, my output data seems to run at 400Khz with a period of 2.5us (should be 800mhz, 1.25us). But, the bit timings are correct at around 0.6us and 0.3us respectively. When ARR set at 149, CCR high 72 and low 36, the data runs at 800Khz and 1.25 period, but the bit timings are looking like half of what they should be.

Keeping in mind I'm using a cheap handheld scope.

Either way, the LED's seem to light up randomly, which seems odd. Shouldn't they only work on one set of timings? They don't flicker or anything. They change on reset.

The other confusion is setting of of DMA, FIFO, Normal/Circular, memory length etc.

Any ideas? Let me know if I can provide any more info


Solution

  • You can never allocate a DMA buffer on the stack. Besides the obvious risk for stack overflow, DMA requires a fixed address. It must also be declared as volatile so the compiler makes no strange assumptions about what's stored there.

    An array of uint32_t is by definition 4 byte aligned, so no need for non-standard keywords.

    That is:

    static volatile uint32_t dmaBuffer[DMA_BUFF_SIZE] = { 0 };
    

    If the DMA driver needs access to the variable to know the address, you might have to drop static and declare it outside of any function. Another option would be to allocate the variable in a custom segment always placed at a certain address.