cancel
Showing results for 
Search instead for 
Did you mean: 

Switching from STM32L4/F0 DMA to STM32F469l-DISCO DMA. What changes are needed to be made?

Fishmaster257
Associate III

Hello,

as many before I am trying to control some SK6812 adressable LEDs with a STM32 MCU. I have successfully done so on a custom board using the STM32L433CCT and this neat guide:

https://www.thevfdcollective.com/blog/stm32-and-sk6812-rgbw-led

and this gihub repository: https://github.com/hey-frnk/STM32_HAL_NeoPixel

I follwed the tutorial step by step using CubeMX and SW4STM32.

Now however I have to switch to a STM32F469l-DISCO board and I thought well the same code should work right? No! Looking at the signal using a scope, I saw that it was nowhere near the needed 800kHz. So after looking around in manuals and posts I figured out, that some changes to the DMA setup have to be made. See this post:

https://community.st.com/s/question/0D50X00009XkeDcSAJ/stm32f4-dma-and-varying-duty-cycle

So and here is my problem I dont really understand what exactly I have to change since they are setting up their DMA manually in the post above, while I used CubeMX to set it up again without FIFO and couldnt find such DMA_Init code in my main.c file. With FIFO.

0693W000006HZJ1QAO.png 

Edit: So after taking a break and thinking about the problem a bit, I think that my led_render, PulseHalfFinishedCallback and PulseFinishedCallback functions need to be adjusted to fit the new DMA Sorry for my misleading first question (if it really wasnt the problem :^) ). Code from the git above inbound:

// LED parameters
#define NUM_BPP (4) // SK6812
#define NUM_PIXELS (8) //number of LEDs controlled for tim1 -> adjust for application needed
#define NUM_BYTES (NUM_BPP * NUM_PIXELS)
 
// LED color buffer
uint8_t rgb_arr[NUM_BYTES] = {0};//Tim1
 
// LED write buffer
#define WR_BUF_LEN (NUM_BPP * 8 * 2)//Tim1
uint8_t wr_buf[WR_BUF_LEN] = {0};
uint_fast8_t wr_buf_p = 0;
 
void led_render() {
  if(wr_buf_p != 0 || hdma_tim1_ch1.State != HAL_DMA_STATE_READY) {
    // Ongoing transfer, cancel!
    for(uint8_t i = 0; i < WR_BUF_LEN; ++i) wr_buf[i] = 0;
    wr_buf_p = 0;
    HAL_TIM_PWM_Stop_DMA(&htim1, TIM_CHANNEL_1);
    return;
  }
  // Ooh boi the first data buffer half (and the second!)
  for(uint_fast8_t i = 0; i < 8; ++i) {
    wr_buf[i     ] = PWM_LO << (((rgb_arr[0] << i) & 0x80) > 0);
    wr_buf[i +  8] = PWM_LO << (((rgb_arr[1] << i) & 0x80) > 0);
    wr_buf[i + 16] = PWM_LO << (((rgb_arr[2] << i) & 0x80) > 0);
    wr_buf[i + 24] = PWM_LO << (((rgb_arr[3] << i) & 0x80) > 0);
    wr_buf[i + 32] = PWM_LO << (((rgb_arr[4] << i) & 0x80) > 0);
    wr_buf[i + 40] = PWM_LO << (((rgb_arr[5] << i) & 0x80) > 0);
    wr_buf[i + 48] = PWM_LO << (((rgb_arr[6] << i) & 0x80) > 0);
    wr_buf[i + 56] = PWM_LO << (((rgb_arr[7] << i) & 0x80) > 0);
  }
 
  HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t *)wr_buf, WR_BUF_LEN);
  wr_buf_p = 2; // Since we're ready for the next buffer
}
 
