MCPWM/capture: API workflow optimization

deprecate isr_register to introduce new callback based API, with framework managed interrupt routine
new HC-SR04 example
pull/7386/head
SalimTerryLi 2021-07-13 20:13:17 +08:00
rodzic b4e8d7f57a
commit e5ddc12a08
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: F05CCEF2191AF770
12 zmienionych plików z 481 dodań i 173 usunięć

Wyświetl plik

@ -152,6 +152,16 @@ typedef enum {
MCPWM_BOTH_EDGE = BIT(1) | BIT(0), /*!<Capture both edges*/
} mcpwm_capture_on_edge_t;
/**
* @brief Interrupt masks for MCPWM capture
*/
__attribute__ ((deprecated("please use callback function to avoid directly accessing registers")))
typedef enum {
MCPWM_LL_INTR_CAP0 = BIT(27), ///< Capture 0 happened
MCPWM_LL_INTR_CAP1 = BIT(28), ///< Capture 1 happened
MCPWM_LL_INTR_CAP2 = BIT(29), ///< Capture 2 happened
} mcpwm_intr_t;
/**
* @brief Select type of MCPWM counter
*/
@ -223,6 +233,35 @@ typedef enum {
MCPWM_SELECT_CAP2, /*!<Select CAP2 as input*/
} mcpwm_capture_signal_t;
/**
* @brief MCPWM capture channel ID alias
*/
typedef mcpwm_capture_signal_t mcpwm_capture_channel_id_t;
/**
* @brief event data that will be passed into ISR callback
*/
typedef struct {
mcpwm_capture_on_edge_t cap_edge; /*!<Which signal edge is detected*/
uint32_t cap_value; /*!<Corresponding timestamp when event occurs. Clock rate = APB(usually 80M)*/
} cap_event_data_t;
/**
* @brief Type of capture event callback
* @param mcpwm MCPWM unit(0-1)
* @param cap_channel capture channel ID
* @param edata Capture event data, contains capture edge and capture value, fed by the driver
* @param user_data User registered data, passed from `mcpwm_capture_config_t`
*
* @note Since this an ISR callback so do not do anything that may block and call APIs that is designed to be used within ISR(usually has '_ISR' postfix)
*
* @return Whether a task switch is needed after the callback function returns,
* this is usually due to the callback wakes up some high priority task.
*
*/
typedef bool (*cap_isr_cb_t)(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_channel, const cap_event_data_t *edata,
void *user_data);
/**
* @brief MCPWM config structure
*/
@ -245,6 +284,16 @@ typedef struct {
mcpwm_carrier_out_ivt_t carrier_ivt_mode; /*!<Invert output of carrier*/
} mcpwm_carrier_config_t;
/**
* @brief MCPWM config capture structure
*/
typedef struct {
mcpwm_capture_on_edge_t cap_edge; /*!<Set capture edge*/
uint32_t cap_prescale; /*!<Prescale of capture signal, ranging from 1 to 256*/
cap_isr_cb_t capture_cb; /*!<User defined capture event callback, running under interrupt context */
void *user_data; /*!<User defined ISR callback function args*/
} mcpwm_capture_config_t;
/**
* @brief This function initializes each gpio signal for MCPWM
*
@ -673,12 +722,13 @@ esp_err_t mcpwm_fault_deinit(mcpwm_unit_t mcpwm_num, mcpwm_fault_signal_t fault_
* @param mcpwm_num set MCPWM unit(0-1)
* @param cap_edge set capture edge, BIT(0) - negative edge, BIT(1) - positive edge
* @param cap_sig capture pin, which needs to be enabled
* @param num_of_pulse Input capture signal prescaling, num_of_pulse ranges from 0 to 255, representing prescaling from 1 to 256.
* @param num_of_pulse Input capture signal prescaling, ranges from 0 to 255, representing prescaling from 1 to 256.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
__attribute__((deprecated("please use mcpwm_capture_enable_channel instead")))
esp_err_t mcpwm_capture_enable(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t cap_sig, mcpwm_capture_on_edge_t cap_edge,
uint32_t num_of_pulse);
@ -692,13 +742,39 @@ esp_err_t mcpwm_capture_enable(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t ca
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
__attribute__((deprecated("please use mcpwm_capture_disable_channel instead")))
esp_err_t mcpwm_capture_disable(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t cap_sig);
/**
* @brief Enable capture channel
*
* @param mcpwm_num set MCPWM unit(0-1)
* @param cap_channel capture channel, which needs to be enabled
* @param cap_conf capture channel configuration
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t mcpwm_capture_enable_channel(mcpwm_unit_t mcpwm_num, mcpwm_capture_channel_id_t cap_channel, const mcpwm_capture_config_t *cap_conf);
/**
* @brief Disable capture channel
*
* @param mcpwm_num set MCPWM unit(0-1)
* @param cap_channel capture channel, which needs to be disabled
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t mcpwm_capture_disable_channel(mcpwm_unit_t mcpwm_num, mcpwm_capture_channel_id_t cap_channel);
/**
* @brief Get capture value
*
* @param mcpwm_num set MCPWM unit(0-1)
* @param cap_sig capture pin on which value is to be measured
* @param cap_sig capture channel on which value is to be measured
*
* @return
* Captured value
@ -709,7 +785,7 @@ uint32_t mcpwm_capture_signal_get_value(mcpwm_unit_t mcpwm_num, mcpwm_capture_si
* @brief Get edge of capture signal
*
* @param mcpwm_num set MCPWM unit(0-1)
* @param cap_sig capture pin of whose edge is to be determined
* @param cap_sig capture channel of whose edge is to be determined
*
* @return
* Capture signal edge: 1 - positive edge, 2 - negtive edge
@ -759,8 +835,9 @@ esp_err_t mcpwm_sync_disable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num);
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Function pointer error.
*/
esp_err_t mcpwm_isr_register(mcpwm_unit_t mcpwm_num, void (*fn)(void *), void *arg, int intr_alloc_flags, intr_handle_t *handle);
__attribute__((deprecated("interrupt events are handled by driver, please use callback")))
esp_err_t mcpwm_isr_register(mcpwm_unit_t mcpwm_num, void (*fn)(void *), void *arg, int intr_alloc_flags,
intr_handle_t *handle);
#ifdef __cplusplus
}

