Page 1 of 1

[SOLVED] STM32C071RB DMA callback not working?

Posted: Sat Sep 13, 2025 10:55 pm
by Engemil
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:

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;
}


Re: STM32C071RB DMA callback not working?  Topic is solved

Posted: Sat Sep 13, 2025 11:34 pm
by Engemil
SOLUTION!

I accidentally came over how to fix it, after trying to add more functions :lol:

Solution is after calling dmaStreamDisable(), you must always setup the mode again with dmaStreamSetMode().