v5.5.2 RMT模块分段调用莫名报错
Posted: Thu May 14, 2026 4:11 am
背景是使用 rmt 解析ir nec协议, 参考 ir_nec_transceiver 案列,但需要根据时段获取数据,预期非使能时段静默不处理
---
全周期使能代码如下
时段流程: 时段开始调用start函数,期间轮训调用get_data函数,时段结束调用stop函数,上述为一个时段的调用逻辑
表现: 第一个时段工作正常,第二个时段调start就会报 E (4450559) rmt: rmt_receive(401): channel not in enable state 错误
---
根据时段始末使能rmt
调用流程不变,表现是第一次正常,第二个时段调用start报错 E (15369) rmt: rmt_rx_enable(495): channel not in init state
===
不清楚是否与在主线程中调用有关,main中while(1) 是稳定有效,上述时段调用是在异步task中调用
---
全周期使能代码如下
Code: Select all
/*
* @brief NEC解码:校验脉冲范围
*/
static inline bool nec_check_in_range(uint32_t signal_duration, uint32_t spec_duration) {
return (signal_duration < (spec_duration + NEC_DECODE_MARGIN)) &&
(signal_duration > (spec_duration - NEC_DECODE_MARGIN));
}
/*
* @brief NEC协议解码类构造函数
*/
IRNecDecoder::IRNecDecoder(gpio_num_t rx_pin) : _rx_pin(rx_pin), _rmt_chan(NULL), _rx_queue(NULL) {
// 初始化默认数据
}
/*
* @brief NEC初始化资源
*/
bool IRNecDecoder::init() {
// 创建接收队列
_rx_queue = xQueueCreate(2, sizeof(rmt_rx_done_event_data_t));
if (_rx_queue == NULL) {
ESP_LOGE(_TAG, "队列创建失败");
return false;
}
// 配置RMT接收通道
rmt_rx_channel_config_t rx_cfg = {.gpio_num = _rx_pin,
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = IR_RESOLUTION_HZ,
.mem_block_symbols = 64,
.intr_priority = 0,
.flags = {.invert_in = 0, .with_dma = 0, .io_loop_back = 0, .allow_pd = 0}};
if (rmt_new_rx_channel(&rx_cfg, &_rmt_chan) != ESP_OK) {
ESP_LOGE(_TAG, "RMT通道创建失败");
return false;
}
// 注册中断回调(传递当前类实例指针)
rmt_rx_event_callbacks_t cbs = {.on_recv_done = rx_done_callback};
if (rmt_rx_register_event_callbacks(_rmt_chan, &cbs, this) != ESP_OK) {
ESP_LOGE(_TAG, "RMT通道注册中断失败");
return false;
}
if (rmt_enable(_rmt_chan) != ESP_OK) {
ESP_LOGE(_TAG, "RMT通道使能失败");
return false;
}
_receiving = false;
ESP_LOGI(_TAG, "NEC接收初始化完成 (GPIO: %d)", _rx_pin);
return true;
}
/*
* @brief NEC释放资源
*/
void IRNecDecoder::deinit() {
// 停止接收
stop();
// 删除RMT通道
if (_rmt_chan != NULL) {
rmt_del_channel(_rmt_chan);
_rmt_chan = NULL;
}
// 删除队列
if (_rx_queue != NULL) {
vQueueDelete(_rx_queue);
_rx_queue = NULL;
}
}
/*
* @brief NEC启动接收
*/
void IRNecDecoder::start() {
xQueueReset(_rx_queue);
rmt_receive(_rmt_chan, _rx_buf, sizeof(_rx_buf), &_recv_cfg);
_receiving = true;
ESP_LOGD(_TAG, "接收已开启");
}
/*
* @brief NEC停止接收
*/
void IRNecDecoder::stop() {
if (!_receiving) {
return;
}
xQueueReset(_rx_queue);
// 清空新数据标志
_receiving = false;
ESP_LOGD(_TAG, "接收已关闭");
}
/*
* @brief 中断回调函数(静态)
*/
bool IRAM_ATTR IRNecDecoder::rx_done_callback(rmt_channel_handle_t chan, const rmt_rx_done_event_data_t *edata,
void *ctx) {
IRNecDecoder *instance = (IRNecDecoder *)ctx;
BaseType_t need_wake = pdFALSE;
// 将接收数据推入队列
xQueueSendFromISR(instance->_rx_queue, edata, &need_wake);
return need_wake;
}
/*
* @brief 解码NEC协议数据
*/
bool IRNecDecoder::decode_nec(rmt_symbol_word_t *symbols, size_t symbol_num) {
if (symbol_num != NEC_STD_SYMBOLS_LEN) { // 标准NEC帧=34个脉冲
return false;
}
return true;
}
// 外部10ms轮询调用:获取解码数据
nec_result IRNecDecoder::get_data() {
rmt_rx_done_event_data_t rx_data;
// 读取队列数据
if (xQueueReceive(_rx_queue, &rx_data, 0) == pdTRUE) {
decode_nec(rx_data.received_symbols, rx_data.num_symbols);
if (_receiving) {
rmt_receive(_rmt_chan, _rx_buf, sizeof(_rx_buf), &_recv_cfg);
}
}
return _nec_result;
}时段流程: 时段开始调用start函数,期间轮训调用get_data函数,时段结束调用stop函数,上述为一个时段的调用逻辑
表现: 第一个时段工作正常,第二个时段调start就会报 E (4450559) rmt: rmt_receive(401): channel not in enable state 错误
---
根据时段始末使能rmt
Code: Select all
/*
* @brief NEC初始化资源
*/
bool IRNecDecoder::init() {
// 创建接收队列
_rx_queue = xQueueCreate(2, sizeof(rmt_rx_done_event_data_t));
if (_rx_queue == NULL) {
ESP_LOGE(_TAG, "队列创建失败");
return false;
}
// 配置RMT接收通道
rmt_rx_channel_config_t rx_cfg = {.gpio_num = _rx_pin,
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = IR_RESOLUTION_HZ,
.mem_block_symbols = 64,
.intr_priority = 0,
.flags = {.invert_in = 0, .with_dma = 0, .io_loop_back = 0, .allow_pd = 0}};
if (rmt_new_rx_channel(&rx_cfg, &_rmt_chan) != ESP_OK) {
ESP_LOGE(_TAG, "RMT通道创建失败");
return false;
}
// 注册中断回调(传递当前类实例指针)
rmt_rx_event_callbacks_t cbs = {.on_recv_done = rx_done_callback};
if (rmt_rx_register_event_callbacks(_rmt_chan, &cbs, this) != ESP_OK) {
ESP_LOGE(_TAG, "RMT通道注册中断失败");
return false;
}
_receiving = false;
ESP_LOGI(_TAG, "NEC接收初始化完成 (GPIO: %d)", _rx_pin);
return true;
}
/*
* @brief NEC释放资源
*/
void IRNecDecoder::deinit() {
// 停止接收
stop();
// 删除RMT通道
if (_rmt_chan != NULL) {
rmt_del_channel(_rmt_chan);
_rmt_chan = NULL;
}
// 删除队列
if (_rx_queue != NULL) {
vQueueDelete(_rx_queue);
_rx_queue = NULL;
}
}
/*
* @brief NEC启动接收
*/
void IRNecDecoder::start() {
xQueueReset(_rx_queue);
esp_err_t err = rmt_enable(_rmt_chan);
if (err != ESP_OK) {
ESP_LOGE(_TAG, "rmt_enable failed: %d, %s", err, esp_err_to_name(err));
}
rmt_receive(_rmt_chan, _rx_buf, sizeof(_rx_buf), &_recv_cfg);
_receiving = true;
ESP_LOGD(_TAG, "接收已开启");
}
/*
* @brief NEC停止接收
*/
void IRNecDecoder::stop() {
if (!_receiving) {
return;
}
xQueueReset(_rx_queue);
esp_err_t err = rmt_disable(_rmt_chan);
if (err != ESP_OK) {
ESP_LOGE(_TAG, "rmt_disable failed: %d, %s", err, esp_err_to_name(err));
}
_receiving = false;
ESP_LOGD(_TAG, "接收已关闭");
}===
不清楚是否与在主线程中调用有关,main中while(1) 是稳定有效,上述时段调用是在异步task中调用