CH32V003 Timer2 DMA控制WS2812异常:仅输出16us单脉冲求助
CH32V003基于DMA控制WS2812仅输出单脉冲问题排查
我基于ch32v003fun框架实现WS2812 LED的DMA控制,思路是把高电平时长值存入24字节数组,用于定时器比较寄存器。按逻辑,定时器每次达到ATRLR值时触发DMA更新请求,DMA控制器应该递增地址并把内存值写入外设地址,但每次调用start_output()时,仅能得到一个16us的单脉冲,求帮忙排查问题。
相关代码
main.c
#include <ch32v003fun.h> #include <sys_tick.h> #include <soft_uart.h> #include <status_led.h> #include <stdbool.h> uint8_t green = 0; uint8_t blue = 0; int main(void){ SystemInit(); funGpioInitAll(); soft_uart_init(SOFT_UART_TX); Delay_Ms(100); init_status_led(PC1); set_color(255, 255, 0); soft_uart_tx_str("Hello world\n"); bool flag = false; uint8_t red = 0; uint32_t next_led_update_time_ticks = SysTick->CNT + Ticks_from_Ms(1000); while(1){ if(SysTick->CNT >= next_led_update_time_ticks){ red = flag ? 255 : 0; flag = flag ^ 1; set_color(red, green, blue); next_led_update_time_ticks = SysTick->CNT + Ticks_from_Ms(1000); } }; }
status_led.c
#include "status_led.h" #include <ch32v003fun.h> #include <sys_tick.h> #include <stdio.h> #include <stdbool.h> #include <soft_uart.h> #define BITS_CNT 24 #define BH 42 #define BL 15 static uint8_t pixel_bits[BITS_CNT] = { BL, BL, BL, BL, BL, BL, BL, BH, BL, BL, BL, BL, BL, BL, BL, BH, BL, BL, BL, BL, BL, BL, BL, BH }; uint8_t bit_index; uint32_t color; void start_output() { soft_uart_tx_str("start output\n"); bit_index = 0; // Reset bit index TIM2->CH1CVR = 0; // Clear Compare Register initially TIM2->SWEVGR |= TIM_UG; // Generate an update event to load new prescaler value // Force output low initially TIM2->CHCTLR1 &= ~TIM_OC1M; // Clear output compare mode bits for CH1 TIM2->CHCTLR1 |= TIM_ForcedAction_InActive; // Force output inactive // Configure DMA for pixel_bits transfer DMA1_Channel2->CFGR &= ~DMA_CFG5_EN; // Disable DMA channel before configuration DMA1_Channel2->PADDR = (uint32_t)&TIM2->CH1CVR; // Peripheral address (16-bit register) DMA1_Channel2->MADDR = (uint32_t)pixel_bits; // Memory address DMA1_Channel2->CNTR = BITS_CNT; // Number of data items to transfer DMA1_Channel2->CFGR = DMA_CFG5_MINC | // Enable memory increment DMA_CFG5_DIR | // Memory to Peripheral DMA_CFG5_PSIZE_0 | // Peripheral size 8 bits DMA_CFG5_MSIZE_0 | // Memory size 8 bits DMA_Priority_High; // High priority for DMA DMA1_Channel2->CFGR |= DMA_CFG5_EN; // Enable DMA channel // Enable Update DMA request TIM2->DMAINTENR |= TIM_UDE; // Start the timer TIM2->CTLR1 |= TIM_CEN; // Enable Timer2 __NOP(); // Small delay to ensure low state is applied // Set PWM Mode 1 TIM2->CHCTLR1 &= ~TIM_OC1M; // Clear output compare mode bits for CH1 TIM2->CHCTLR1 |= TIM_OCMode_PWM1; // Set PWM Mode 1 TIM2->CHCTLR1 |= TIM_OC1PE; // Enable Preload for CH1 TIM2->CCER |= TIM_CC1E; // Enable Output for CH1 soft_uart_tx_str("start ouptut end\n"); } void stop_output() { soft_uart_tx_str("stop output\n"); TIM2->DMAINTENR &= ~(TIM_UDE); // Disable Update DMA request TIM2->CCER &= ~TIM_CC1E; // Disable Output for CH1 to force low TIM2->CTLR1 &= ~(TIM_CEN); // Disable Timer2 DMA1_Channel2->CFGR &= ~DMA_CFG5_EN; // Disable DMA channel soft_uart_tx_str("stop ouptut end\n"); } void init_status_led(uint8_t pin) { soft_uart_tx_str("init led start\n"); funPinMode(pin, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF); // Configure pin as Alternate Function __asm volatile("csrw 0x7C0, %0" : : "r"(1<<13)); AFIO->PCFR1 |= AFIO_PCFR1_TIM2_REMAP_FULLREMAP; // Set full remap mode RCC->APB1PCENR |= RCC_APB1Periph_TIM2; // Enable timer clock RCC->AHBPCENR |= RCC_AHBPeriph_DMA1; // Enable DMA clock TIM2->PSC = 0; // Set prescaler (internal ticks per one timer tick) TIM2->ATRLR = 62; // Max timer ticks to overflow soft_uart_tx_str("init led end\n"); } void set_color(uint8_t red, uint8_t green, uint8_t blue) { for (uint8_t color_index = 0; color_index < 3; color_index++) { uint8_t _color = 0; switch (color_index) { case 0: _color = blue; break; case 1: _color = red; break; case 2: _color = green; break; default: break; } for (uint8_t i = 0; i < 8; i++) { pixel_bits[color_index * 8 + i] = (_color & (1 << (7 - i))) ? BH : BL; } } start_output(); // Start DMA-based output }
排查要点
- DMA传输完成后未终止外设运行:当前代码未处理DMA传输完成的事件,24次传输结束后DMA停止工作,但定时器仍在运行,此时
CH1CVR不再更新,会一直输出最后一次的电平。需在DMA传输完成后调用stop_output(),可通过轮询DMA完成标志或开启DMA中断实现。 - 外设与内存数据宽度不匹配:
TIM2->CH1CVR是16位寄存器,但配置DMA外设宽度为8位(DMA_CFG5_PSIZE_0),写入8位数据会导致寄存器高8位残留旧值,干扰后续脉冲输出。应将DMA外设宽度设为16位(DMA_CFG5_PSIZE_1),同时把pixel_bits数组改为uint16_t类型,保证数据宽度一致。 - 定时器与DMA启动顺序错误:现有代码先启动定时器再配置PWM模式,可能导致第一个脉冲后,后续DMA请求无法正确触发。建议先配置好PWM模式和预加载,再启动定时器与DMA。
- 未确认定时器更新事件允许状态:需确保
TIM2->CTLR1中的UDIS位为0(允许更新事件),可显式添加TIM2->CTLR1 &= ~TIM_UDIS;语句。
内容的提问来源于stack exchange,提问作者Bravo13
相关产品推荐
相关产品推荐

