Closed murarduino closed 2 weeks ago
只有靜態方法可以存入C的函數指針
所以需要一個額外的參數存this
void* user_ctx
通常可以用來達到此目的
class DCCDetector {
static bool IRAM_ATTR DCCDetector::_capture_callback(mcpwm_cap_channel_handle_t _cap_channel_handle, const mcpwm_capture_event_data_t *edata, void *user_data);
...
TaskHandle_t m_task = nullptr;
};
bool DCCDetector::_capture_callback(mcpwm_cap_channel_handle_t _cap_channel_handle, const mcpwm_capture_event_data_t *edata, void *user_data) {
auto& self = *(DCCDetector*)user_data;
TaskHandle_t cur_task = self.m_task;
// do something
}
void DCCDetector::_detector_cap_init(int dcc_pin) {
// ...
// 註冊回調
mcpwm_capture_event_callbacks_t cap_cb = {
.on_cap = _capture_callback,
};
m_task = xTaskGetCurrentTaskHandle();
mcpwm_capture_channel_register_event_callbacks(_cap_channel_handle, &cap_cb, this};
// ...
}
只有靜態方法可以存入C的函數指針 所以需要一個額外的參數存this
void* user_ctx
通常可以用來達到此目的class DCCDetector { static bool IRAM_ATTR DCCDetector::_capture_callback(mcpwm_cap_channel_handle_t _cap_channel_handle, const mcpwm_capture_event_data_t *edata, void *user_data); ... TaskHandle_t m_task = nullptr; }; bool DCCDetector::_capture_callback(mcpwm_cap_channel_handle_t _cap_channel_handle, const mcpwm_capture_event_data_t *edata, void *user_data) { auto& self = *(DCCDetector*)user_data; TaskHandle_t cur_task = self.m_task; // do something } void DCCDetector::_detector_cap_init(int dcc_pin) { // ... // 註冊回調 mcpwm_capture_event_callbacks_t cap_cb = { .on_cap = _capture_callback, }; m_task = xTaskGetCurrentTaskHandle(); mcpwm_capture_channel_register_event_callbacks(_cap_channel_handle, &cap_cb, this}; // ... }
我在慢慢理解你给出建议。
我的改动如下:
在DCCDetector.h将回调函数定义为一个静态成员(是否回调函数必需是静态成员,我还不是很理解)
static bool IRAM_ATTR _capture_callback(mcpwm_cap_channel_handle_t _cap_channel_handle, const mcpwm_capture_event_data_t *edata, void *user_data);
并在类中定义回调函数的句柄
TaskHandle_t cb_task= nullptr;
在回调函数内将user_data进行地址转换
auto &self = *(DCCDetector *)user_data;
但是为什么需要将成员变量(回调函数句柄)赋值给另任务钉句柄cur_task,我不是很理解。因为cur_task没有被其它部分调用。
TaskHandle_t cur_task = self.cb_task;
正巧在回调函数中还需要调用对象函数。所以
cap_begin_analog_value = self.analog_read();
可以这样应用。对么?
void DCCDetector::_detector_cap_init(int dcc_pin) { ... mcpwm_capture_event_callbacks_t cap_cb = { .on_cap = _capture_callback, }; cb_task = xTaskGetCurrentTaskHandle(); mcpwm_capture_channel_register_event_callbacks(_cap_channel_handle, &cap_cb, this); ... }
回调函数必需是静态成员,我还不是很理解
C++的官網有解釋成員函數指針和普通函數指針 及 靜態/非靜態成員函數的差異 https://isocpp.org/wiki/faq/pointers-to-members
GCC有提供轉換非靜態成員函數為普通函數指針的擴展,但此為non portable,clang等其他編譯器不支持,慎用之 https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html
C++23後,使用explicit object parameter的成員函數也可取出函數指針,不過須待ESP-IDF使用的GCC升級到14+才可使用。當前的GCC13尚未支持此特性。
并在类中定义回调函数的句柄 (恕刪)...
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); mcpwm_capture_channel_register_event_callbacks(_cap_channel_handle, &cap_cb, cur_task);//注册> > capture通道的事件回调
... 因为cur_task没有被其它部分调用。
因為看你的代碼有把任務句柄傳入回調
才增加新成員TaskHandle_t m_task;
作為傳參使用
如果用不到可以刪去 這非必要😅
在回调函数中还需要调用对象函数。所以
cap_begin_analog_value = self.analog_read();
可以这样应用。对么?
是的
此外,在github的issue頁面貼代碼時可以考慮使用此markdown語法啟用語法高亮
回调函数必需是静态成员,我还不是很理解
C++的官網有解釋成員函數指針和普通函數指針 及 靜態/非靜態成員函數的差異 https://isocpp.org/wiki/faq/pointers-to-members
GCC有提供轉換非靜態成員函數為普通函數指針的擴展,但此為non portable,clang等其他編譯器不支持,慎用之 https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html
C++23後,使用explicit object parameter的成員函數也可取出函數指針,不過須待ESP-IDF使用的GCC升級到14+才可使用。當前的GCC13尚未支持此特性。
并在类中定义回调函数的句柄 (恕刪)...
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); mcpwm_capture_channel_register_event_callbacks(_cap_channel_handle, &cap_cb, cur_task);//注册> > capture通道的事件回调
... 因为cur_task没有被其它部分调用。
因為看你的代碼有把任務句柄傳入回調 才增加新成員
TaskHandle_t m_task;
作為傳參使用 如果用不到可以刪去 這非必要😅在回调函数中还需要调用对象函数。所以
cap_begin_analog_value = self.analog_read();
可以这样应用。对么?是的
此外,在github的issue頁面貼代碼時可以考慮使用此markdown語法啟用語法高亮
谢谢您的指导。这个问题应该可以解决了。
Answers checklist.
General issue report
我需要将mcpwm的capture功能类化。所以我尝试编写组件:
DccDetetor.h
pragma once
include
include
include "sdkconfig.h"
include <driver/gpio.h>
include <driver/mcpwm_cap.h>
include <esp_adc/adc_oneshot.h>
const static char *TAG = "DCCDetector";
define CAP_RESOLUTION_HZ (110001000)
define DETECTOR_ADC_ATTEN ADC_ATTEN_DB_12
ifdef __cplusplus
extern "C" {
endif
class DCCDetector { private: int _dcc_pin; int analog_pin; uint16_t _analog_value; adc_oneshot_unit_handle_t _adc_handle = NULL; mcpwm_cap_timer_handle_t _cap_timer_handle= NULL; mcpwm_cap_channel_handle_t _cap_channel_handle=NULL; adc_unit_t _adc_unit = ADC_UNIT_1; adc_channel_t _adc1_channel; void _detector_cap_init(int dcc_pin); void _detector_analog_init(int analog_pin); bool IRAM_ATTR _capture_callback(mcpwm_cap_channel_handle_t _cap_channel_handle, const mcpwm_capture_event_data_t edata, void user_data);
public: DCCDetector(int dcc_pin,int analog_pin); ~DCCDetector(); uint16_t analog_read(); };
ifdef __cplusplus
}
endif
DccDetector.cpp:
include
include <freertos/FreeRTOS.h>
include <freertos/task.h>
include "DCCDetector.h"
DCCDetector::DCCDetector(int dcc_pin, int analog_pin) { _detector_cap_init(dcc_pin); _detector_analog_init(analog_pin); }
DCCDetector::~DCCDetector() { } void DCCDetector::_detector_cap_init(int dcc_pin) { //------- Install capture timer mcpwm_capture_timer_config_t cap_conf = { .group_id = 0, .clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT, .resolution_hz = CAP_RESOLUTION_HZ, };
}
void DCCDetector::_detector_analog_init(int analog_pin) { //-----Install oneshot adc adc_oneshot_io_to_channel(analog_pin, &_adc_unit, &_adc1_channel); adc_oneshot_unit_init_cfg_t adc_init_config = { .unit_id = _adc_unit, .clk_src = ADC_RTC_CLK_SRC_DEFAULT, .ulp_mode = ADC_ULP_MODE_DISABLE, }; adc_oneshot_new_unit(&adc_init_config, &_adc_handle); adc_oneshot_chan_cfg_t adc_config = { .atten = DETECTOR_ADC_ATTEN, .bitwidth = ADC_BITWIDTH_DEFAULT, }; ESP_ERROR_CHECK(adc_oneshot_config_channel(_adc_handle, _adc1_channel, &adc_config)); }
uint16_t DCCDetector::analog_read() { adc_oneshot_read(_adc_handle, _adc1_channel, (int *)&_analog_value); return _analog_value; }
bool DCCDetector::_capture_callback(mcpwm_cap_channel_handle_t _cap_channel_handle, const mcpwm_capture_event_data_t edata, void user_data) { static uint32_t cap_val_begin_of_sample = 0; static uint32_t cap_val_end_of_sample = 0; static uint16_t cap_begin_analog_value = 0; TaskHandle_t task_to_notify = (TaskHandle_t)user_data; BaseType_t high_task_wakeup = pdFALSE; // calculate the interval in the ISR, // so that the interval will be always correct even when capture_queue is not handled in time and overflow. if (edata->cap_edge == MCPWM_CAP_EDGE_POS) { // store the timestamp when pos edge is detected cap_val_begin_of_sample = edata->cap_value; cap_val_end_of_sample = cap_val_begin_of_sample; cap_begin_analog_value = analog_read();//?回调函数可以放在 } else { cap_val_end_of_sample = edata->cap_value; uint32_t tof_ticks = cap_val_end_of_sample - cap_val_begin_of_sample;
}
但在注册capture的Callback时,报如下错误: "bool (DCCDetector::)(mcpwm_cap_channel_handle_t _cap_channel_handle, const mcpwm_capture_event_data_t edata, void user_data)" 类型的值不能用于初始化 "mcpwm_capture_event_cb_t" (aka "bool ()(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_data_t edata, void user_ctx)") 类型的实体C/C++(144)
我该如何处理?