2026-02-05 10:06 AM
hello everybody
I am programming 10 addressable Leds with PWM with DMA using the M7 cortex of an STM32H747 in a custom board.
Characteristics:
If I run the timer alone (fixed duty), the PWM is correct and stable.
When I enable PWM + DMA, the waveform becomes random/discontinuous. On the oscilloscope I see “splits” and gaps; the PWM is not continuous. For example, the first 8 bits might be high and the following 16 low (intended to represent RED), but the stream breaks unpredictably. The data buffer looks correct when inspected in the debugger.
I am using channel 4 timer 3. with internal clock source.
and DMA is configured as:
where data width is half word since the pulse of the PWM generator channel 4 is 16 bit value.
Here is my code. All IRQs are activated.
#define NUMTOTALLEDS 10
#define NUMCOLORS 24 //< 8 BYTESxCOLOUR
#define STARTSAFEBUFFER 50
#define ENDSAFEBUFFER 50
// Placed in RAM_D2
uint16_t pwmData[NUMCOLORS*NUMTOTALLEDS+STARTSAFEBUFFER+ENDSAFEBUFFER];
const
uint16_t ALLRED [340] = {
// Start safe buffer
PWM_RESET, PWM_RESET, PWM_RESET ...
// 8 bits high + 8 bits low + 8 bits low (Red, gren, blue)
PWM_HIGH, PWM_HIGH, PWM_HIGH, PWM_HIGH, PWM_HIGH, PWM_HIGH, PWM_HIGH, PWM_HIGH, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW, PWM_LOW,
// End safe buffer
... PWM_RESET, PWM_LOW, PWM_LOW};
// First of all i cpy a static buffer with all the information to fill 10 leds in red
memcpy(pwmData, ALLRED, sizeof(pwmData));
// Then
if (HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_4 , (uint32_t *)pwmData, sizeof(pwmData)) == HAL_OK) {
// It returns Ok always
}
// Callback
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_4);
}Everything is normal to me, but this is what my oscilloscope gets:
with zoom (the first 8 bits are high, and the 16 folowing are low, meaning red colour (RGB)):
My problem is that the PWM is random, and never correct.
What can I do?
Thanks in advance,
2026-02-05 10:37 AM
Hi,
Change in configuration in DMA for Memory from HalfWord to Byte. Do not change for Peripheral. Also change buffer pwmData and ALLRED from uint16_t to uint8_t.
Best Regards.
2026-02-05 10:54 AM
Yeah,
i tried so, however i do not why (maybe is a bug) both memory an peripheral must have the same length. I mean, the GUI forces the memory size to be equal to peripheral…
2026-02-05 10:59 AM
Hi,
Change in the generated code, not in GUI. Only for test.
2026-02-05 12:22 PM
1. Why don't you use UART or SPI - much less data required.
2. I don't use HAL, so I am not an expert there but I believe the size argument for HAT_PWM_Start_DMA should be the number of items rather than size in bytes.
3. When using PWM, make sure to add one more element to the table with a value of 0, so that timer output stays at 0 between packets.
4. Make sure that pwmData is a static object, not a function's local variable.
2026-02-05 12:28 PM
Thankyou.
do you know any examples using uart?
thanks ;)
2026-02-05 11:33 PM
Here are some code fragments for G030 - should be easy to port to any new STM32.
// initial setup - note the setting of USART CR2
RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN;
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
RCC->APBENR2 = RCC_APBENR2_USART1EN;
// the UART may also work with 8-bit frames, this requires shifting the data by one to the left (verified)
WS_UART->BRR = (HCLK_FREQ + WSBAUD / 2) / WSBAUD;
WS_UART->CR2 = USART_CR2_TXINV | USART_CR2_MSBFIRST | USART_CR2_DATAINV | USART_CR2_SWAP;
WS_UART->CR3 = USART_CR3_DMAT;
WS_UART->CR1 = USART_CR1_M1 | USART_CR1_TE | USART_CR1_UE;
WS_DMACH->CPAR = (uint32_t)&WS_UART->TDR;
BF2F(GPIOB->MODER, 7) = GPIO_MODER_AF;
DMAMUX1_Channel0->CCR = DMAREQ_ID_USART1_TX;
// encoding ==============================================================
void encode(uint8_t *encbuf, const struct ledc_ *img, uint16_t nleds)
{
// MSB-first encoding table
static const uint8_t uenc[] = {
0000 | 022, 0001 | 022, 0010 | 022, 0011 | 022,
0100 | 022, 0101 | 022, 0110 | 022, 0111 | 022
};
for (uint16_t i = 0; i < nleds; i++)
{
uint32_t v = img[i].r | img[i].g << 8 | img[i].b << 16;
for (uint8_t b = 0; b < 8; b++)
{
*encbuf++ = uenc[v >> 21 & 7];
v <<= 3;
}
}
}
// sending ===============================================================
encode(txbuf, ledcolor, NLEDS);
WS_DMACH->CCR = 0;
WS_DMACH->CMAR = (uint32_t)txbuf;
WS_DMACH->CNDTR = sizeof(txbuf);
WS_DMACH->CCR = DMA_CCR_DIR_M2P | DMA_CCR_MINC | DMA_CCR_EN;P9823 and similar chips will not work with 1/3 + 2/3 encoding (same for timer, SPI and UART). They require at least 4 bit slices per data bit (so 2 bits per UART frame instead of 3 used by WS2812).