2025-09-05 1:22 AM - last edited on 2025-09-05 5:26 AM by Saket_Om
I'm trying to drive the PWM of an STM32G0 but the waveform generated had an inconsistent period between bit 0 and bit 1, and I can't understand why
I'm using TIMER 4 on Channel 2. On the main.c I'm initializing MX_DMA_Init
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA1_Channel2_3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
}
and TIM4 configured as
void MX_TIM4_Init(void)
{
/* USER CODE BEGIN TIM4_Init 0 */
HAL_TIM_Base_DeInit(&htim4);
/* USER CODE END TIM4_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim4.Instance = TIM4;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 60 - 1;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM4_Init 2 */
/* USER CODE END TIM4_Init 2 */
HAL_TIM_MspPostInit(&htim4);
}
The HAL_TIM_MspPostInit has
__HAL_RCC_GPIOD_CLK_ENABLE();
/**TIM4 GPIO Configuration
PD13 ------> TIM4_CH2
PD14 ------> TIM4_CH3
PD15 ------> TIM4_CH4
*/
GPIO_InitStruct.Pin = OUT_uC3_Pin|OUT_uC2_Pin|OUT_uC1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM4;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
with
#define OUT_uC3_Pin GPIO_PIN_13
#define OUT_uC3_GPIO_Port GPIOD
The led_render function correctly fills the buffer with
// Fill with alternating pattern: HI, LO, HI, LO...
for(uint8_t i = 0; i < WR_BUF_LEN; i++) {
wr_buf[i] = (i % 2) ? PWM_HI : PWM_LO;
}
HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_2, (uint32_t *) wr_buf, WR_BUF_LEN);
and in the .h
#define PWM_HI (38)
#define PWM_LO (19)
// LED parameters
#define NUM_BPP (3) // WS2812B
#define NUM_PIXELS (18)
#define NUM_BYTES (NUM_BPP * NUM_PIXELS)
But for some reason I'm seeing a the strange, not correct, waveform
Am I doing something wrong?
2025-09-05 3:59 AM - edited 2025-09-05 4:07 AM
What is the expected waveform and how is the observed one different?
[EDIT] Assuming timer clock is equal to system clock, 60 cycles may be sufficient for DMA, but AFAIK Cube/HAL does not use the Update event to load TIMx_CCRx but the CC event, which may leave much less time, and result in DMA not feeding the TIMx_CCRx fast enough. I don't say this *is* the problem here (as I don't know what are the symptoms, see my first question), but may be a thing to consider. Try longer times, or higher system clock frequency.
JW
2025-09-05 4:46 AM
What is the declaration of wr_buf and where is it placed?
2025-09-05 5:52 AM
Hello @nico23
Please refer to the example TIM_PWMOutput in the STM32CubeG0 package.
2025-09-05 5:57 AM
Hi @waclawek.jan and thanks for answering.
Expected waveform has a bunch of 1 and 0 encoding the LEDs colours for istance
Instead the one I previously upload has only zeros.
System clock is 48Mhz (HAL_RCC_GetSysClockFreq and HAL_RCC_GetPCLK1Freq both return 48M) so the period for each beat should be around 1,25us
but instead I'm seeing different periods. Below a working PWM generated by DMA
WHat do you mean with Try longer times, or higher system clock frequency. ?
2025-09-05 6:00 AM
Hi @gbm and thanks for the answer
wr_buff is defined on top of adressable_LED.c (same file where led_render function is in)
// LED write buffer
#define WR_BUF_LEN (NUM_BPP * 8 * 2)
uint8_t wr_buf[WR_BUF_LEN] = { 0 };
uint_fast8_t wr_buf_p = 0;
2025-09-05 6:54 AM
Timer DMA expects 32-bit samples. You are preparing 8-bit duty values, then writing them as 32-bit words - that doesn't make too much sense - 4 values are written as single 32-bit duty value.
Sidenote: it is much easier and more memory-efficient to use UART or SPI instead of a timer for controlling WS2812-alikes.
2025-09-05 7:08 AM - edited 2025-09-05 7:11 AM
I'm using this method/code because it was already implemented into an STM32F091 and it worked pretty well. I'm using 8-bit as the value is always < 256 so, I would save space on storing the buffer and the value copied should be teh same, right?
I've basically ported it to the STM32G0 but I'm stuck with this issue.
About your sidenote, I didn't know about that. I'll check if PD15 could be set to use UART or SPI instead of a timer (but I think I'll have to re-write a great part of the code)
2025-09-05 7:16 AM
Hi @Saket_Om
the example doesn't uses DMA. Do you suggest to avoid using it? Also, I need to drive the PWM using the buffer
2025-09-05 7:58 AM
Below is an example with DMA: