[SOLVED] STM32C071RB DMA callback not working?
Posted: Sat Sep 13, 2025 10:55 pm
Hi!
I've been trying to setup a simple example on controlling a LED (WS2812) on the STM32C071RB chip with PWM + DMA. Most things work! However, it seems that the callback dma_callback is never called. Any tips on what to look for to make it work?
Here is the main.c file for the example code:
I've been trying to setup a simple example on controlling a LED (WS2812) on the STM32C071RB chip with PWM + DMA. Most things work! However, it seems that the callback dma_callback is never called. Any tips on what to look for to make it work?
Here is the main.c file for the example code:
Code: Select all
#include "ch.h"
#include "hal.h"
#define PWM_HI (12)
#define PWM_LO (6)
#define BITS_PER_PIXEL (24)
#define WR_BUF_SIZE (BITS_PER_PIXEL + 1U) // Include 1 extra bit
uint8_t wr_buf[WR_BUF_SIZE] = {0}; // Ensure last value is untouched (always zero).
static const PWMConfig pwm_cfg = {
.frequency = 16000000, // Counter clock frequency for PSC=2
.period = 20, // PWM period in ticks (ARR + 1)
.callback = NULL,
.channels = {
{.mode = PWM_OUTPUT_ACTIVE_HIGH, .callback = NULL},
{.mode = PWM_OUTPUT_DISABLED, .callback = NULL},
{.mode = PWM_OUTPUT_DISABLED, .callback = NULL},
{.mode = PWM_OUTPUT_DISABLED, .callback = NULL}
},
.cr2 = STM32_TIM_CR2_CCDS, // DMA requests on capture/compare events
#if STM32_ADVANCED_DMA
.bdtr = 0,
#endif
.dier = STM32_TIM_DIER_CC1DE // Enable DMA on CC1 event
};
static const stm32_dma_stream_t *dma_stream; // Global DMA stream pointer
//static volatile bool dma_ready = true;
static void dma_callback(void *p, uint32_t flags) {
(void)p;
if (flags & STM32_DMA_ISR_TCIF) {
//dma_ready = true;
}
// Handle errors if flags & STM32_DMA_ISR_TEIF, etc.
}
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;
}
// wr_buf[24] remains 0
}
void led_render(void) {
// Commented out since DMA callback is never called.
/*
while (!dma_ready) {
chThdSleepMilliseconds(1); // Wait for previous DMA to complete
}
dma_ready = false;
*/
dmaStreamDisable(dma_stream);
dmaStreamSetMemory0(dma_stream, wr_buf);
dmaStreamSetTransactionSize(dma_stream, WR_BUF_SIZE);
dmaStreamEnable(dma_stream);
}
int main(void) {
halInit();
chSysInit();
// Set PA0 to alternate function for TIM16_CH1 (AF2)
palSetPadMode(GPIOA, GPIOA_PIN0, PAL_MODE_ALTERNATE(2));
// Start PWM
pwmStart(&PWMD16, &pwm_cfg);
// Setup DMA
dma_stream = dmaStreamAlloc(STM32_DMA_STREAM_ID(1U, 1U), 0,
(stm32_dmaisr_t)dma_callback, NULL);
dmaSetRequestSource(dma_stream, 44); // DMAMUX request 44 for TIM16_CH1 (See RM0490 Reference Manual, Table 49)
dmaStreamSetPeripheral(dma_stream, &TIM16->CCR1);
dmaStreamSetMode(dma_stream,
STM32_DMA_CR_DIR_M2P // Memory to peripheral
| STM32_DMA_CR_MINC // Increment memory pointer
| STM32_DMA_CR_PSIZE_HWORD // Peripheral size 16 bits
| STM32_DMA_CR_MSIZE_BYTE // Memory size 8 bits
| STM32_DMA_CR_TCIE // Transfer complete interrupt enable
| STM32_DMA_CR_TEIE // Transfer error interrupt enable
| STM32_DMA_CR_PL(0) // Priority low
);
// Start the PWM channel. PWMD for TIM16 (&PWMD16), Channel 1 (0), duty cycle 0 (off)
//pwmEnableChannel(&PWMD16, 0, 0); // Not needed
while (true) {
led_set_RGB(0xFF, 0x00, 0x00);
led_render();
chThdSleepMilliseconds(500);
led_set_RGB(0x00, 0xFF, 0x00);
led_render();
chThdSleepMilliseconds(500);
led_set_RGB(0x00, 0x00, 0xFF);
led_render();
chThdSleepMilliseconds(500);
}
return 0;
}