RGB闪烁问题(WS2811)

关联文章:https://www.cnblogs.com/xjxcxjx/p/15637859.html

WS2811使用的是上古代码,也就是我来公司时候那位硬件工程师从网上抄的代码。本着能跑的代码就不动的原则,从2019年到现在2025年,芯片从STM32F103换到STM32F450,再到GD32F450,GD32F470,WS2811的驱动代码就没动过。

现在的项目因为都统一上了系统,然后又增加了一些硬件需要驱动,WS2811就开时闪烁了。很没有规律,有时候好长时间不闪一下,有时候连续闪烁。

先把上古代码贴出来:(反正都是网上抄的,也不存在泄密)

  1 void WS2812_send( uint8_t (*color)[ 3 ], uint16_t len,uint8_t PWM_CHANNEL )
  2 {
  3     uint8_t  j;
  4 
  5     uint8_t led = 0;
  6 
  7     uint16_t memaddr= 0;
  8 
  9     uint16_t buffersize = 0;
 10 
 11     buffersize = ( len * 24 ) + 42; // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes
 12 
 13     if(buffersize>=RGB_BUFFER_LEN)
 14     {
 15         return;//数据超过buf长度
 16     }
 17     while ( len )
 18     {
 19         for ( j = 0; j < 8; j++ )  // GREEN data
 20         {
 21             LED_BYTE_Buffer[ memaddr++ ] = ((color[led][1]<<j) & 0x80)?RGB_LOGICAL_H:RGB_LOGICAL_L;
 22         }
 23         for ( j = 0; j < 8; j++ )// red data
 24         {
 25             LED_BYTE_Buffer[ memaddr++ ] = ((color[led][0]<<j) & 0x80)?RGB_LOGICAL_H:RGB_LOGICAL_L;
 26         }
 27         for ( j = 0; j < 8; j++ )  // BLUE data
 28         {
 29             LED_BYTE_Buffer[ memaddr++ ] = ((color[led][2]<<j) & 0x80)?RGB_LOGICAL_H:RGB_LOGICAL_L;
 30         }
 31         led++;
 32         len--;
 33     }
 34     /*
 35         RGB0 = PB1      /       T2_CH3
 36         RGB1 = PA5      /       T1_CH0
 37         RGB2 = PB5      /       T2_CH1        
 38         TWINKLE_LED_BEGIN = PB6 /T3_CH0
 39         RGB3 = PD13     /       T3_CH1  ASCD1
 40     */
 41     if(PWM_CHANNEL==0)  timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_3,0);
 42     if(PWM_CHANNEL==1)  timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_0,0);
 43     if(PWM_CHANNEL==2)  timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_1,0);
 44     if(PWM_CHANNEL==3)  timer_channel_output_pulse_value_config(TIMER3,TIMER_CH_0,0);
 45 
 46     while ( memaddr < buffersize )
 47     {
 48         LED_BYTE_Buffer[ memaddr++ ] = 0;
 49     }
 50     
 51     usart_interrupt_disable(USART0, USART_INT_TC);
 52     usart_interrupt_disable(USART0, USART_INT_RBNE); 
 53     spi_i2s_interrupt_disable(SPI1,SPI_I2S_INT_RBNE);
 54     
 55     vTaskSuspendAll();
 56     //__set_FAULTMASK (1);
 57     //    __IRQ_SAFE  //暂时关闭总中断    
 58     {    
 59         if(PWM_CHANNEL==0)
 60         {
 61             Tim2CH3_init_DMA(buffersize);
 62             //          dma_transfer_number_config(DMA0, DMA_CH2, buffersize);
 63             dma_channel_enable(DMA0, DMA_CH2);
 64             timer_enable(TIMER2);
 65             while ( !dma_flag_get( DMA0, DMA_CH2,DMA_INTF_FTFIF ) );   // wait until transfer complete
 66             dma_channel_disable(DMA0, DMA_CH2);
 67             dma_flag_clear( DMA0, DMA_CH2,DMA_INTF_FTFIF );
 68             timer_disable(TIMER2);
 69             
 70         }
 71         else if(PWM_CHANNEL==1) //TIM4CH1
 72         {
 73             //          Timer1CH0_init_DMA();
 74             dma_channel_enum dma_channel = DMA_CH7;
 75             dma_transfer_number_config(DMA0, dma_channel, buffersize);
 76             dma_channel_enable(DMA0, dma_channel);
 77             timer_enable((TIMER1));
 78             while ( !dma_flag_get( DMA0, dma_channel,DMA_INTF_FTFIF ) );   // wait until transfer complete
 79             dma_channel_disable(DMA0, dma_channel);
 80             dma_flag_clear( DMA0, dma_channel,DMA_INTF_FTFIF);
 81             timer_disable((TIMER1));
 82             
 83         }
 84         else if(PWM_CHANNEL==2) //TIM2_CH1
 85         {
 86             Tim2CH1_init_DMA(buffersize);
 87             //          dma_transfer_number_config(DMA0, DMA_CH2, buffersize);
 88             dma_channel_enable(DMA0, DMA_CH2);
 89             timer_enable(TIMER2);
 90             while ( !dma_flag_get( DMA0, DMA_CH2,DMA_INTF_FTFIF ) );   // wait until transfer complete
 91             dma_channel_disable(DMA0, DMA_CH2);
 92             dma_flag_clear( DMA0, DMA_CH2,DMA_INTF_FTFIF );
 93             timer_disable(TIMER2);
 94 
 95         }
 96         else if(PWM_CHANNEL==3) //TIM3_CH0
 97         {
 98             TIMER3CH0_init_DMA(buffersize);
 99             // dma_transfer_number_config(DMA0, DMA_CH6, buffersize);
100             dma_channel_enable(DMA0, DMA_CH6);
101             timer_enable(TIMER3);
102             while ( !dma_flag_get( DMA0, DMA_CH6,DMA_INTF_FTFIF ) );   // wait until transfer complete
103             dma_channel_disable(DMA0, DMA_CH6);
104             dma_flag_clear( DMA0, DMA_CH6,DMA_INTF_FTFIF );
105             timer_disable(TIMER3);        
106         }
107     }    
108     xTaskResumeAll();
109     //__set_FAULTMASK (0);
110 
111     spi_i2s_interrupt_enable(SPI1,SPI_I2S_INT_RBNE);
112     usart_interrupt_enable(USART0, USART_INT_TC);
113     usart_interrupt_enable(USART0, USART_INT_RBNE); 
114 }

