在WLED项目中使用TWAI时WS28xx LED闪烁问题求助
问题描述
我尝试将CAN-bus命令接收功能与现有WLED源码集成,以驱动WS2812b LED灯带。项目硬件基于ESP32-WROOM-32芯片,搭配电平转换器、CAN收发器等,使用ESP32-TWAI-CAN v1.0.1库,用户模块代码如下:
#include "wled.h" #include <ESP32-TWAI-CAN.hpp> #define CAN_RX 18 #define CAN_TX 19 #define CAN_READ_TIMEOUT 0 class CanUsermod : public Usermod { setup() { [...] twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); twai_general_config_t g_config = {.mode = TWAI_MODE_LISTEN_ONLY, .tx_io = (gpio_num_t) CAN_TX, .rx_io = (gpio_num_t) CAN_RX, .clkout_io = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED, .tx_queue_len = 5, .rx_queue_len = 5, .alerts_enabled = TWAI_ALERT_NONE, .clkout_divider = 0, .intr_flags = ESP_INTR_FLAG_LEVEL1}; twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS(); ESP32Can.begin(ESP32Can.convertSpeed(250), CAN_TX, CAN_RX, 5, 5, &f_config, &g_config, &t_config); } [...] void loop() { while(ESP32Can.readFrame(rxFrame, CAN_READ_TIMEOUT)) [...] } }
单独运行WLED或CAN总线均正常,能正确接收CAN消息,但同时运行时(有CAN总线流量且点亮WS28xx LED),LED会闪烁,即使选择Solid效果也会出现该问题:
- 断开CAN总线,闪烁停止
- 注释
ESP32Can.begin([...])代码,闪烁停止
已排查但无效的操作:
- 修改RX、TX队列长度
- 设置CAN过滤器为全拒绝:
{.acceptance_code = 0, .acceptance_mask = 0x00000000, .single_filter = true} - 在g_config中设置
.intr_flags=ESP_INTR_FLAG_LEVEL3 - 修改
CAN_READ_TIMEOUT为0、10、100 - 注释loop()中的while循环
- 验证控制器板数据输出通道无振铃
- 验证数据输出通道逻辑电平正确
- 验证LED灯带供电电压正确
- 将LED数据输出从GPIO16改为GPIO4
因此我怀疑TWAI接口的ISR导致LED的800kHz位模式生成出现抖动,请问是否需要在LED位生成期间暂停中断?若需要,应如何实现?
解决方案
你的怀疑是正确的:WS2812B的800kHz时序要求极高(误差不能超过±150ns),TWAI的中断会打断LED数据发送的精确时序,导致灯带识别错误从而出现闪烁。
实现思路
WLED内部通过RMT外设发送LED数据,你需要在RMT发送LED帧的前后分别禁用/启用TWAI中断,避免时序被打断。
具体代码修改
- 在CAN用户模块中添加控制TWAI中断的函数:
// 禁用TWAI中断 void disableTwaiInterrupts() { esp_intr_disable(ESP32Can.getTwaiHandle()); } // 启用TWAI中断 void enableTwaiInterrupts() { esp_intr_enable(ESP32Can.getTwaiHandle()); }
- Hook WLED的LED发送回调:
WLED提供了onBeforeRender()和onAfterRender()钩子函数,你可以在用户模块中重写这两个方法,分别在发送LED数据前后控制TWAI中断:
class CanUsermod : public Usermod { // ... 现有代码 ... void onBeforeRender() override { disableTwaiInterrupts(); } void onAfterRender() override { enableTwaiInterrupts(); } // ... 现有代码 ... };
- 备选方案:调整TWAI中断优先级
如果禁用中断影响CAN接收的实时性,你可以尝试将TWAI的中断优先级设置为低于RMT外设的中断优先级(ESP32中RMT默认优先级较高)。修改g_config中的intr_flags:
twai_general_config_t g_config = { // ... 现有配置 ... .intr_flags = ESP_INTR_FLAG_LEVEL2 // 设置为比RMT默认优先级低的等级 };
不过这个方案的可靠性不如直接禁用中断,因为即使优先级低,高负载下仍可能出现抢占。
注意事项
- 禁用TWAI中断期间,CAN总线的消息会暂时存在TWAI的硬件FIFO中(TWAI控制器有32个消息的硬件FIFO),只要CAN流量不是持续满负荷,不会丢失消息。
- 如果你的CAN流量极大,超过硬件FIFO容量,可以考虑在禁用中断前先读取所有队列中的消息,减少丢失风险。
内容的提问来源于stack exchange,提问作者mach
相关产品推荐
相关产品推荐

