You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:55:54