这个项目刚启动的时候其实RGB灯就存在闪烁问题了,当时我的解决方案是添加了

__IRQ_SAFE

用来关闭总中断。但是这样做的后果就是会影响其他应用。

通过这次的排查也能发现,一次刷灯的时间最长7644us,这个时长是接受不了的

 这个时长问题其实这个项目二次启动的时候我就发现这个问题了,并且项目经理安排了同事去解决。

她当初说她通过DMA的方式解决了,但是她花了1个多月解决的方案并没有应用,我也没看到她的代码。。。。

===========================================================我是快乐的分割线=============================================================

===========================================================防火防盗防CSDN=============================================================

记录下排查过程:

  1. 通过屏蔽大法先排查问题所在

    1.   以下就是排查过程,各种情况似是而非完全无法锁定问题所在
  2. 计算函数运行时间
    1. 通过跟同事分享排查结果,他提示,可能是RGB灯刷新的时候被打断导致的
    2. 于是就有了上面的那张时间表

  我设定的FreeRTOS的时间片是1ms,这个函数一次运行需要7ms,铁定被打断。而且RGB刷新还是连续刷新,不能被打断的。也就无法通过分拆的方法

只能通过DMA的方式试试看有没有效果。结果是好的,DMA能完美解决。

  1 static void WS2812_send_internal(uint8_t (*color)[3], uint16_t len, uint8_t PWM_CHANNEL)
  2 {
  3     uint16_t buffersize = (len * 24) + 42;
  4     if(buffersize >= RGB_BUFFER_LEN) {
  5         return;
  6     }
  7     
  8     // 准备数据
  9     uint16_t memaddr = 0;
 10     for(uint16_t led = 0; led < len; led++) {
 11         // 准备RGB数据
 12         for(uint8_t j = 0; j < 8; j++) {
 13             LED_BYTE_Buffer[memaddr++] = ((color[led][1]<<j) & 0x80) ? RGB_LOGICAL_H : RGB_LOGICAL_L;
 14         }
 15         for(uint8_t j = 0; j < 8; j++) {
 16             LED_BYTE_Buffer[memaddr++] = ((color[led][0]<<j) & 0x80) ? RGB_LOGICAL_H : RGB_LOGICAL_L;
 17         }
 18         for(uint8_t j = 0; j < 8; j++) {
 19             LED_BYTE_Buffer[memaddr++] = ((color[led][2]<<j) & 0x80) ? RGB_LOGICAL_H : RGB_LOGICAL_L;
 20         }
 21     }
 22     
 23     // 填充剩余缓冲区
 24     while(memaddr < buffersize) {
 25         LED_BYTE_Buffer[memaddr++] = 0;
 26     }
 27     
 28     // 配置DMA
 29     switch(PWM_CHANNEL) {
 30         case 0:
 31             Tim2CH3_init_DMA(buffersize);
 32             dma_channel_enable(DMA0, DMA_CH2);
 33             timer_enable(TIMER2);
 34             break;
 35         case 1:
 36             dma_transfer_number_config(DMA0, DMA_CH7, buffersize);
 37             dma_channel_enable(DMA0, DMA_CH7);
 38             timer_enable(TIMER1);
 39             break;
 40         case 2:
 41             Tim2CH1_init_DMA(buffersize);
 42             dma_channel_enable(DMA0, DMA_CH2);
 43             timer_enable(TIMER2);
 44             break;
 45         case 3:
 46             dma_transfer_number_config(DMA0, DMA_CH6, buffersize);
 47             dma_channel_enable(DMA0, DMA_CH6);
 48             timer_enable(TIMER3);
 49             break;
 50     }
 51 }
 52 void WS2812_send(uint8_t (*color)[3], uint16_t len, uint8_t PWM_CHANNEL)
 53 {
 54     WS2812_send_internal(color, len, PWM_CHANNEL);
 55 }
 56 
 57 void rgb_dma_complete_callback(uint8_t channel)
 58 {
 59     if(channel >= RGB_CHANNEL_NUM) {
 60         return;
 61     }
 62     
 63     switch(channel) {
 64         case 0:
 65             dma_channel_disable(DMA0, DMA_CH2);
 66             timer_disable(TIMER2);
 67             break;
 68         case 1:
 69             dma_channel_disable(DMA0, DMA_CH7);
 70             timer_disable(TIMER1);
 71             break;
 72         case 2:
 73             dma_channel_disable(DMA0, DMA_CH2);
 74             timer_disable(TIMER2);
 75             break;
 76         case 3:
 77             dma_channel_disable(DMA0, DMA_CH6);
 78             timer_disable(TIMER3);
 79             break;
 80     }
 81     rgb_state.is_refreshing = false;
 82 }
 83 
 84 // DMA中断处理函数
 85 void DMA0_Channel2_IRQHandler(void)
 86 {
 87     if(dma_flag_get(DMA0, DMA_CH2, DMA_INTF_FTFIF)) {
 88         dma_flag_clear(DMA0, DMA_CH2, DMA_INTF_FTFIF);
 89         // 由于DMA2被通道0和2共享,我们需要知道当前是哪个通道在使用
 90         // 这里我们假设通道0和2是交替使用的
 91         static uint8_t last_channel = 1;  // 初始值设为1,这样第一次会使用通道0
 92         if(last_channel == 1) {
 93             rgb_dma_complete_callback(0);
 94             last_channel = 0;
 95         } else {
 96             rgb_dma_complete_callback(2);
 97             last_channel = 1;
 98         }
 99     }
100 }
101 
102 void DMA0_Channel6_IRQHandler(void)
103 {
104     if(dma_flag_get(DMA0, DMA_CH6, DMA_INTF_FTFIF)) {
105         dma_flag_clear(DMA0, DMA_CH6, DMA_INTF_FTFIF);
106         rgb_dma_complete_callback(3);
107     }
108 }
109 
110 void DMA0_Channel7_IRQHandler(void)
111 {
112     if(dma_flag_get(DMA0, DMA_CH7, DMA_INTF_FTFIF)) {
113         dma_flag_clear(DMA0, DMA_CH7, DMA_INTF_FTFIF);
114         rgb_dma_complete_callback(1);
115     }
116 }
117 void Tim2CH3_init_DMA(UINT32 number)
118 {
119     dma_single_data_parameter_struct dma_data_parameter;
120     
121 ////RGB2-Timer2_Ch0--DMA0_ch4_stream5
122     dma_deinit(DMA0,DMA_CH2);
123     
124     /* initialize DMA single data mode */
125     dma_data_parameter.periph_addr    = (uint32_t)TIMER2_CCR3_Address;  //DMA 目标地址:TIM2通道3的输出比较
126     dma_data_parameter.periph_inc    = DMA_PERIPH_INCREASE_DISABLE;    //一个周期内地址是否自增
127     dma_data_parameter.memory0_addr = (uint32_t)LED_BYTE_Buffer;      //DMA 源地址
128     dma_data_parameter.memory_inc    = DMA_MEMORY_INCREASE_ENABLE;     //一个周期内地址是否自增
129     dma_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;  //设置DMA传输的位数
130     dma_data_parameter.direction    = DMA_MEMORY_TO_PERIPH;           //DMA由内存向外设写入数据
131     dma_data_parameter.number        = number;                         //DMA每次传输的数据长度
132     dma_data_parameter.priority     = DMA_PRIORITY_HIGH;              //设置DMA的优先级
133     
134     dma_single_data_mode_init(DMA0, DMA_CH2, &dma_data_parameter);    //初始化DMA0的通道2
135     dma_channel_subperipheral_select(DMA0, DMA_CH2, DMA_SUBPERI5);    //选择subperipheral
136     dma_circulation_disable(DMA0, DMA_CH2);
137     timer_dma_enable(TIMER2,TIMER_DMA_UPD);
138     dma_channel_disable(DMA0, DMA_CH2);
139 
140     dma_interrupt_enable(DMA0, DMA_CH2, DMA_CHXCTL_FTFIE);
141     nvic_irq_enable(DMA0_Channel2_IRQn, TIMER2_IRQ_LEVEL, 0);
142 }
143 
144 void Tim2CH1_init_DMA(UINT32 number)
145 {
146     dma_single_data_parameter_struct dma_data_parameter;
147         
148     dma_deinit(DMA0,DMA_CH2);
149     
150     /* initialize DMA single data mode */
151     dma_data_parameter.periph_addr    = (uint32_t)TIMER2_CCR1_Address;
152     dma_data_parameter.periph_inc    = DMA_PERIPH_INCREASE_DISABLE;
153     dma_data_parameter.memory0_addr = (uint32_t)LED_BYTE_Buffer;
154     dma_data_parameter.memory_inc    = DMA_MEMORY_INCREASE_ENABLE;
155     dma_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
156     dma_data_parameter.direction    = DMA_MEMORY_TO_PERIPH;
157     dma_data_parameter.number        = number;
158     dma_data_parameter.priority     = DMA_PRIORITY_HIGH; 
159     
160     dma_single_data_mode_init(DMA0, DMA_CH2, &dma_data_parameter);
161     dma_channel_subperipheral_select(DMA0, DMA_CH2, DMA_SUBPERI5);
162     dma_circulation_disable(DMA0, DMA_CH2);
163     timer_dma_enable(TIMER2,TIMER_DMA_UPD);
164     dma_channel_disable(DMA0, DMA_CH2);
165 
166     dma_interrupt_enable(DMA0, DMA_CH2, DMA_CHXCTL_FTFIE);
167     nvic_irq_enable(DMA0_Channel2_IRQn, TIMER2_IRQ_LEVEL, 0);
168 }

 

posted @ 2025-06-11 10:09  xjxcxjx  阅读(80)  评论(0)    收藏  举报