2025-09-06 1:57 PM - edited 2025-09-06 2:35 PM
I'm working on a STM32C071RB, which I've managed to generate PWM pulses as desired. However, I try to more than once while the chip is running, it stops working.
My main.c file:
#include "main.h"
#define PWM_HI (12)
#define PWM_LO (6)
TIM_HandleTypeDef htim1;
DMA_HandleTypeDef hdma_tim1_ch1;
uint8_t wr_buf[32] = {0};
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_TIM1_Init(void);
void led_set_RGB(uint8_t r, uint8_t g, uint8_t b) {
for(int i = 0; i < 8; i++){
wr_buf[i] = (g & (1 << i)) ? PWM_HI : PWM_LO;
wr_buf[i + 8] = (r & (1 << i)) ? PWM_HI : PWM_LO;
wr_buf[i + 16] = (b & (1 << i)) ? PWM_HI : PWM_LO;
}
}
void led_render() {
while (HAL_DMA_GetState(&hdma_tim1_ch1) != HAL_DMA_STATE_READY) {}
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t *)wr_buf, 32);
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM1) {
// Stop PWM and DMA after all pulses are transfered
HAL_TIM_PWM_Stop_DMA(htim, TIM_CHANNEL_1);
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM1_Init();
while (1)
{
led_set_RGB(0xFF,0x00,0x00);
led_render();
HAL_Delay(500);
led_set_RGB(0x00,0xFF,0x00);
led_render();
HAL_Delay(500);
led_set_RGB(0x00,0x00,0xFF);
led_render();
HAL_Delay(500);
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 2;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 19;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.BreakFilter = 0;
sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;
sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
sBreakDeadTimeConfig.Break2Filter = 0;
sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htim1);
}
static void MX_DMA_Init(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */
As you can see in the while loop in the main function, it manages to run the first segment of led_render(), however, whenever led_render() is called again, it just leads to HAL_TIM_PWM_Start_DMA() to return HAL_ERROR, which seem to be coming from HAL_DMA_Start_IT().
Update:
I discovered, by not calling HAL_TIM_PWM_Stop_DMA(), it works fine. Might be a bug that you cannot call the _Start_DMA() after _Stop_DMA() ??
2025-09-07 1:56 AM - edited 2025-09-07 1:58 AM
Most of what seem to be bugs in Cube/HAL are incorrect usage, or, more precisely, usage different from what was anticipated by Cube/HAL authors. Unfortunately, that's poorly documented, as Cube/HAL is mostly a vehicle to sell through the idea of "programming by clicking", i.e. it's intended to provide the fabric needed to CubeMX to be usable.
So, you can try to find the expected usage in the provided "documentation". However, Cube/HAL is open source, so you may be better of simply looking into the sources to try to find out the intended usage.
Comparing differences in TIM and DMA registers content of the "working" and "non-working" cases (just before DMA is enabled, that is, the very line deep in Cube/HAL which enables the DMA channel) will provide insight to the context under which the DMA works/does not work as expected.
JW
2025-09-07 5:09 AM
wr_buf should be declared as uint32_t, as I already wrote in another thread. The likely problem is the alignment error.
2025-09-07 5:26 AM
> The likely problem is the alignment error.
But would that be the cause, that would prevent running it in the first time, too, wouldn't it?
JW
2025-09-07 3:26 PM
I did try to define wr_buf as uint32_t and change settings for DMA: Data Width for Memory from Byte to Word. Result, it just ended up setting the order of a "HIGH" and "LOW" PWM pulse incorrectly, yielding in an undesirable result.
I tried to find the thread you talked about, and I couldn't find it under your profile.
2025-09-08 3:41 AM
Hello @EngEmil
Is the hdma state is deferent from HAL_DMA_STATE_READY before calling the HAL_DMA_Start_IT()?
2025-09-08 3:41 AM - edited 2025-09-08 3:41 AM
What @gbm probably meant was, that besides setting DMA to word, you'd change also the array's definition to uint32_t.
As I wrote above, I don't think this is the root of problem.
JW