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=============================================================
记录下排查过程:
-
通过屏蔽大法先排查问题所在
- 以下就是排查过程,各种情况似是而非完全无法锁定问题所在
-
- 以下就是排查过程,各种情况似是而非完全无法锁定问题所在
- 计算函数运行时间
- 通过跟同事分享排查结果,他提示,可能是RGB灯刷新的时候被打断导致的
- 于是就有了上面的那张时间表
我设定的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 }
本文来自博客园,作者:xjxcxjx,转载请注明原文链接:https://www.cnblogs.com/xjxcxjx/p/18923331,谢绝CSDN转载!

浙公网安备 33010602011771号