Wyświetl plik

@ -34,6 +34,7 @@ static const char *TAG = "mcpwm";
#define MCPWM_GPIO_ERROR "MCPWM GPIO NUM ERROR"
#define MCPWM_GEN_ERROR "MCPWM GENERATOR ERROR"
#define MCPWM_DT_ERROR "MCPWM DEADTIME TYPE ERROR"
#define MCPWM_CAP_EXIST_ERROR "MCPWM USER CAP INT SERVICE ALREADY EXISTS"
#define MCPWM_GROUP_CLK_PRESCALE (16)
#define MCPWM_GROUP_CLK_HZ (SOC_MCPWM_BASE_CLK_HZ / MCPWM_GROUP_CLK_PRESCALE)
@ -61,27 +62,42 @@ _Static_assert(SOC_MCPWM_GENERATORS_PER_OPERATOR == 2, "This driver assumes the
ESP_RETURN_ON_FALSE((gen) < MCPWM_GEN_MAX, ESP_ERR_INVALID_ARG, TAG, MCPWM_GEN_ERROR); \
} while (0)
typedef struct {
cap_isr_cb_t fn; // isr function
void *args; // isr function args
} cap_isr_func_t;
typedef struct {
mcpwm_hal_context_t hal;
portMUX_TYPE spinlock;
_lock_t mutex_lock;
const int group_id;
int group_pre_scale; // starts from 1, not 0. will be subtracted by 1 in ll driver
int timer_pre_scale[SOC_MCPWM_TIMERS_PER_GROUP]; // same as above
intr_handle_t mcpwm_intr_handle; // handler for ISR register, one per MCPWM group
cap_isr_func_t cap_isr_func[SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER]; // handler for ISR callback, one for each cap ch
} mcpwm_context_t;
static mcpwm_context_t context[SOC_MCPWM_GROUPS] = {
[0] = {
.hal = {MCPWM_LL_GET_HW(0)},
.spinlock = portMUX_INITIALIZER_UNLOCKED,
.group_id = 0,
.group_pre_scale = SOC_MCPWM_BASE_CLK_HZ / MCPWM_GROUP_CLK_HZ,
.timer_pre_scale = {[0 ... SOC_MCPWM_TIMERS_PER_GROUP - 1] =
MCPWM_GROUP_CLK_HZ / MCPWM_TIMER_CLK_HZ},
.mcpwm_intr_handle = NULL,
.cap_isr_func = {[0 ... SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER - 1] = {NULL, NULL}},
},
[1] = {
.hal = {MCPWM_LL_GET_HW(1)},
.spinlock = portMUX_INITIALIZER_UNLOCKED,
.group_id = 1,
.group_pre_scale = SOC_MCPWM_BASE_CLK_HZ / MCPWM_GROUP_CLK_HZ,
.timer_pre_scale = {[0 ... SOC_MCPWM_TIMERS_PER_GROUP - 1] =
MCPWM_GROUP_CLK_HZ / MCPWM_TIMER_CLK_HZ},
.mcpwm_intr_handle = NULL,
.cap_isr_func = {[0 ... SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER - 1] = {NULL, NULL}},
}
};
@ -97,6 +113,14 @@ static inline void mcpwm_critical_exit(mcpwm_unit_t mcpwm_num)
portEXIT_CRITICAL(&context[mcpwm_num].spinlock);
}
static inline void mcpwm_mutex_lock(mcpwm_unit_t mcpwm_num){
_lock_acquire(&context[mcpwm_num].mutex_lock);
}
static inline void mcpwm_mutex_unlock(mcpwm_unit_t mcpwm_num){
_lock_release(&context[mcpwm_num].mutex_lock);
}
esp_err_t mcpwm_gpio_init(mcpwm_unit_t mcpwm_num, mcpwm_io_signals_t io_signal, int gpio_num)
{
if (gpio_num < 0) { // ignore on minus gpio number
@ -705,6 +729,30 @@ esp_err_t mcpwm_fault_set_oneshot_mode(mcpwm_unit_t mcpwm_num, mcpwm_timer_t tim
return ESP_OK;
}
static void mcpwm_default_isr_handler(void *arg) {
mcpwm_context_t *curr_context = (mcpwm_context_t *) arg;
uint32_t intr_status = mcpwm_ll_intr_get_capture_status(curr_context->hal.dev);
mcpwm_ll_intr_clear_capture_status(curr_context->hal.dev, intr_status);
bool need_yield = false;
for (int i = 0; i < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER; ++i) {
if ((intr_status >> i) & 0x1) {
if (curr_context->cap_isr_func[i].fn != NULL) {
cap_event_data_t edata;
edata.cap_edge = mcpwm_ll_capture_is_negedge(curr_context->hal.dev, i) ? MCPWM_NEG_EDGE
: MCPWM_POS_EDGE;
edata.cap_value = mcpwm_ll_capture_get_value(curr_context->hal.dev, i);
if (curr_context->cap_isr_func[i].fn(curr_context->group_id, i, &edata,
curr_context->cap_isr_func[i].args)) {
need_yield = true;
}
}
}
}
if (need_yield) {
portYIELD_FROM_ISR();
}
}
esp_err_t mcpwm_capture_enable(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t cap_sig, mcpwm_capture_on_edge_t cap_edge,
uint32_t num_of_pulse)
{
@ -746,6 +794,85 @@ esp_err_t mcpwm_capture_disable(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t c
return ESP_OK;
}
esp_err_t mcpwm_capture_enable_channel(mcpwm_unit_t mcpwm_num, mcpwm_capture_channel_id_t cap_channel, const mcpwm_capture_config_t *cap_conf)
{
ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
ESP_RETURN_ON_FALSE(cap_channel < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER, ESP_ERR_INVALID_ARG, TAG, MCPWM_CAPTURE_ERROR);
ESP_RETURN_ON_FALSE(context[mcpwm_num].cap_isr_func[cap_channel].fn == NULL, ESP_ERR_INVALID_STATE, TAG,
MCPWM_CAP_EXIST_ERROR);
mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
// enable MCPWM module incase user don't use `mcpwm_init` at all. always increase reference count
periph_module_enable(mcpwm_periph_signals.groups[mcpwm_num].module);
mcpwm_hal_init_config_t init_config = {
.host_id = mcpwm_num
};
mcpwm_hal_init(hal, &init_config);
mcpwm_critical_enter(mcpwm_num);
mcpwm_ll_group_set_clock_prescale(hal->dev, context[mcpwm_num].group_pre_scale);
mcpwm_ll_capture_enable_timer(hal->dev, true);
mcpwm_ll_capture_enable_channel(hal->dev, cap_channel, true);
mcpwm_ll_capture_enable_negedge(hal->dev, cap_channel, cap_conf->cap_edge & MCPWM_NEG_EDGE);
mcpwm_ll_capture_enable_posedge(hal->dev, cap_channel, cap_conf->cap_edge & MCPWM_POS_EDGE);
mcpwm_ll_capture_set_prescale(hal->dev, cap_channel, cap_conf->cap_prescale);
// capture feature should be used with interupt, so enable it by default
mcpwm_ll_intr_enable_capture(hal->dev, cap_channel, true);
mcpwm_ll_intr_clear_capture_status(hal->dev, 1 << cap_channel);
mcpwm_critical_exit(mcpwm_num);
mcpwm_mutex_lock(mcpwm_num);
context[mcpwm_num].cap_isr_func[cap_channel].fn = cap_conf->capture_cb;
context[mcpwm_num].cap_isr_func[cap_channel].args = cap_conf->user_data;
esp_err_t ret = ESP_OK;
if (context[mcpwm_num].mcpwm_intr_handle == NULL) {
ret = esp_intr_alloc(mcpwm_periph_signals.groups[mcpwm_num].irq_id, 0,
mcpwm_default_isr_handler,
(void *) (context + mcpwm_num), &(context[mcpwm_num].mcpwm_intr_handle));
}
mcpwm_mutex_unlock(mcpwm_num);
return ret;
}
esp_err_t mcpwm_capture_disable_channel(mcpwm_unit_t mcpwm_num, mcpwm_capture_channel_id_t cap_channel)
{
ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
ESP_RETURN_ON_FALSE(cap_channel < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER, ESP_ERR_INVALID_ARG, TAG, MCPWM_CAPTURE_ERROR);
mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
mcpwm_critical_enter(mcpwm_num);
mcpwm_ll_capture_enable_channel(hal->dev, cap_channel, false);
mcpwm_ll_intr_enable_capture(hal->dev, cap_channel, false);
mcpwm_critical_exit(mcpwm_num);
mcpwm_mutex_lock(mcpwm_num);
context[mcpwm_num].cap_isr_func[cap_channel].fn = NULL;
context[mcpwm_num].cap_isr_func[cap_channel].args = NULL;
// if all user defined ISR callback is disabled, free the handle
bool should_free_handle = true;
for (int i = 0; i < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER; ++i) {
if (context[mcpwm_num].cap_isr_func[i].fn != NULL) {
should_free_handle = false;
break;
}
}
esp_err_t ret = ESP_OK;
if (should_free_handle) {
ret = esp_intr_free(context[mcpwm_num].mcpwm_intr_handle);
if (ret != ESP_OK){
ESP_LOGE(TAG, "failed to free interrupt handle");
}
context[mcpwm_num].mcpwm_intr_handle = NULL;
}
mcpwm_mutex_unlock(mcpwm_num);
// always decrease reference count
periph_module_disable(mcpwm_periph_signals.groups[mcpwm_num].module);
return ret;
}
uint32_t mcpwm_capture_signal_get_value(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t cap_sig)
{
ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);

Wyświetl plik

@ -29,8 +29,6 @@
#define MCPWM_TEST_GROUP_CLK_HZ (SOC_MCPWM_BASE_CLK_HZ / 16)
#define MCPWM_TEST_TIMER_CLK_HZ (MCPWM_TEST_GROUP_CLK_HZ / 10)
static mcpwm_dev_t *MCPWM[2] = {&MCPWM0, &MCPWM1}; // interrupt handling still lacks API to get/clear pending event, currently we have to read/write interrupt register
const static mcpwm_io_signals_t pwma[] = {MCPWM0A, MCPWM1A, MCPWM2A};
const static mcpwm_io_signals_t pwmb[] = {MCPWM0B, MCPWM1B, MCPWM2B};
const static mcpwm_fault_signal_t fault_sig_array[] = {MCPWM_SELECT_F0, MCPWM_SELECT_F1, MCPWM_SELECT_F2};
@ -383,18 +381,13 @@ static void mcpwm_capture_test(mcpwm_unit_t unit, mcpwm_capture_signal_t cap_cha
TaskHandle_t task_hdl;
} test_capture_callback_data_t;
void test_mcpwm_intr_handler(void *arg) {
bool test_mcpwm_intr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_sig, const cap_event_data_t *edata, void *arg) {
BaseType_t high_task_wakeup = pdFALSE;
test_capture_callback_data_t *cb_data = (test_capture_callback_data_t *)arg;
uint32_t status = MCPWM[cb_data->unit]->int_st.val;
MCPWM[cb_data->unit]->int_clr.val = status;
vTaskNotifyGiveFromISR(cb_data->task_hdl, &high_task_wakeup);
if (high_task_wakeup == pdTRUE) {
portYIELD_FROM_ISR();
}
return high_task_wakeup == pdTRUE;
}
intr_handle_t mcpwm_intr = NULL;
test_capture_callback_data_t callback_data = {
.unit = unit,
.task_hdl = xTaskGetCurrentTaskHandle(),
@ -402,17 +395,23 @@ static void mcpwm_capture_test(mcpwm_unit_t unit, mcpwm_capture_signal_t cap_cha
//each timer test the capture sig with the same id with it.
mcpwm_io_signals_t cap_io = cap_io_sig_array[cap_chan];
mcpwm_capture_signal_t cap_sig = cap_sig_array[cap_chan];
mcpwm_capture_channel_id_t cap_channel = cap_sig_array[cap_chan];
TEST_ESP_OK(test_mcpwm_gpio_init(unit, cap_io, TEST_CAP_GPIO));
TEST_ESP_OK(mcpwm_capture_enable(unit, cap_sig, MCPWM_POS_EDGE, 0));
TEST_ESP_OK(mcpwm_isr_register(unit, test_mcpwm_intr_handler, &callback_data, 0, &mcpwm_intr));
mcpwm_capture_config_t conf = {
.cap_edge = MCPWM_POS_EDGE,
.cap_prescale = 1,
.capture_cb = test_mcpwm_intr_handler,
.user_data = &callback_data
};
TEST_ESP_OK(mcpwm_capture_enable_channel(unit, cap_channel, &conf));
// generate an posage
gpio_set_level(TEST_CAP_GPIO, 0);
gpio_set_level(TEST_CAP_GPIO, 1);
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(40)));
uint32_t cap_val0 = mcpwm_capture_signal_get_value(unit, cap_chan);
// generate another posage
gpio_set_level(TEST_CAP_GPIO, 0);
gpio_set_level(TEST_CAP_GPIO, 1);
@ -421,8 +420,7 @@ static void mcpwm_capture_test(mcpwm_unit_t unit, mcpwm_capture_signal_t cap_cha
// capture clock source is APB (80MHz), 100ms means 8000000 ticks
TEST_ASSERT_UINT_WITHIN(100000, 8000000, cap_val1 - cap_val0);
TEST_ESP_OK(mcpwm_capture_disable(unit, cap_sig));
TEST_ESP_OK(esp_intr_free(mcpwm_intr));
TEST_ESP_OK(mcpwm_capture_disable_channel(unit, cap_channel));
}
TEST_CASE("MCPWM capture test", "[mcpwm]")

Wyświetl plik

@ -23,7 +23,7 @@ More detailed block diagram of the MCPWM unit is shown below. Each A/B pair may
MCPWM Block Diagram
Description of this API starts with configuration of MCPWM's **Timer** and **Generator** submodules to provide the basic motor control functionality. Then it discusses more advanced submodules and functionalities of a **Fault Handler**, signal **Capture**, **Carrier** and **Interrupts**.
Description of this API starts with configuration of MCPWM's **Timer** and **Generator** submodules to provide the basic motor control functionality. Then it discusses more advanced submodules and functionalities of a **Fault Handler**, signal **Capture** and **Carrier**.
Contents
--------
@ -34,7 +34,6 @@ Contents
* `Capture`_ external signals to provide additional control over the outputs
* Use `Fault Handler`_ to detect and manage faults
* Add a higher frequency `Carrier`_, if output signals are passed through an isolation transformer
* Configuration and handling of `Interrupts`_.
* Extra configuration of `Resolution`_.
@ -109,11 +108,13 @@ One of requirements of BLDC (Brushless DC, see figure below) motor control is se
The capture functionality may be used for other types of motors or tasks. The functionality is enabled in two steps:
1. Configuration of GPIOs to act as the capture signal inputs by calling functions :cpp:func:`mcpwm_gpio_init` or :cpp:func:`mcpwm_set_pin`, that were described in section `Configure`_.
2. Enabling of the functionality itself by invoking :cpp:func:`mcpwm_capture_enable`, selecting desired signal input from :cpp:type:`mcpwm_capture_signal_t`, setting the signal edge with :cpp:type:`mcpwm_capture_on_edge_t` and the signal count prescaler.
2. Enabling of the functionality itself by invoking :cpp:func:`mcpwm_capture_enable_channel`, selecting desired signal input from :cpp:type:`mcpwm_capture_channel_id_t`, setting the signal edge, signal count prescaler and user callback within :cpp:type:`mcpwm_capture_config_t`
Within the second step above a 32-bit capture timer is enabled. The timer runs continuously driven by the APB clock. The clock frequency is typically 80 MHz. On each capture event the capture timers value is stored in time-stamp register that may be then checked by calling :cpp:func:`mcpwm_capture_signal_get_value`. The edge of the last signal may be checked with :cpp:func:`mcpwm_capture_signal_get_edge`.
Within the second step above a 32-bit capture timer is enabled. The timer runs continuously driven by the APB clock. The clock frequency is typically 80 MHz. On each capture event the capture timers value is stored in time-stamp register that may be then checked by calling :cpp:func:`mcpwm_capture_signal_get_value`. The edge of the last signal may be checked with :cpp:func:`mcpwm_capture_signal_get_edge`. Those data are also provided inside callback function as event data :cpp:type:`cap_event_data_t`
If not required anymore, the capture functionality may be disabled with :cpp:func:`mcpwm_capture_disable`.
If not required anymore, the capture functionality may be disabled with :cpp:func:`mcpwm_capture_disable_channel`.
Capture prescale is different from other modules as it is applied to the input signal, not the timer source. Prescaler has maintained its own level state with the initial value set to low and is detecting the positive edge of the input signal to change its internal state. That means if two pairs of positive and negative edges are passed to input, the prescaler's internal state will change twice. ISR will report on this internal state change, not the input signal. For example, setting prescale to 2 will generate ISR callback on each positive edge of input if both edge is selected via :cpp:type:`mcpwm_capture_config_t`. Or each 2 positive edges of input if only one edge is selected though :cpp:type:`mcpwm_capture_config_t`.
Fault Handler
@ -158,7 +159,7 @@ To disable carrier functionality call :cpp:func:`mcpwm_carrier_disable`.
Interrupts
----------
Registering of the MCPWM interrupt handler is possible by calling :cpp:func:`mcpwm_isr_register`.
Registering of the MCPWM interrupt handler is possible by calling :cpp:func:`mcpwm_isr_register`. Note if :cpp:func:`mcpwm_capture_enable_channel` is used then a default ISR routine will be installed hence please do not call this function to register any more.
Resolution
@ -171,15 +172,17 @@ Note that, these two APIs won't update the frequency and duty automatically, to
To get PWM pulse that is below 15Hz, please set the resolution to a lower value. For high frequency PWM with limited step range, please set them with higher value.
Application Example
-------------------
Examples of using MCPWM for motor control: :example:`peripherals/mcpwm`:
MCPWM example are located under: :example:`peripherals/mcpwm`:
* Demonstration how to use each submodule of the MCPWM - :example:`peripherals/mcpwm/mcpwm_basic_config`
* Control of BLDC (brushless DC) motor with hall sensor feedback - :example:`peripherals/mcpwm/mcpwm_bldc_control`
* Brushed DC motor control - :example:`peripherals/mcpwm/mcpwm_brushed_dc_control`
* Servo motor control - :example:`peripherals/mcpwm/mcpwm_servo_control`
* HC-SR04 sensor with capture - :example:`peripherals/mcpwm/mcpwm_capture_hc_sr04`
API Reference

Wyświetl plik

@ -18,23 +18,13 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_attr.h"
#include "soc/rtc.h"
#include "driver/mcpwm.h"
#include "soc/mcpwm_periph.h"
#define MCPWM_EN_CARRIER 0 //Make this 1 to test carrier submodule of mcpwm, set high frequency carrier parameters
#define MCPWM_EN_DEADTIME 0 //Make this 1 to test deadtime submodule of mcpwm, set deadtime value and deadtime mode
#define MCPWM_EN_FAULT 0 //Make this 1 to test fault submodule of mcpwm, set action on MCPWM signal on fault occurence like overcurrent, overvoltage, etc
#define MCPWM_EN_SYNC 0 //Make this 1 to test sync submodule of mcpwm, sync timer signals
#define MCPWM_EN_CAPTURE 0 //Make this 1 to test capture submodule of mcpwm, measure time between rising/falling edge of captured signal
#define MCPWM_GPIO_INIT 0 //select which function to use to initialize gpio signals
#define CAP_SIG_NUM 3 //Three capture signals
#define CAP0_INT_EN BIT(27) //Capture 0 interrupt bit
#define CAP1_INT_EN BIT(28) //Capture 1 interrupt bit
#define CAP2_INT_EN BIT(29) //Capture 2 interrupt bit
#define GPIO_PWM0A_OUT 19 //Set GPIO 19 as PWM0A
#define GPIO_PWM0B_OUT 18 //Set GPIO 18 as PWM0B
@ -42,9 +32,6 @@
#define GPIO_PWM1B_OUT 16 //Set GPIO 16 as PWM1B
#define GPIO_PWM2A_OUT 15 //Set GPIO 15 as PWM2A
#define GPIO_PWM2B_OUT 14 //Set GPIO 14 as PWM2B
#define GPIO_CAP0_IN 23 //Set GPIO 23 as CAP0
#define GPIO_CAP1_IN 25 //Set GPIO 25 as CAP1
#define GPIO_CAP2_IN 26 //Set GPIO 26 as CAP2
#define GPIO_SYNC0_IN 2 //Set GPIO 02 as SYNC0
#define GPIO_SYNC1_IN 4 //Set GPIO 04 as SYNC1
#define GPIO_SYNC2_IN 5 //Set GPIO 05 as SYNC2
@ -52,19 +39,6 @@
#define GPIO_FAULT1_IN 33 //Set GPIO 33 as FAULT1
#define GPIO_FAULT2_IN 34 //Set GPIO 34 as FAULT2
typedef struct {
uint32_t capture_signal;
mcpwm_capture_signal_t sel_cap_signal;
} capture;
uint32_t *current_cap_value = NULL;
uint32_t *previous_cap_value = NULL;
xQueueHandle cap_queue;
#if MCPWM_EN_CAPTURE
static mcpwm_dev_t *MCPWM[2] = {&MCPWM0, &MCPWM1};
#endif
static void mcpwm_example_gpio_initialize(void)
{
printf("initializing mcpwm gpio...\n");
@ -97,16 +71,10 @@ static void mcpwm_example_gpio_initialize(void)
.mcpwm_sync2_in_num = GPIO_SYNC2_IN,
.mcpwm_fault0_in_num = GPIO_FAULT0_IN,
.mcpwm_fault1_in_num = GPIO_FAULT1_IN,
.mcpwm_fault2_in_num = GPIO_FAULT2_IN,
.mcpwm_cap0_in_num = GPIO_CAP0_IN,
.mcpwm_cap1_in_num = GPIO_CAP1_IN,
.mcpwm_cap2_in_num = GPIO_CAP2_IN
.mcpwm_fault2_in_num = GPIO_FAULT2_IN
};
mcpwm_set_pin(MCPWM_UNIT_0, &pin_config);
#endif
gpio_pulldown_en(GPIO_CAP0_IN); //Enable pull down on CAP0 signal
gpio_pulldown_en(GPIO_CAP1_IN); //Enable pull down on CAP1 signal
gpio_pulldown_en(GPIO_CAP2_IN); //Enable pull down on CAP2 signal
gpio_pulldown_en(GPIO_SYNC0_IN); //Enable pull down on SYNC0 signal
gpio_pulldown_en(GPIO_SYNC1_IN); //Enable pull down on SYNC1 signal
gpio_pulldown_en(GPIO_SYNC2_IN); //Enable pull down on SYNC2 signal
@ -115,82 +83,6 @@ static void mcpwm_example_gpio_initialize(void)
gpio_pulldown_en(GPIO_FAULT2_IN); //Enable pull down on FAULT2 signal
}
/**
* @brief Set gpio 12 as our test signal that generates high-low waveform continuously, connect this gpio to capture pin.
*/
static void gpio_test_signal(void *arg)
{
printf("intializing test signal...\n");
gpio_config_t gp;
gp.intr_type = GPIO_INTR_DISABLE;
gp.mode = GPIO_MODE_OUTPUT;
gp.pin_bit_mask = GPIO_SEL_12;
gpio_config(&gp);
while (1) {
//here the period of test signal is 20ms
gpio_set_level(GPIO_NUM_12, 1); //Set high
vTaskDelay(10); //delay of 10ms
gpio_set_level(GPIO_NUM_12, 0); //Set low
vTaskDelay(10); //delay of 10ms
}
}
/**
* @brief When interrupt occurs, we receive the counter value and display the time between two rising edge
*/
static void disp_captured_signal(void *arg)
{
capture evt;
while (1) {
xQueueReceive(cap_queue, &evt, portMAX_DELAY);
if (evt.sel_cap_signal == MCPWM_SELECT_CAP0) {
printf("CAP0 : %d us\n", evt.capture_signal);
}
if (evt.sel_cap_signal == MCPWM_SELECT_CAP1) {
printf("CAP1 : %d us\n", evt.capture_signal);
}
if (evt.sel_cap_signal == MCPWM_SELECT_CAP2) {
printf("CAP2 : %d us\n", evt.capture_signal);
}
}
}
#if MCPWM_EN_CAPTURE
/**
* @brief this is ISR handler function, here we check for interrupt that triggers rising edge on CAP0 signal and according take action
*/
static void IRAM_ATTR isr_handler(void* arg)
{
uint32_t mcpwm_intr_status;
capture evt;
mcpwm_intr_status = MCPWM[MCPWM_UNIT_0]->int_st.val; //Read interrupt status
//calculate the interval in the ISR,
//so that the interval will be always correct even when cap_queue is not handled in time and overflow.
if (mcpwm_intr_status & CAP0_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
current_cap_value[0] = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP0); //get capture signal counter value
evt.capture_signal = (current_cap_value[0] - previous_cap_value[0]) / (rtc_clk_apb_freq_get() / 1000000);
previous_cap_value[0] = current_cap_value[0];
evt.sel_cap_signal = MCPWM_SELECT_CAP0;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
if (mcpwm_intr_status & CAP1_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
current_cap_value[1] = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP1); //get capture signal counter value
evt.capture_signal = (current_cap_value[1] - previous_cap_value[1]) / (rtc_clk_apb_freq_get() / 1000000);
previous_cap_value[1] = current_cap_value[1];
evt.sel_cap_signal = MCPWM_SELECT_CAP1;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
if (mcpwm_intr_status & CAP2_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
current_cap_value[2] = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP2); //get capture signal counter value
evt.capture_signal = (current_cap_value[2] - previous_cap_value[2]) / (rtc_clk_apb_freq_get() / 1000000);
previous_cap_value[2] = current_cap_value[2];
evt.sel_cap_signal = MCPWM_SELECT_CAP2;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
MCPWM[MCPWM_UNIT_0]->int_clr.val = mcpwm_intr_status;
}
#endif
/**
* @brief Configure whole MCPWM module
*/
@ -266,29 +158,11 @@ static void mcpwm_example_config(void *arg)
mcpwm_sync_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_SELECT_SYNC0, 200); //Load counter value with 20% of period counter of mcpwm timer 1 when sync 0 occurs
#endif
#if MCPWM_EN_CAPTURE
//7. Capture configuration
//comment if you don't want to use capture submodule, also u can comment the capture gpio signals
//configure CAP0, CAP1 and CAP2 signal to start capture counter on rising edge
//we generate a gpio_test_signal of 20ms on GPIO 12 and connect it to one of the capture signal, the disp_captured_function displays the time between rising edge
//In general practice you can connect Capture to external signal, measure time between rising edge or falling edge and take action accordingly
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, MCPWM_POS_EDGE, 0); //capture signal on rising edge, prescale = 0 i.e. 800,000,000 counts is equal to one second
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP2, MCPWM_POS_EDGE, 0); //capture signal on rising edge, prescale = 0 i.e. 800,000,000 counts is equal to one second
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP1, MCPWM_POS_EDGE, 0); //capture signal on rising edge, prescale = 0 i.e. 800,000,000 counts is equal to one second
//enable interrupt, so each this a rising edge occurs interrupt is triggered
MCPWM[MCPWM_UNIT_0]->int_ena.val = CAP0_INT_EN | CAP1_INT_EN | CAP2_INT_EN; //Enable interrupt on CAP0, CAP1 and CAP2 signal
mcpwm_isr_register(MCPWM_UNIT_0, isr_handler, NULL, ESP_INTR_FLAG_IRAM, NULL); //Set ISR Handler
#endif
vTaskDelete(NULL);
}
void app_main(void)
{
printf("Testing MCPWM...\n");
cap_queue = xQueueCreate(1, sizeof(capture)); //comment if you don't want to use capture module
current_cap_value = (uint32_t *)malloc(CAP_SIG_NUM*sizeof(uint32_t)); //comment if you don't want to use capture module
previous_cap_value = (uint32_t *)malloc(CAP_SIG_NUM*sizeof(uint32_t)); //comment if you don't want to use capture module
xTaskCreate(disp_captured_signal, "mcpwm_config", 4096, NULL, 5, NULL); //comment if you don't want to use capture module
xTaskCreate(gpio_test_signal, "gpio_test_signal", 4096, NULL, 5, NULL); //comment if you don't want to use capture module
xTaskCreate(mcpwm_example_config, "mcpwm_example_config", 4096, NULL, 5, NULL);
}

