pointersstructstm32unionsstm32f4

STM32 HAL_TIM_PWM_PulseFinishedCallback not being called. Suspect that problem is related to use of struct or union pointers


I'm writing a Neopixel driver for STM32 based on the example provided here.

In summary, the pixels aren't lighting correctly. My latest attempt had the first row of 8 pixels light up green before it stalled. This doesn't happen every time, but regardless, this assortment of LEDs light up eventually, always in the same colors..

These are the related functions in the driver I'm working on:

#define NUM_PIXELS 40
#define RGBW_DMA_BUFF_SIZE (NUM_PIXELS * 32) + 1

typedef union
{
  struct
  {
    uint8_t w;
    uint8_t b;
    uint8_t r;
    uint8_t g;
  } color;
  uint32_t data;
} PixelRGBW_t;

/*
 * Note, must use a 32bit timer channel to work.
 */
typedef struct
{
    TIM_HandleTypeDef timer;
    uint32_t timer_channel;
    uint32_t pixel_length;
    uint32_t buf_length;
}PixelDRV_t;

uint8_t pixelRGBW_init(PixelDRV_t *strip, PixelRGBW_t *color, TIM_HandleTypeDef setTIMER, uint32_t setCHANNEL, uint32_t setLENGTH, uint32_t *Pbuff)
{

    strip->timer = setTIMER;
    strip->timer_channel = setCHANNEL;
    strip->pixel_length = setLENGTH;
    strip->buf_length = (setLENGTH * 32) + 1;
    for(int x = 0; x < setLENGTH; x++)
    {
        for (int b = 31; b >= 0; b--)
        {
            if ((color->data >> b) & 0x01)
            {
                *Pbuff = NEOPIXEL_RGBW_ONE;
            }
            else
            {
                *Pbuff = NEOPIXEL_RGBW_ZERO;
            }

            Pbuff++;
        }
        color++;
    }
    return HAL_OK;
}

uint8_t setPixelColorMonoRGBW(PixelDRV_t *strip, PixelRGBW_t *color, uint32_t pixel, uint8_t red, uint8_t green, uint8_t blue, uint8_t white)
{
    color[pixel].color.r = red;
    color[pixel].color.g = green;
    color[pixel].color.b = blue;
    color[pixel].color.w = white;
    return HAL_OK;
}
uint8_t setPixelLengthColorMonoRGBW(PixelDRV_t *strip, PixelRGBW_t *color, uint8_t red, uint8_t green, uint8_t blue, uint8_t white)
{
    for(int a = 0; a < strip->pixel_length; a++)
    {
        color[a].color.r = red;
        color[a].color.g = green;
        color[a].color.b = blue;
        color[a].color.w = white;
    }
    return HAL_OK;
}
/*
Sets the relevant "1" and "0" bits into the buffer to be written to the Neopixels via PWM. Must set whole strip's values, even if you are only updating one LED in one pixel, since all pixels need to know their RGB values, even if they are zero.
*/
uint8_t setPixelBufRGBW(PixelDRV_t *strip, PixelRGBW_t *color, uint32_t *Pbuff)
{
    for(int x = 0; x < strip->pixel_length; x++)
    {
        for (int b = 31; b >= 0; b--)
        {
            if ((color[x].data >> b) & 0x01)
            {
                *Pbuff = NEOPIXEL_RGBW_ONE;
            }
            else
            {
                *Pbuff = NEOPIXEL_RGBW_ZERO;
            }
            Pbuff++;
        }
    }
    return HAL_OK;
}

uint8_t transmitPixelBufRGBW(PixelDRV_t *strip, PixelRGBW_t *color, uint32_t *Pbuff)
{
    HAL_TIM_PWM_Start_DMA(&strip->timer, strip->timer_channel, Pbuff, strip->buf_length);
    return HAL_OK;
}

And this is the related code in the main file I'm using to test/troubleshoot it.

PixelRGBW_t STRIP1;
PixelRGBW_t* strip1 = &STRIP1;

PixelDRV_t PIXEL_DRV1;
PixelDRV_t* pixel_drv1 = &PIXEL_DRV1;

uint32_t dmaBuffer[RGBW_DMA_BUFF_SIZE] = {0};
uint32_t *pBuff = &dmaBuffer;

void Init_NeoPixels();
void ascendingRed();
void descendingRed();
void ascendingGreen();
void descendingGreen();
void ascendingBlue();
void descendingBlue();

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
  HAL_TIM_PWM_Stop_DMA(&htim2, TIM_CHANNEL_3);
}

