v5.5.2 RMT模块分段调用莫名报错

zasdx369
Posts: 1
Joined: Thu May 14, 2026 3:19 am

v5.5.2 RMT模块分段调用莫名报错

Postby zasdx369 » Thu May 14, 2026 4:11 am

背景是使用 rmt 解析ir nec协议, 参考 ir_nec_transceiver 案列,但需要根据时段获取数据,预期非使能时段静默不处理
---
全周期使能代码如下

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, "接收已关闭");
}
调用流程不变,表现是第一次正常,第二个时段调用start报错 E (15369) rmt: rmt_rx_enable(495): channel not in init state

===
不清楚是否与在主线程中调用有关,main中while(1) 是稳定有效,上述时段调用是在异步task中调用

Who is online

Users browsing this forum: Bing [Bot] and 2 guests