在上一篇文章中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會自動的循環一直發送數據。
代碼工作原理如下:
- 用兩顆LED燈的長度(24x2)作為DMA Buffer
- DMA發送數據的時候會產生兩個中斷,一個是數據發送一半的時候產生一次中斷,在中斷里將下一個燈的數據填入Buffer的前半段
- DMA一次發送完成產生第二個中斷,我們再將下一個燈的數據填入下半Buffer
- 循環往復直到所有的燈的數據都發送完成
當然前后都需要發送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; }