void HAL_TIM_PWM_PulseFinishedHalfCpltCallback(TIM_HandleTypeDef *htim) {
  // DMA buffer set from LED(wr_buf_p) to LED(wr_buf_p + 1)
  if(wr_buf_p < NUM_PIXELS) {
    // We're in. Fill the even buffer
    for(uint_fast8_t i = 0; i < 8; ++i) {
      wr_buf[i     ] = PWM_LO << (((rgb_arr[4 * wr_buf_p    ] << i) & 0x80) > 0);
      wr_buf[i +  8] = PWM_LO << (((rgb_arr[4 * wr_buf_p + 1] << i) & 0x80) > 0);
      wr_buf[i + 16] = PWM_LO << (((rgb_arr[4 * wr_buf_p + 2] << i) & 0x80) > 0);
      wr_buf[i + 24] = PWM_LO << (((rgb_arr[4 * wr_buf_p + 3] << i) & 0x80) > 0);
    }
    wr_buf_p++;
  } else if (wr_buf_p < NUM_PIXELS + 2) {
    // Last two transfers are resets. SK6812: 64 * 1.25 us = 80 us == good enough reset
    // First half reset zero fill
    for(uint8_t i = 0; i < WR_BUF_LEN / 2; ++i) wr_buf[i] = 0;
    wr_buf_p++;
  }
}
 
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {
  // DMA buffer set from LED(wr_buf_p) to LED(wr_buf_p + 1)
  if(wr_buf_p < NUM_PIXELS) {
    // We're in. Fill the odd buffer
    for(uint_fast8_t i = 0; i < 8; ++i) {
      wr_buf[i + 32] = PWM_LO << (((rgb_arr[4 * wr_buf_p    ] << i) & 0x80) > 0);
      wr_buf[i + 40] = PWM_LO << (((rgb_arr[4 * wr_buf_p + 1] << i) & 0x80) > 0);
      wr_buf[i + 48] = PWM_LO << (((rgb_arr[4 * wr_buf_p + 2] << i) & 0x80) > 0);
      wr_buf[i + 56] = PWM_LO << (((rgb_arr[4 * wr_buf_p + 3] << i) & 0x80) > 0);
    }
    wr_buf_p++;
  } else if (wr_buf_p < NUM_PIXELS + 2) {
    // Second half reset zero fill
    for(uint8_t i = WR_BUF_LEN / 2; i < WR_BUF_LEN; ++i) wr_buf[i] = 0;
    ++wr_buf_p;
  } else {
    // We're done. Lean back and until next time!
    wr_buf_p = 0;
    HAL_TIM_PWM_Stop_DMA(&htim2, TIM_CHANNEL_1);
  }
}

Any help on what changes need to be made , whether with CubeMX or via coding, are much appreciated!

1 ACCEPTED SOLUTION

Accepted Solutions
Fishmaster257
Associate III
4 REPLIES 4
Khouloud ZEMMELI
ST Employee

Hi @Fishmaster257​ 

Can you please share you ioc file to check the problem of DMA_Init generation function in main ?

Thanks, Khouloud

The base timing is going to depend on the clocks to the bus. The Prescaler and Period of the TIM will need to be changed to hit the desired frequency. In some cases the CPU frequency will need changing if integer dividers are not sufficient.

T​he HAL or LL should provide the means to drive a TIMs CCRx registers. The DMA TIM relationship will be in the Reference Manual. The older SPL might have been easier.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Fishmaster257
Associate III

Thanks for your fast answers. @Khouloud ZEMMELI​  I pinned the .ioc file to this comment. As far as my understanding goes, my DMA_Init only sets up the interrupts needed fot the DMA itself.

Concerning the clocks on the bus, I set up the Sysclock to be 180MHz and through the APB1/ABP2 prescalers and multipliers I set up the APB1/APB2 timer clocks to be 90MHz. I didnt mention it earlier, thought it didn't matter. I want to use two DMA channels and Timers for a parallel control of 2 LED-strips.

My timer periods are 111 (112) each, resulting in ~803,6 kHz (1,244 µs) so well within the range of the LEDs. My low duty cycle being 28 (25% and logic 0) and my high duty cycle being 56 (50% and logic 1), which are needed for the logic ones and zeros the LEDs understand.

Thanks again.

Fishmaster257
Associate III

See here for my other question with solution: https://community.st.com/s/feed/0D53W00000ToyBISAZ