STM32 G431RB 利用PWM+DMA+Circular Mode 少量内存 实现对WS2812 灯带的控制

在上一篇文章中DMA的设置使用的是"Normal" Mode,这种实现方法的问题是费内存,要控制168颗灯带的颜色,需要准备168*24 +2*TRST 约4K Byte的内存,

在MCU的世界里就麻烦了,这次这个项目中需要控制4个灯带,两个168,一个21, 一个23.内存严重告急。采用DMA Circular模式能大大的减少内存的使用。

DMA Cirular模式就是DMA发送完成后,在调用HAL_TIM_PWM_Stop_DMA,DMA会自动的循环一直发送数据。

代码工作原理如下:

  1. 用两颗LED灯的长度(24x2)作为DMA Buffer
  2. DMA发送数据的时候会产生两个中断,一个是数据发送一半的时候产生一次中断,在中断里将下一个灯的数据填入Buffer的前半段
  3. DMA一次发送完成产生第二个中断,我们再将下一个灯的数据填入下半Buffer
  4. 循环往复直到所有的灯的数据都发送完成

当然前后都需要发送TRST数据。

 

 

我在例子代码中可以同时配置4个PWM+DMA通道,进行数据发送。感兴趣的可以直接用我的实现函数。

代码放到Github上了。https://github.com/magicduan/demo_pwm_dma

/**
* @brief Initilaize the pwm_dma data(Global array pwm_dma_data) according to dma_id.
* 
* @param dma_id: the PWM_DMA item (0 - PWM_LED_CHANNEL_MAX_COUNT-1)
* @param htim: pwm Timer
* @param channel: pwm DMA channel
* @param p_colors: the color buffer of LEDs, every LED color use 24bit (RGB)  = 3 Byte 
* @param leds_count: the numbers of LEDs
* @retval 0: success
*/
void pwm_dma_init(uint32_t dma_id, TIM_HandleTypeDef *htim, uint32_t channel,
                 uint8_t* p_colors, uint32_t leds_count )
{
    if (dma_id >= PWM_LED_CHANNEL_MAX_COUNT){
        return;
    }

    pwm_dma_data[dma_id].htim = htim;    
    pwm_dma_data[dma_id].dma_channel = channel;
    pwm_dma_data[dma_id].p_dma_colors = p_colors;    
    pwm_dma_data[dma_id].total_leds = leds_count;
}                 

/**
* @brief Send colors to LEDs by PWM + DMA + Circular mode
* 
* @param dma_id: the PWM_DMA item (0 - PWM_LED_CHANNEL_MAX_COUNT-1)
* @param channel: pwm DMA channel
* @param p_colors: the color buffer of LEDs, every LED color use 24bit (RGB)  = 3 Byte 
* @param leds_count: the numbers of LEDs
* @param b_block: flag whether waiting complete for DMA send. b_block = 1, waiting block mode , = 0 noblock mode
* @retval 0: success
*/
int pwm_dma_send(uint32_t dma_id,uint8_t b_block)
{
    int res = 0;
    if (dma_id >= PWM_LED_CHANNEL_MAX_COUNT){
        return -1;
    }

    if(pwm_dma_data[dma_id].htim == NULL){
        return -1;
    }

    pwm_dma_data[dma_id].inter_dma_data.status = PWM_DMA_HEAD_RST;
    pwm_dma_data[dma_id].inter_dma_data.cur_led = 0;
    pwm_dma_data[dma_id].inter_dma_data.b_completed = 0;

    led_data_fill(pwm_dma_data+dma_id,0);
    res = HAL_TIM_PWM_Start_DMA(pwm_dma_data[dma_id].htim,
                                pwm_dma_data[dma_id].dma_channel,
                                (uint32_t*)(pwm_dma_data[dma_id].inter_dma_data.pwm_buffer),
                                DMA_BUFFER_LEN);
    if ( res != HAL_OK){
        return res;
    }

    if (b_block){ // Block Mode
        while(pwm_dma_data[dma_id].inter_dma_data.b_completed == 0){
            osDelay(1);
        }
    }

    return res;
}                

 

posted @ 2022-01-15 17:17  magicduan  阅读(1112)  评论(0编辑  收藏  举报