int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_I2C1_Init();
  MX_I2S3_Init();
  MX_DMA_Init();
  MX_TIM2_Init(); //timer being used for neopixels.
  MX_SPI2_Init();
  /* USER CODE BEGIN 2 */
  Init_NeoPixels();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
      ascendingRed();
      HAL_Delay(1000);
      descendingRed();
      HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

void Init_NeoPixels()
{
    pixelRGBW_init(pixel_drv1, strip1, htim2, TIM_CHANNEL_3, 40, pBuff);
}

void ascendingRed()
{
    for(int x = 0; x < pixel_drv1->pixel_length; x++)
    {
        setPixelColorMonoRGBW(pixel_drv1, strip1, x, 255, 0, 0, 0);
        setPixelBufRGBW(pixel_drv1, strip1, pBuff);
        transmitPixelBufRGBW(pixel_drv1, strip1, pBuff);
        HAL_Delay(125);
    }
}

void descendingRed()
{
    for(int x = pixel_drv1->pixel_length; x >= 0; x--)
    {
        setPixelColorMonoRGBW(pixel_drv1, strip1, x, 0, 0, 0, 0);
        setPixelBufRGBW(pixel_drv1, strip1, pBuff);
        transmitPixelBufRGBW(pixel_drv1, strip1, pBuff);
        HAL_Delay(125);
    }
}

The goal is to light up the LEDs in one solid color, one after the other, then turn them off in the opposite direction. Instead, I'm getting whatever nonsense is happening in the image I showed above.

My initial assumption was that I wasn't using the union pointer within the functions properly (and that's still in the back of my mind), but as I did more troubleshooting, I noticed that HAL_TIM_PWM_PulseFinishedCallback was only being called once, the very first time in ascendingRed's for loop, and never again. Normally that means the Pulse value (CC1) hasn't been reached.

I compared the contents of htim3 during what appears to be the first successful cycle with its contents on all the subsequent ones and saw this in the debugger:

These two images are before and during the successful call for HAL_TIM_PWM_PulseFinishedCallback respectively: Before entering HAL_TIM_PWM_PulseFinishedCallback for the first time

HAL_TIM_PWM_PulseFinishedCallback being called

And afterwards:

Loop 2 Loop 3

I'm pretty sure the problem is in pointer structs I'm using to keep all the information grouped together. Does anyone see anything I've missed?

edit: I've made some progress thanks to someone elsewhere suggesting I switch PixelDRV_t's timer parameter to use a pointer.

typedef struct
{
    TIM_HandleTypeDef* timer;
    uint32_t timer_channel;
    uint32_t pixel_length;
    uint32_t buf_length;
}PixelDRV_t;

uint8_t pixelRGBW_init(PixelDRV_t *strip, PixelRGBW_t *color, TIM_HandleTypeDef* setTIMER, uint32_t setCHANNEL, uint32_t setLENGTH, uint32_t *Pbuff)
{
    /*
    if(strip->pixel_length != sizeof(color))
    {
        return HAL_ERROR;
    }
    */
    strip->timer = setTIMER;
    strip->timer_channel = setCHANNEL;
    strip->pixel_length = setLENGTH;
    strip->buf_length = (setLENGTH * 32) + 1;
    for(int x = 0; x < setLENGTH; x++)
    {
        for (int b = 31; b >= 0; b--)
        {
            if ((color->data >> b) & 0x01)
            {
                *Pbuff = NEOPIXEL_RGBW_ONE;
            }
            else
            {
                *Pbuff = NEOPIXEL_RGBW_ZERO;
            }

            Pbuff++;
        }
        color++;
    }
    return HAL_OK;
}

Now the interrupt is being called when it should be, and the LEDs are being updated both ways! However, I still have that problem at the start, where the LEDs are lighting up in an odd pattern before being overwritten by the desired LED values.


Solution

  • So the answer for most of the problem was rather obvious in retrospect, and listed above in my edits.

    I was copying the timer struct into the the pixelDRV_t struct, rather than using the original struct, which was causing all sorts of mess to occur. I switched pixelDRV_t to instead point to the original struct, and now everything is running fine! I'm still not 100% sure why the LEDs are lighting up in that same pattern at the start, but my guess is that it's because there weren't set values. I adjusted the code to set all the LED values to zero at the start, which seems to have cleared up the problem.