Wyświetl plik

@ -20,10 +20,8 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_attr.h"
#include "soc/rtc.h"
#include "driver/mcpwm.h"
#include "soc/mcpwm_periph.h"
#define INITIAL_DUTY 10.0 //initial duty cycle is 10.0%
#define MCPWM_GPIO_INIT 0 //select which function to use to initialize gpio signals
@ -56,8 +54,6 @@ static uint32_t hall_sensor_previous = 0;
xQueueHandle cap_queue;
static mcpwm_dev_t *MCPWM[2] = {&MCPWM0, &MCPWM1};
static void mcpwm_example_gpio_initialize(void)
{
printf("initializing mcpwm bldc control gpio...\n");
@ -162,27 +158,25 @@ static void disp_captured_signal(void *arg)
/**
* @brief this is ISR handler function, here we check for interrupt that triggers rising edge on CAP0 signal and according take action
*/
static void IRAM_ATTR isr_handler(void *arg)
static bool isr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_sig, const cap_event_data_t *edata, void *arg)
{
uint32_t mcpwm_intr_status;
capture evt;
mcpwm_intr_status = MCPWM[MCPWM_UNIT_0]->int_st.val; //Read interrupt status
if (mcpwm_intr_status & CAP0_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP0); //get capture signal counter value
if (cap_sig == MCPWM_SELECT_CAP0) { //Check for interrupt on rising edge on CAP0 signal
evt.capture_signal = edata->cap_value; //get capture signal counter value
evt.sel_cap_signal = MCPWM_SELECT_CAP0;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
if (mcpwm_intr_status & CAP1_INT_EN) { //Check for interrupt on rising edge on CAP1 signal
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP1); //get capture signal counter value
if (cap_sig == MCPWM_SELECT_CAP1) { //Check for interrupt on rising edge on CAP1 signal
evt.capture_signal = edata->cap_value; //get capture signal counter value
evt.sel_cap_signal = MCPWM_SELECT_CAP1;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
if (mcpwm_intr_status & CAP2_INT_EN) { //Check for interrupt on rising edge on CAP2 signal
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP2); //get capture signal counter value
if (cap_sig == MCPWM_SELECT_CAP2) { //Check for interrupt on rising edge on CAP2 signal
evt.capture_signal = edata->cap_value; //get capture signal counter value
evt.sel_cap_signal = MCPWM_SELECT_CAP2;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
MCPWM[MCPWM_UNIT_0]->int_clr.val = mcpwm_intr_status;
return false;
}
#if CHANGE_DUTY_CONTINUOUSLY
@ -228,12 +222,15 @@ static void mcpwm_example_bldc_control(void *arg)
//configure CAP0, CAP1 and CAP2 signal to start capture counter on rising edge
//we generate a gpio_test_signal of 20ms on GPIO 12 and connect it to one of the capture signal, the disp_captured_function displays the time between rising edge
//In general practice you can connect Capture to external signal, measure time between rising edge or falling edge and take action accordingly
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, MCPWM_POS_EDGE, 0); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP1, MCPWM_POS_EDGE, 0); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP2, MCPWM_POS_EDGE, 0); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
//enable interrupt, so each this a rising edge occurs interrupt is triggered
MCPWM[MCPWM_UNIT_0]->int_ena.val = (CAP0_INT_EN | CAP1_INT_EN | CAP2_INT_EN); //Enable interrupt on CAP0, CAP1 and CAP2 signal
mcpwm_isr_register(MCPWM_UNIT_0, isr_handler, NULL, ESP_INTR_FLAG_IRAM, NULL); //Set ISR Handler
mcpwm_capture_config_t conf = {
.cap_edge = MCPWM_POS_EDGE,
.cap_prescale = 1,
.capture_cb = isr_handler,
.user_data = NULL,
};
mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, &conf); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP1, &conf); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP2, &conf); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
//According to the hall sensor input value take action on PWM0A/0B/1A/1B/2A/2B
while (1) {
hall_sensor_value = (gpio_get_level(GPIO_NUM_27) * 1) + (gpio_get_level(GPIO_NUM_26) * 2) + (gpio_get_level(GPIO_NUM_25) * 4);

Wyświetl plik

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mcpwm_capture_hc_sr04)

