I got DMA with PWM working with TIM1_CH1, TIM16_CH1, and I guess all TIMx_CH1. However, I seem to have a problem with utilizing other TIM channels, such as TIM1_CH3. And here I'm not sure if there are steps missing to make it work?
ChibiOS version: Newest (Master branch, commit f3e9979a4896ad6c8cad402fc56543181472f279 from Github)
With the newest chconf.h, halconf.h, and mcuconf.h files.
I imagined it was only necessary to specify with this:
Code: Select all
// Set PA0 to alternate function for TIM16_CH1 (AF2)
palSetPadMode(GPIOA, GPIOA_PIN0, PAL_MODE_ALTERNATE(2));
pwmStart(&PWMD16, &pwm_cfg); // For TIM1 (PA0 for TIM16_CH1)
dma_stream = ... ;
dmaSetRequestSource(dma_stream, 44); // DMAMUX request 44 for TIM16_CH1 (See RM0490 Reference Manual, Table 49)
dmaStreamSetPeripheral(dma_stream, &TIM16->CCR1);
Here I am showing my PWM+DMA example for STM32C071RB to control a WS2812B LED light:
main.c, under main() I'm showing 3 different tests.
Code: Select all
#include "ch.h"
#include "hal.h"
#define PWM_HI (14)
#define PWM_LO (6)
#define BITS_PER_PIXEL (24)
#define PWM_BUFFER_SIZE (BITS_PER_PIXEL + 1U) // Include 1 extra bit
#define PWM_RESET_BUFFER_SIZE (40U) // 40 * 1.25us = 50us reset time
// Common DMA mode settings (with MINC)
#define DMA_MODE_1 ( \
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 */ \
)
// Common DMA mode settings (without MINC)
#define DMA_MODE_2 ( \
STM32_DMA_CR_DIR_M2P /* Memory to peripheral */ \
| 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 */ \
)
const uint8_t pwm_zero_buf = 0;
uint8_t pwm_buf[PWM_BUFFER_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++){
pwm_buf[i] = (g & (1 << i)) ? PWM_HI : PWM_LO;
pwm_buf[i + 8] = (r & (1 << i)) ? PWM_HI : PWM_LO;
pwm_buf[i + 16] = (b & (1 << i)) ? PWM_HI : PWM_LO;
}
// pwm_buf[24] remains 0
}
void led_reset(void){
while (!dma_ready) {
chThdSleepMilliseconds(1); // Wait for previous DMA to complete
}
dma_ready = false;
dmaStreamDisable(dma_stream);
dmaStreamSetMode(dma_stream, DMA_MODE_2); // No MINC for repeated zero
dmaStreamSetMemory0(dma_stream, &pwm_zero_buf);
//dmaStreamSetMemory0(dma_stream, (uint8_t*)PWM_ZERO);
dmaStreamSetTransactionSize(dma_stream, PWM_RESET_BUFFER_SIZE);
dmaStreamEnable(dma_stream);
}
void led_render(void) {
while (!dma_ready) {
chThdSleepMilliseconds(1); // Wait for previous DMA to complete
}
dma_ready = false;
dmaStreamDisable(dma_stream);
dmaStreamSetMode(dma_stream, DMA_MODE_1);
dmaStreamSetMemory0(dma_stream, pwm_buf);
dmaStreamSetTransactionSize(dma_stream, PWM_BUFFER_SIZE);
dmaStreamEnable(dma_stream);
}
int main(void) {
halInit();
chSysInit();
// TEST 1
//palSetPadMode(GPIOA, GPIOA_PIN0, PAL_MODE_ALTERNATE(2)); // WORKS (TIM16_CH1)
// TEST 2
//palSetPadMode(GPIOC, GPIOC_PIN8, PAL_MODE_ALTERNATE(2)); // WORKS (TIM1_CH1)
// TEST 3
palSetPadMode(GPIOC, GPIOC_PIN10, PAL_MODE_ALTERNATE(2)); // NOT WORKING
// Start PWM
// TEST 1
//pwmStart(&PWMD16, &pwm_cfg); // For TIM16 (PA0 for TIM16_CH1)
// TEST 2
pwmStart(&PWMD1, &pwm_cfg); // For TIM1 (PC8 for TIM1_CH1)
// TEST 3
//pwmStart(&PWMD1, &pwm_cfg); // For TIM1 (PC10 for TIM1_CH3)
// Setup DMA
dma_stream = dmaStreamAlloc(STM32_DMA_STREAM_ID(1U, 1U), 0, (stm32_dmaisr_t)dma_callback, NULL);
// TEST 1
//dmaSetRequestSource(dma_stream, 44); // DMAMUX request 44 for TIM16_CH1 (See RM0490 Reference Manual, Table 49)
//dmaStreamSetPeripheral(dma_stream, &TIM16->CCR1);
// TEST 21
dmaSetRequestSource(dma_stream, 20); // DMAMUX request 20 for TIM1_CH1 (See RM0490 Reference Manual, Table 49)
dmaStreamSetPeripheral(dma_stream, &TIM1->CCR1);
// TEST 3
//dmaSetRequestSource(dma_stream, 22); // DMAMUX request 22 for TIM1_CH3 (See RM0490 Reference Manual, Table 49)
//dmaStreamSetPeripheral(dma_stream, &TIM1->CCR3);
//dmaStreamSetMode(dma_stream, DMA_MODE_1); // Not needed here
while (true) {
led_set_RGB(0xFF, 0x00, 0x00);
led_reset();
led_render();
chThdSleepMilliseconds(500);
led_set_RGB(0x00, 0xFF, 0x00);
led_reset();
led_render();
chThdSleepMilliseconds(500);
led_set_RGB(0x00, 0x00, 0xFF);
led_reset();
led_render();
chThdSleepMilliseconds(500);
}
return 0;
}
halconf.h, specific changes:
Code: Select all
...
#define HAL_USE_PWM TRUE
...
mcuconf.h, specific changes:
Code: Select all
...
#define STM32_PWM_USE_TIM1 TRUE // For Test 2 and 3
//#define STM32_PWM_USE_TIM16 TRUE // For test 1
...
/*
* DMA driver system settings.
*/
#define STM32_DMA_REQUIRED TRUE // Added for DMA support
...
