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-08 4:44 AM
Could it be an issue on the MOSFET that actually drives the OUT and not the microcontroller output itself?
I'm currently attached to OUT, and not OUT_uC, and I'm unable to have a probe at the OUT_uC
2025-09-08 6:50 AM - edited 2025-09-08 6:51 AM
What hardware is this, your own, or some "known good" board such as Nucleo or Disco?
In the former case, what's the power supply, are all VSS/VDD pins connected properly and with proper voltage at them, what's the primary clock source, how are FLASH waitstates set up?
Is there any other code running, or is this result of a minimal example doing nothing but the DMA/PWM thing?
JW
2025-09-08 11:21 PM
Yes, it is custom hardware. Yes, all VSS/VDD pins are connected properly with the proper voltage. The primary clock source is the STM32G0 internal oscillator.
The code is part of a bigger project so, yeah, there are other things running
2025-09-09 1:59 AM
Can't those other things be in the way?
In other words, prepare a simple minimal program which does nothing but the PWM+DMA, and try.
JW
2025-09-09 2:17 AM - edited 2025-09-09 2:18 AM
Is there a pullup resisitor at the WS2812 input? The transisitor output is effectively open drain, so you need to enforce logic high state somehow.
And again, make sure that timer duty data is of uint32_t type an the DMA is programmed to transfer words.
Depending on the MCU family, it may also work with uint16_t and 16-bit transfers but definitely NOT with uint8_t and 8-bit transfers.
2025-09-09 2:52 AM
@gbm,
> Depending on the MCU family, it may also work with uint16_t and 16-bit transfers but definitely NOT with uint8_t and 8-bit transfers.
It may work.
The single-port DMA in 'G0 zero-extends the data if destination width (here, DMA_CCRx.PSIZE) is higher than source width (here, DMA_CCRx.MSIZE), see Programmable data width and endian behavior table. In this detail the single-port DMA behaves differently from the dual-port DMAs e.g. in 'F4. We haven't been shown how exactly is the DMA set up.
JW
2025-09-09 3:10 AM
I'm connecting directly the OUT in the scheme to the data line of the WS2812. The WS2812 has two other lines, 5V and GND, which are connected to +5V 0V.
2025-09-09 3:58 AM
@nico23 So you definitely need a pull-up resistor of 1k5 to 4k7 between the WS2812 input and +5V. Another important thing: your PWM duty data array should probably end with an entry of duty 0, at least if you don't disable timer output.
@waclawek.jan That's something different from older series - good to know. I implemented WS2812 via timer on F1, F1 and L4 few years ago. As of today I only use UART or SPI for WS2812.