Wyświetl plik

@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := mcpwm_capture_hc_sr04
include $(IDF_PATH)/make/project.mk

Wyświetl plik

@ -0,0 +1,81 @@
| Supported Targets | ESP32 | ESP32-S3 |
| ----------------- | ----- | -------- |
# HC-SR04 Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
The capture module in MCPWM peripheral is designed to accurately log the time stamp on the hardware side when an event happens (compared to GPIO ISR which requires a software-based logging method). Each capture unit has three channels, which can be used together to capture IO events parallelly.
This example shows how to make use of the HW features to decode the pulse width signals generated from a common HC-SR04 sonar range finder -- [HC-SR04](https://www.sparkfun.com/products/15569).
The signal that HC-SR04 produces (and what can be handled by this example) is a simple pulse whose width indicates the measured distance. A pulse is required to send to HC-SR04 on `Trig` pin to begin a new measurement. Then the pulse described above will be sent back on `Echo` pin for decoding.
Typical signals:
```
Trig +-----+
| |
| |
-----+ +-----------------------
Echo +-----+
| |
| |
-----------------+ +-----------
+--------------------------------------->
Timeline
```
## How to Use Example
### Hardware Required
* An ESP development board
* HC-SR04 module
Connection :
```
+------+ +---------------------------------+
+-------+ | | |
| | VCC +--------------+ 5V |
+-------+ | | |
+ Echo +----=====>----+ GPIO18 (internal pull up) |
| | | |
+ Trig +----<=====----+ GPIO19 |
+-------| | | |
| | GND +--------------+ GND |
+-------| | | |
+------+ +---------------------------------+
```
### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (314) hc-sr04: HC-SR04 example based on capture function from MCPWM
I (324) hc-sr04: Echo pin configured
I (324) gpio: GPIO[19]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (334) hc-sr04: Trig pin configured
I (344) hc-sr04: trig task started
I (444) hc-sr04: Pulse width: 419us, Measured distance: 7.22cm
I (544) hc-sr04: Pulse width: 419us, Measured distance: 7.22cm
I (644) hc-sr04: Pulse width: 416us, Measured distance: 7.17cm
I (744) hc-sr04: Pulse width: 415us, Measured distance: 7.16cm
I (844) hc-sr04: Pulse width: 415us, Measured distance: 7.16cm
I (944) hc-sr04: Pulse width: 416us, Measured distance: 7.17cm
I (1044) hc-sr04: Pulse width: 391us, Measured distance: 6.74cm
```
This example runs at 10Hz sampling rate. out of range data is dropped and only valid measurement is printed.
## Troubleshooting
For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

Wyświetl plik

@ -0,0 +1,2 @@
idf_component_register(SRCS "mcpwm_capture_hc_sr04.c"
INCLUDE_DIRS ".")

Wyświetl plik

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

Wyświetl plik

@ -0,0 +1,132 @@
/* MCPWM capture example: HC-SR04
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
* This example will show you how to use capture function to read HC-SR04 sonar sensor.
*
* HC_SR04_SAMPLE_PERIOD_MS should be at least 50ms
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "esp_check.h"
#include "soc/rtc.h"
#include "driver/mcpwm.h"
const static char *TAG = "hc-sr04";
#define HC_SR04_SAMPLE_PERIOD_MS 100
_Static_assert(HC_SR04_SAMPLE_PERIOD_MS > 50, "Sample period too short!");
#define HC_SR04_PIN_ECHO GPIO_NUM_18
#define HC_SR04_PIN_TRIG GPIO_NUM_19
#define TRIGGER_THREAD_STACK_SIZE 512
#define TRIGGER_THREAD_PRIORITY 5
typedef struct {
uint32_t capture_signal;
mcpwm_capture_signal_t sel_cap_signal;
} capture;
static uint32_t cap_val_begin_of_sample = 0;
static uint32_t cap_val_end_of_sample = 0;
static xQueueHandle cap_queue;
/**
* @brief generate single pulse on Trig pin to activate a new sample
*/
static void gen_trig_output(void *arg) {
TickType_t xLastWakeTime = xTaskGetTickCount();
while (true) {
vTaskDelayUntil(&xLastWakeTime, HC_SR04_SAMPLE_PERIOD_MS / portTICK_PERIOD_MS);
ESP_ERROR_CHECK(gpio_set_level(HC_SR04_PIN_TRIG, 1)); // set high
esp_rom_delay_us(10);
ESP_ERROR_CHECK(gpio_set_level(HC_SR04_PIN_TRIG, 0)); // set low
}
}
/**
* @brief this is an ISR callback, we take action according to the captured edge
*/
static bool sr04_echo_isr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_sig, const cap_event_data_t *edata,
void *arg) {
//calculate the interval in the ISR,
//so that the interval will be always correct even when cap_queue is not handled in time and overflow.
BaseType_t high_task_wakeup = pdFALSE;
if (edata->cap_edge == MCPWM_POS_EDGE) {
// 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;
} else {
cap_val_end_of_sample = edata->cap_value;
// following formula refers to: https://www.elecrow.com/download/HC_SR04%20Datasheet.pdf
uint32_t pulse_count = cap_val_end_of_sample - cap_val_begin_of_sample;
// send measurement back though queue
xQueueSendFromISR(cap_queue, &pulse_count, &high_task_wakeup);
}
return high_task_wakeup == pdTRUE;
}
void app_main(void) {
ESP_LOGI(TAG, "HC-SR04 example based on capture function from MCPWM");
/* configure Echo pin */
// set CAP_0 on GPIO
ESP_ERROR_CHECK(mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, HC_SR04_PIN_ECHO));
// enable pull down CAP0, to reduce noise
ESP_ERROR_CHECK(gpio_pulldown_en(HC_SR04_PIN_ECHO));
// enable both edge capture on CAP0
mcpwm_capture_config_t conf = {
.cap_edge = MCPWM_BOTH_EDGE,
.cap_prescale = 1,
.capture_cb = sr04_echo_isr_handler,
.user_data = NULL
};
ESP_ERROR_CHECK(mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, &conf));
ESP_LOGI(TAG, "Echo pin configured");
/* configure Trig pin */
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pin_bit_mask = BIT64(HC_SR04_PIN_TRIG),
};
ESP_ERROR_CHECK(gpio_config(&io_conf));
ESP_ERROR_CHECK(gpio_set_level(HC_SR04_PIN_TRIG, 0)); // drive low by default
ESP_LOGI(TAG, "Trig pin configured");
// the queue where we read data
cap_queue = xQueueCreate(1, sizeof(uint32_t));
if (cap_queue == 0) {
ESP_LOGE(TAG, "failed to alloc cap_queue");
}
// start generating trig signal
xTaskCreate(gen_trig_output, "gen_trig_output", TRIGGER_THREAD_STACK_SIZE, NULL, TRIGGER_THREAD_PRIORITY, NULL);
ESP_LOGI(TAG, "trig task started");
// forever loop
while (true) {
uint32_t pulse_count;
// block and wait for new measurement
xQueueReceive(cap_queue, &pulse_count, portMAX_DELAY);
uint32_t pulse_width_us = pulse_count * (1000000.0 / rtc_clk_apb_freq_get());
// following formula is based on: https://www.elecrow.com/download/HC_SR04%20Datasheet.pdf
if (pulse_width_us > 35000) {
// out of range
continue;
}
float distance = (float) pulse_width_us / 58;
ESP_LOGI(TAG, "Pulse width: %uus, Measured distance: %.2fcm", pulse_width_us, distance);
}
}