mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
mcpwm: new driver implementation
This commit is contained in:
parent
fd3a1ffc21
commit
1571417679
@ -8,6 +8,10 @@ components/driver/test_apps/i2s_test_apps/legacy_i2s_adc_dac:
|
||||
disable:
|
||||
- if: SOC_I2S_SUPPORTS_ADC_DAC != 1
|
||||
|
||||
components/driver/test_apps/legacy_mcpwm_driver:
|
||||
disable:
|
||||
- if: SOC_MCPWM_SUPPORTED != 1
|
||||
|
||||
components/driver/test_apps/legacy_pcnt_driver:
|
||||
disable:
|
||||
- if: SOC_PCNT_SUPPORTED != 1
|
||||
@ -20,6 +24,10 @@ components/driver/test_apps/legacy_rtc_temp_driver:
|
||||
disable:
|
||||
- if: SOC_TEMP_SENSOR_SUPPORTED != 1
|
||||
|
||||
components/driver/test_apps/mcpwm:
|
||||
disable:
|
||||
- if: SOC_MCPWM_SUPPORTED != 1
|
||||
|
||||
components/driver/test_apps/pulse_cnt:
|
||||
disable:
|
||||
- if: SOC_PCNT_SUPPORTED != 1
|
||||
|
@ -32,7 +32,15 @@ if(CONFIG_SOC_ADC_DMA_SUPPORTED)
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_MCPWM_SUPPORTED)
|
||||
list(APPEND srcs "mcpwm.c")
|
||||
list(APPEND srcs "mcpwm/mcpwm_cap.c"
|
||||
"mcpwm/mcpwm_cmpr.c"
|
||||
"mcpwm/mcpwm_com.c"
|
||||
"mcpwm/mcpwm_fault.c"
|
||||
"mcpwm/mcpwm_gen.c"
|
||||
"mcpwm/mcpwm_oper.c"
|
||||
"mcpwm/mcpwm_sync.c"
|
||||
"mcpwm/mcpwm_timer.c"
|
||||
"deprecated/mcpwm_legacy.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_DEDICATED_GPIO_SUPPORTED)
|
||||
@ -88,9 +96,12 @@ if(CONFIG_SOC_TOUCH_SENSOR_SUPPORTED)
|
||||
list(APPEND srcs "touch_sensor_common.c" "${target}/touch_sensor.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_SDIO_SLAVE_SUPPORTED)
|
||||
list(APPEND srcs "sdio_slave.c")
|
||||
endif()
|
||||
|
||||
if(${target} STREQUAL "esp32")
|
||||
list(APPEND srcs "dac_common.c"
|
||||
"sdio_slave.c"
|
||||
"deprecated/adc_i2s_deprecated.c"
|
||||
"esp32/dac.c")
|
||||
endif()
|
||||
|
194
components/driver/include/driver/mcpwm_cap.h
Normal file
194
components/driver/include/driver/mcpwm_cap.h
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MCPWM capture timer configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
int group_id; /*!< Specify from which group to allocate the capture timer */
|
||||
mcpwm_capture_clock_source_t clk_src; /*!< MCPWM capture timer clock source */
|
||||
} mcpwm_capture_timer_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create MCPWM capture timer
|
||||
*
|
||||
* @param[in] config MCPWM capture timer configuration
|
||||
* @param[out] ret_cap_timer Returned MCPWM capture timer handle
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM capture timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM capture timer failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM capture timer failed because out of memory
|
||||
* - ESP_ERR_NOT_FOUND: Create MCPWM capture timer failed because can't find free resource
|
||||
* - ESP_FAIL: Create MCPWM capture timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mcpwm_cap_timer_handle_t *ret_cap_timer);
|
||||
|
||||
/**
|
||||
* @brief Delete MCPWM capture timer
|
||||
*
|
||||
* @param[in] cap_timer MCPWM capture timer, allocated by `mcpwm_new_capture_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Delete MCPWM capture timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Delete MCPWM capture timer failed because of invalid argument
|
||||
* - ESP_FAIL: Delete MCPWM capture timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_del_capture_timer(mcpwm_cap_timer_handle_t cap_timer);
|
||||
|
||||
/**
|
||||
* @brief Enable MCPWM capture timer
|
||||
*
|
||||
* @param[in] cap_timer MCPWM capture timer handle, allocated by `mcpwm_new_capture_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Enable MCPWM capture timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Enable MCPWM capture timer failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Enable MCPWM capture timer failed because timer is enabled already
|
||||
* - ESP_FAIL: Enable MCPWM capture timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_capture_timer_enable(mcpwm_cap_timer_handle_t cap_timer);
|
||||
|
||||
/**
|
||||
* @brief Disable MCPWM capture timer
|
||||
*
|
||||
* @param[in] cap_timer MCPWM capture timer handle, allocated by `mcpwm_new_capture_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Disable MCPWM capture timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Disable MCPWM capture timer failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Disable MCPWM capture timer failed because timer is disabled already
|
||||
* - ESP_FAIL: Disable MCPWM capture timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_capture_timer_disable(mcpwm_cap_timer_handle_t cap_timer);
|
||||
|
||||
/**
|
||||
* @brief Start MCPWM capture timer
|
||||
*
|
||||
* @param[in] cap_timer MCPWM capture timer, allocated by `mcpwm_new_capture_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Start MCPWM capture timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Start MCPWM capture timer failed because of invalid argument
|
||||
* - ESP_FAIL: Start MCPWM capture timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_capture_timer_start(mcpwm_cap_timer_handle_t cap_timer);
|
||||
|
||||
/**
|
||||
* @brief Start MCPWM capture timer
|
||||
*
|
||||
* @param[in] cap_timer MCPWM capture timer, allocated by `mcpwm_new_capture_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Stop MCPWM capture timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Stop MCPWM capture timer failed because of invalid argument
|
||||
* - ESP_FAIL: Stop MCPWM capture timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_capture_timer_stop(mcpwm_cap_timer_handle_t cap_timer);
|
||||
|
||||
/**
|
||||
* @brief MCPWM Capture timer sync phase configuration
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_sync_handle_t sync_src; /*!< The sync event source */
|
||||
uint32_t count_value; /*!< The count value that should lock to upon sync event */
|
||||
mcpwm_timer_direction_t direction; /*!< The count direction that should lock to upon sync event */
|
||||
} mcpwm_capture_timer_sync_phase_config_t;
|
||||
|
||||
/**
|
||||
* @brief Set sync phase for MCPWM capture timer
|
||||
*
|
||||
* @param[in] cap_timer MCPWM capture timer, allocated by `mcpwm_new_capture_timer()`
|
||||
* @param[in] config MCPWM capture timer sync phase configuration
|
||||
* @return
|
||||
* - ESP_OK: Set sync phase for MCPWM capture timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set sync phase for MCPWM capture timer failed because of invalid argument
|
||||
* - ESP_FAIL: Set sync phase for MCPWM capture timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_capture_timer_set_phase_on_sync(mcpwm_cap_timer_handle_t cap_timer, const mcpwm_capture_timer_sync_phase_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief MCPWM capture channel configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
int gpio_num; /*!< GPIO used capturing input signal */
|
||||
uint32_t prescale; /*!< Prescale of input signal, effective frequency = cap_input_clk/prescale */
|
||||
struct {
|
||||
uint32_t pos_edge: 1; /*!< Whether to capture on positive edge */
|
||||
uint32_t neg_edge: 1; /*!< Whether to capture on negative edge */
|
||||
uint32_t pull_up: 1; /*!< Whether to pull up internally */
|
||||
uint32_t pull_down: 1; /*!< Whether to pull down internally */
|
||||
uint32_t invert_cap_signal: 1; /*!< Invert the input capture signal */
|
||||
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
|
||||
} flags; /*!< Extra configuration flags for capture channel */
|
||||
} mcpwm_capture_channel_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create MCPWM capture channel
|
||||
*
|
||||
* @param[in] cap_timer MCPWM capture timer, allocated by `mcpwm_new_capture_timer()`, will be connected to the new capture channel
|
||||
* @param[in] config MCPWM capture channel configuration
|
||||
* @param[out] ret_cap_channel Returned MCPWM capture channel
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM capture channel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM capture channel failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM capture channel failed because out of memory
|
||||
* - ESP_ERR_NOT_FOUND: Create MCPWM capture channel failed because can't find free resource
|
||||
* - ESP_FAIL: Create MCPWM capture channel failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_capture_channel(mcpwm_cap_timer_handle_t cap_timer, const mcpwm_capture_channel_config_t *config, mcpwm_cap_channel_handle_t *ret_cap_channel);
|
||||
|
||||
/**
|
||||
* @brief Delete MCPWM capture channel
|
||||
*
|
||||
* @param[in] cap_channel MCPWM capture channel handle, allocated by `mcpwm_new_capture_channel()`
|
||||
* @return
|
||||
* - ESP_OK: Delete MCPWM capture channel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Delete MCPWM capture channel failed because of invalid argument
|
||||
* - ESP_FAIL: Delete MCPWM capture channel failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_del_capture_channel(mcpwm_cap_channel_handle_t cap_channel);
|
||||
|
||||
/**
|
||||
* @brief Group of supported MCPWM capture event callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_capture_event_cb_t on_cap; /*!< Callback function that would be invoked when capture event occurred */
|
||||
} mcpwm_capture_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief Set event callbacks for MCPWM capture channel
|
||||
*
|
||||
* @param[in] cap_channel MCPWM capture channel handle, allocated by `mcpwm_new_capture_channel()`
|
||||
* @param[in] cbs Group of callback functions
|
||||
* @param[in] user_data User data, which will be passed to callback functions directly
|
||||
* @return
|
||||
* - ESP_OK: Set event callbacks successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
|
||||
* - ESP_FAIL: Set event callbacks failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_capture_channel_register_event_callbacks(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Trigger a catch by software
|
||||
*
|
||||
* @param[in] cap_channel MCPWM capture channel handle, allocated by `mcpwm_new_capture_channel()`
|
||||
* @return
|
||||
* - ESP_OK: Trigger software catch successfully
|
||||
* - ESP_ERR_INVALID_ARG: Trigger software catch failed because of invalid argument
|
||||
* - ESP_FAIL: Trigger software catch failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_capture_channel_trigger_soft_catch(mcpwm_cap_channel_handle_t cap_channel);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
91
components/driver/include/driver/mcpwm_cmpr.h
Normal file
91
components/driver/include/driver/mcpwm_cmpr.h
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MCPWM comparator configuration
|
||||
*/
|
||||
typedef struct {
|
||||
struct {
|
||||
uint32_t update_cmp_on_tez: 1; /*!< Whether to update compare value when timer count equals to zero (tez) */
|
||||
uint32_t update_cmp_on_tep: 1; /*!< Whether to update compare value when timer count equals to peak (tep) */
|
||||
uint32_t update_cmp_on_sync: 1; /*!< Whether to update compare value on sync event */
|
||||
} flags; /*!< Extra configuration flags for comparator */
|
||||
} mcpwm_comparator_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create MCPWM comparator
|
||||
*
|
||||
* @param[in] oper MCPWM operator, allocated by `mcpwm_new_operator()`, the new comparator will be allocated from this operator
|
||||
* @param[in] config MCPWM comparator configuration
|
||||
* @param[out] ret_cmpr Returned MCPWM comparator
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM comparator successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM comparator failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM comparator failed because out of memory
|
||||
* - ESP_ERR_NOT_FOUND: Create MCPWM comparator failed because can't find free resource
|
||||
* - ESP_FAIL: Create MCPWM comparator failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_comparator(mcpwm_oper_handle_t oper, const mcpwm_comparator_config_t *config, mcpwm_cmpr_handle_t *ret_cmpr);
|
||||
|
||||
/**
|
||||
* @brief Delete MCPWM comparator
|
||||
*
|
||||
* @param[in] cmpr MCPWM comparator handle, allocated by `mcpwm_new_comparator()`
|
||||
* @return
|
||||
* - ESP_OK: Delete MCPWM comparator successfully
|
||||
* - ESP_ERR_INVALID_ARG: Delete MCPWM comparator failed because of invalid argument
|
||||
* - ESP_FAIL: Delete MCPWM comparator failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_del_comparator(mcpwm_cmpr_handle_t cmpr);
|
||||
|
||||
/**
|
||||
* @brief Group of supported MCPWM compare event callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_compare_event_cb_t on_reach; /*!< ISR callback function which would be invoked when counter reaches compare value */
|
||||
} mcpwm_comparator_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief Set event callbacks for MCPWM comparator
|
||||
*
|
||||
* @param[in] cmpr MCPWM comparator handle, allocated by `mcpwm_new_comparator()`
|
||||
* @param[in] cbs Group of callback functions
|
||||
* @param[in] user_data User data, which will be passed to callback functions directly
|
||||
* @return
|
||||
* - ESP_OK: Set event callbacks successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
|
||||
* - ESP_FAIL: Set event callbacks failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_comparator_register_event_callbacks(mcpwm_cmpr_handle_t cmpr, const mcpwm_comparator_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Set MCPWM comparator's compare value
|
||||
*
|
||||
* @param[in] cmpr MCPWM comparator handle, allocated by `mcpwm_new_comparator()`
|
||||
* @param[in] cmp_ticks The new compare value
|
||||
* @return
|
||||
* - ESP_OK: Set MCPWM compare value successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set MCPWM compare value failed because of invalid argument (e.g. the cmp_ticks is out of range)
|
||||
* - ESP_ERR_INVALID_STATE: Set MCPWM compare value failed because the operator doesn't have a timer connected
|
||||
* - ESP_FAIL: Set MCPWM compare value failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_comparator_set_compare_value(mcpwm_cmpr_handle_t cmpr, uint32_t cmp_ticks);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
112
components/driver/include/driver/mcpwm_fault.h
Normal file
112
components/driver/include/driver/mcpwm_fault.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MCPWM GPIO fault configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
int group_id; /*!< In which MCPWM group that the GPIO fault belongs to */
|
||||
int gpio_num; /*!< GPIO used by the fault signal */
|
||||
struct {
|
||||
uint32_t active_level: 1; /*!< On which level the fault signal is treated as active */
|
||||
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
|
||||
uint32_t pull_up: 1; /*!< Whether to pull up internally */
|
||||
uint32_t pull_down: 1; /*!< Whether to pull down internally */
|
||||
} flags; /*!< Extra configuration flags for GPIO fault */
|
||||
} mcpwm_gpio_fault_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create MCPWM GPIO fault
|
||||
*
|
||||
* @param[in] config MCPWM GPIO fault configuration
|
||||
* @param[out] ret_fault Returned GPIO fault handle
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM GPIO fault successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM GPIO fault failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM GPIO fault failed because out of memory
|
||||
* - ESP_ERR_NOT_FOUND: Create MCPWM GPIO fault failed because can't find free resource
|
||||
* - ESP_FAIL: Create MCPWM GPIO fault failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_gpio_fault(const mcpwm_gpio_fault_config_t *config, mcpwm_fault_handle_t *ret_fault);
|
||||
|
||||
/**
|
||||
* @brief MCPWM software fault configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
} mcpwm_soft_fault_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create MCPWM software fault
|
||||
*
|
||||
* @param[in] config MCPWM software fault configuration
|
||||
* @param[out] ret_fault Returned software fault handle
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM software fault successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM software fault failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM software fault failed because out of memory
|
||||
* - ESP_FAIL: Create MCPWM software fault failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_soft_fault(const mcpwm_soft_fault_config_t *config, mcpwm_fault_handle_t *ret_fault);
|
||||
|
||||
/**
|
||||
* @brief Delete MCPWM fault
|
||||
*
|
||||
* @param[in] fault MCPWM fault handle allocated by `mcpwm_new_gpio_fault()` or `mcpwm_new_soft_fault()`
|
||||
* @return
|
||||
* - ESP_OK: Delete MCPWM fault successfully
|
||||
* - ESP_ERR_INVALID_ARG: Delete MCPWM fault failed because of invalid argument
|
||||
* - ESP_FAIL: Delete MCPWM fault failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_del_fault(mcpwm_fault_handle_t fault);
|
||||
|
||||
/**
|
||||
* @brief Activate the software fault, trigger the fault event for once
|
||||
*
|
||||
* @param[in] fault MCPWM soft fault, allocated by `mcpwm_new_soft_fault()`
|
||||
* @return
|
||||
* - ESP_OK: Trigger MCPWM software fault event successfully
|
||||
* - ESP_ERR_INVALID_ARG: Trigger MCPWM software fault event failed because of invalid argument
|
||||
* - ESP_FAIL: Trigger MCPWM software fault event failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_soft_fault_activate(mcpwm_fault_handle_t fault);
|
||||
|
||||
/**
|
||||
* @brief Group of supported MCPWM fault event callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_fault_event_cb_t on_fault_enter; /*!< ISR callback function that would be invoked when fault signal becomes active */
|
||||
mcpwm_fault_event_cb_t on_fault_exit; /*!< ISR callback function that would be invoked when fault signal becomes inactive */
|
||||
} mcpwm_fault_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief Set event callbacks for MCPWM fault
|
||||
*
|
||||
* @param[in] fault MCPWM GPIO fault handle, allocated by `mcpwm_new_gpio_fault()`
|
||||
* @param[in] cbs Group of callback functions
|
||||
* @param[in] user_data User data, which will be passed to callback functions directly
|
||||
* @return
|
||||
* - ESP_OK: Set event callbacks successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
|
||||
* - ESP_FAIL: Set event callbacks failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_fault_register_event_callbacks(mcpwm_fault_handle_t fault, const mcpwm_fault_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
187
components/driver/include/driver/mcpwm_gen.h
Normal file
187
components/driver/include/driver/mcpwm_gen.h
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MCPWM generator configuration
|
||||
*/
|
||||
typedef struct {
|
||||
int gen_gpio_num; /*!< The GPIO number used to output the PWM signal */
|
||||
struct {
|
||||
uint32_t invert_pwm: 1; /*!< Whether to invert the PWM signal (done by GPIO matrix) */
|
||||
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
|
||||
} flags; /*!< Extra configuration flags for generator */
|
||||
} mcpwm_generator_config_t;
|
||||
|
||||
/**
|
||||
* @brief Allocate MCPWM generator from given operator
|
||||
*
|
||||
* @param[in] oper MCPWM operator, allocated by `mcpwm_new_operator()`
|
||||
* @param[in] config MCPWM generator configuration
|
||||
* @param[out] ret_gen Returned MCPWM generator
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM generator successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM generator failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM generator failed because out of memory
|
||||
* - ESP_ERR_NOT_FOUND: Create MCPWM generator failed because can't find free resource
|
||||
* - ESP_FAIL: Create MCPWM generator failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_generator(mcpwm_oper_handle_t oper, const mcpwm_generator_config_t *config, mcpwm_gen_handle_t *ret_gen);
|
||||
|
||||
/**
|
||||
* @brief Delete MCPWM generator
|
||||
*
|
||||
* @param[in] gen MCPWM generator handle, allocated by `mcpwm_new_generator()`
|
||||
* @return
|
||||
* - ESP_OK: Delete MCPWM generator successfully
|
||||
* - ESP_ERR_INVALID_ARG: Delete MCPWM generator failed because of invalid argument
|
||||
* - ESP_FAIL: Delete MCPWM generator failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_del_generator(mcpwm_gen_handle_t gen);
|
||||
|
||||
/**
|
||||
* @brief Set force level for MCPWM generator
|
||||
*
|
||||
* @note The force level will be applied to the generator immediately, regardless any other events that would change the generator's behaviour.
|
||||
* @note If the `hold_on` is true, the force level will retain forever, until user removes the force level by setting the force level to `-1`.
|
||||
* @note If the `hold_on` is false, the force level can be overridden by the next event action.
|
||||
*
|
||||
* @param[in] gen MCPWM generator handle, allocated by `mcpwm_new_generator()`
|
||||
* @param[in] level GPIO level to be applied to MCPWM generator, specially, -1 means to remove the force level
|
||||
* @param[in] hold_on Whether the forced PWM level should retain (i.e. will remain unchanged until manually remove the force level)
|
||||
* @return
|
||||
* - ESP_OK: Set force level for MCPWM generator successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set force level for MCPWM generator failed because of invalid argument
|
||||
* - ESP_FAIL: Set force level for MCPWM generator failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_generator_set_force_level(mcpwm_gen_handle_t gen, int level, bool hold_on);
|
||||
|
||||
/**
|
||||
* @brief Generator action on specific timer event
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_timer_direction_t direction; /*!< Timer direction */
|
||||
mcpwm_timer_event_t event; /*!< Timer event */
|
||||
mcpwm_generator_action_t action; /*!< Generator action should perform */
|
||||
} mcpwm_gen_timer_event_action_t;
|
||||
|
||||
/**
|
||||
* @brief Help macros to construct a mcpwm_gen_timer_event_action_t entry
|
||||
*/
|
||||
#define MCPWM_GEN_TIMER_EVENT_ACTION(dir, ev, act) \
|
||||
(mcpwm_gen_timer_event_action_t) { .direction = dir, .event = ev, .action = act }
|
||||
#define MCPWM_GEN_TIMER_EVENT_ACTION_END() \
|
||||
(mcpwm_gen_timer_event_action_t) { .event = MCPWM_TIMER_EVENT_INVALID }
|
||||
|
||||
/**
|
||||
* @brief Set generator actions on different MCPWM timer events
|
||||
*
|
||||
* @param[in] gen MCPWM generator handle, allocated by `mcpwm_new_generator()`
|
||||
* @param[in] ev_act MCPWM timer event action list, must be terminated by `MCPWM_GEN_TIMER_EVENT_ACTION_END()`
|
||||
* @return
|
||||
* - ESP_OK: Set generator actions successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set generator actions failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Set generator actions failed because of timer is not connected to operator
|
||||
* - ESP_FAIL: Set generator actions failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_generator_set_actions_on_timer_event(mcpwm_gen_handle_t gen, mcpwm_gen_timer_event_action_t ev_act, ...);
|
||||
|
||||
/**
|
||||
* @brief Generator action on specific comparator event
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_timer_direction_t direction; /*!< Timer direction */
|
||||
mcpwm_cmpr_handle_t comparator; /*!< Comparator handle */
|
||||
mcpwm_generator_action_t action; /*!< Generator action should perform */
|
||||
} mcpwm_gen_compare_event_action_t;
|
||||
|
||||
/**
|
||||
* @brief Help macros to construct a mcpwm_gen_compare_event_action_t entry
|
||||
*/
|
||||
#define MCPWM_GEN_COMPARE_EVENT_ACTION(dir, cmp, act) \
|
||||
(mcpwm_gen_compare_event_action_t) { .direction = dir, .comparator = cmp, .action = act }
|
||||
#define MCPWM_GEN_COMPARE_EVENT_ACTION_END() \
|
||||
(mcpwm_gen_compare_event_action_t) { .comparator = NULL }
|
||||
|
||||
/**
|
||||
* @brief Set generator actions on different MCPWM compare events
|
||||
*
|
||||
* @param[in] generator MCPWM generator handle, allocated by `mcpwm_new_generator()`
|
||||
* @param[in] ev_act MCPWM compare event action list, must be terminated by `MCPWM_GEN_COMPARE_EVENT_ACTION_END()`
|
||||
* @return
|
||||
* - ESP_OK: Set generator actions successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set generator actions failed because of invalid argument
|
||||
* - ESP_FAIL: Set generator actions failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_generator_set_actions_on_compare_event(mcpwm_gen_handle_t generator, mcpwm_gen_compare_event_action_t ev_act, ...);
|
||||
|
||||
/**
|
||||
* @brief Generator action on specific brake event
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_timer_direction_t direction; /*!< Timer direction */
|
||||
mcpwm_operator_brake_mode_t brake_mode; /*!< Brake mode */
|
||||
mcpwm_generator_action_t action; /*!< Generator action should perform */
|
||||
} mcpwm_gen_brake_event_action_t;
|
||||
|
||||
/**
|
||||
* @brief Help macros to construct a mcpwm_gen_brake_event_action_t entry
|
||||
*/
|
||||
#define MCPWM_GEN_BRAKE_EVENT_ACTION(dir, mode, act) \
|
||||
(mcpwm_gen_brake_event_action_t) { .direction = dir, .brake_mode = mode, .action = act }
|
||||
#define MCPWM_GEN_BRAKE_EVENT_ACTION_END() \
|
||||
(mcpwm_gen_brake_event_action_t) { .brake_mode = MCPWM_OPER_BRAKE_MODE_INVALID }
|
||||
|
||||
/**
|
||||
* @brief Set generator actions on different MCPWM brake events
|
||||
*
|
||||
* @param[in] generator MCPWM generator handle, allocated by `mcpwm_new_generator()`
|
||||
* @param[in] ev_act MCPWM brake event action list, must be terminated by `MCPWM_GEN_BRAKE_EVENT_ACTION_END()`
|
||||
* @return
|
||||
* - ESP_OK: Set generator actions successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set generator actions failed because of invalid argument
|
||||
* - ESP_FAIL: Set generator actions failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_generator_set_actions_on_brake_event(mcpwm_gen_handle_t generator, mcpwm_gen_brake_event_action_t ev_act, ...);
|
||||
|
||||
/**
|
||||
* @brief MCPWM dead time configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t posedge_delay_ticks; /*!< delay time applied to rising edge, 0 means no rising delay time */
|
||||
uint32_t negedge_delay_ticks; /*!< delay time applied to falling edge, 0 means no falling delay time */
|
||||
struct {
|
||||
uint32_t invert_output: 1; /*!< Invert the signal after applied the dead time */
|
||||
} flags; /*!< Extra flags for dead time configuration */
|
||||
} mcpwm_dead_time_config_t;
|
||||
|
||||
/**
|
||||
* @brief Set dead time for MCPWM generator
|
||||
*
|
||||
* @param[in] in_generator MCPWM generator, before adding the dead time
|
||||
* @param[in] out_generator MCPWM generator, after adding the dead time
|
||||
* @param[in] config MCPWM dead time configuration
|
||||
* @return
|
||||
* - ESP_OK: Set dead time for MCPWM generator successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set dead time for MCPWM generator failed because of invalid argument
|
||||
* - ESP_FAIL: Set dead time for MCPWM generator failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_generator_set_dead_time(mcpwm_gen_handle_t in_generator, mcpwm_gen_handle_t out_generator, const mcpwm_dead_time_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
159
components/driver/include/driver/mcpwm_oper.h
Normal file
159
components/driver/include/driver/mcpwm_oper.h
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MCPWM operator configuration
|
||||
*/
|
||||
typedef struct {
|
||||
int group_id; /*!< Specify from which group to allocate the MCPWM operator */
|
||||
struct {
|
||||
uint32_t update_gen_action_on_tez: 1; /*!< Whether to update generator action when timer counts to zero */
|
||||
uint32_t update_gen_action_on_tep: 1; /*!< Whether to update generator action when timer counts to peak */
|
||||
uint32_t update_gen_action_on_sync: 1; /*!< Whether to update generator action on sync event */
|
||||
uint32_t update_dead_time_on_tez: 1; /*!< Whether to update dead time when timer counts to zero */
|
||||
uint32_t update_dead_time_on_tep: 1; /*!< Whether to update dead time when timer counts to peak */
|
||||
uint32_t update_dead_time_on_sync: 1; /*!< Whether to update dead time on sync event */
|
||||
} flags; /*!< Extra configuration flags for operator */
|
||||
} mcpwm_operator_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create MCPWM operator
|
||||
*
|
||||
* @param[in] config MCPWM operator configuration
|
||||
* @param[out] ret_oper Returned MCPWM operator handle
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM operator successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM operator failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM operator failed because out of memory
|
||||
* - ESP_ERR_NOT_FOUND: Create MCPWM operator failed because can't find free resource
|
||||
* - ESP_FAIL: Create MCPWM operator failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_operator(const mcpwm_operator_config_t *config, mcpwm_oper_handle_t *ret_oper);
|
||||
|
||||
/**
|
||||
* @brief Delete MCPWM operator
|
||||
*
|
||||
* @param[in] oper MCPWM operator, allocated by `mcpwm_new_operator()`
|
||||
* @return
|
||||
* - ESP_OK: Delete MCPWM operator successfully
|
||||
* - ESP_ERR_INVALID_ARG: Delete MCPWM operator failed because of invalid argument
|
||||
* - ESP_FAIL: Delete MCPWM operator failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_del_operator(mcpwm_oper_handle_t oper);
|
||||
|
||||
/**
|
||||
* @brief Connect MCPWM operator and timer, so that the operator can be driven by the timer
|
||||
*
|
||||
* @param[in] oper MCPWM operator handle, allocated by `mcpwm_new_operator()`
|
||||
* @param[in] timer MCPWM timer handle, allocated by `mcpwm_new_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Connect MCPWM operator and timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Connect MCPWM operator and timer failed because of invalid argument
|
||||
* - ESP_FAIL: Connect MCPWM operator and timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_operator_connect_timer(mcpwm_oper_handle_t oper, mcpwm_timer_handle_t timer);
|
||||
|
||||
/**
|
||||
* @brief MCPWM brake configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_fault_handle_t fault; /*!< Which fault causes the operator to brake */
|
||||
mcpwm_operator_brake_mode_t brake_mode; /*!< Brake mode */
|
||||
struct {
|
||||
uint32_t cbc_recover_on_tez: 1; /*!< Recovery CBC brake state on tez event */
|
||||
uint32_t cbc_recover_on_tep: 1; /*!< Recovery CBC brake state on tep event */
|
||||
} flags; /*!< Extra flags for brake configuration */
|
||||
} mcpwm_brake_config_t;
|
||||
|
||||
/**
|
||||
* @brief Set brake method for MCPWM operator
|
||||
*
|
||||
* @param[in] operator MCPWM operator, allocated by `mcpwm_new_operator()`
|
||||
* @param[in] config MCPWM brake configuration
|
||||
* @return
|
||||
* - ESP_OK: Set trip for operator successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set trip for operator failed because of invalid argument
|
||||
* - ESP_FAIL: Set trip for operator failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_operator_set_brake_on_fault(mcpwm_oper_handle_t operator, const mcpwm_brake_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Try to make the operator recover from fault
|
||||
*
|
||||
* @note To recover from fault or escape from trip, you make sure the fault signal has dissappeared already.
|
||||
* Otherwise the recovery can't succeed.
|
||||
*
|
||||
* @param[in] operator MCPWM operator, allocated by `mcpwm_new_operator()`
|
||||
* @param[in] fault MCPWM fault handle
|
||||
* @return
|
||||
* - ESP_OK: Recover from fault successfully
|
||||
* - ESP_ERR_INVALID_ARG: Recover from fault failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Recover from fault failed because the fault source is still active
|
||||
* - ESP_FAIL: Recover from fault failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_operator_recover_from_fault(mcpwm_oper_handle_t operator, mcpwm_fault_handle_t fault);
|
||||
|
||||
/**
|
||||
* @brief Group of supported MCPWM operator event callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_brake_event_cb_t on_brake_cbc; /*!< callback function when mcpwm operator brakes in CBC */
|
||||
mcpwm_brake_event_cb_t on_brake_ost; /*!< callback function when mcpwm operator brakes in OST */
|
||||
} mcpwm_operator_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief Set event callbacks for MCPWM operator
|
||||
*
|
||||
* @param[in] oper MCPWM operator handle, allocated by `mcpwm_new_operator()`
|
||||
* @param[in] cbs Group of callback functions
|
||||
* @param[in] user_data User data, which will be passed to callback functions directly
|
||||
* @return
|
||||
* - ESP_OK: Set event callbacks successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
|
||||
* - ESP_FAIL: Set event callbacks failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_operator_register_event_callbacks(mcpwm_oper_handle_t oper, const mcpwm_operator_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief MCPWM carrier configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t frequency_hz; /*!< Carrier frequency in Hz */
|
||||
uint32_t first_pulse_duration_us; /*!< The duration of the first PWM pulse, in us */
|
||||
float duty_cycle; /*!< Carrier duty cycle */
|
||||
struct {
|
||||
uint32_t invert_before_modulate: 1; /*!< Invert the raw signal */
|
||||
uint32_t invert_after_modulate: 1; /*!< Invert the modulated signal */
|
||||
} flags; /*!< Extra flags for carrier configuration */
|
||||
} mcpwm_carrier_config_t;
|
||||
|
||||
/**
|
||||
* @brief Apply carrier feature for MCPWM operator
|
||||
*
|
||||
* @param[in] oper MCPWM operator, allocated by `mcpwm_new_operator()`
|
||||
* @param[in] config MCPWM carrier specific configuration
|
||||
* @return
|
||||
* - ESP_OK: Set carrier for operator successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set carrier for operator failed because of invalid argument
|
||||
* - ESP_FAIL: Set carrier for operator failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_operator_apply_carrier(mcpwm_oper_handle_t oper, const mcpwm_carrier_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
20
components/driver/include/driver/mcpwm_prelude.h
Normal file
20
components/driver/include/driver/mcpwm_prelude.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief MCPWM peripheral contains many submodules, whose drivers are scattered in different header files.
|
||||
* This header file serves as a prelude, contains every thing that is needed to work with the MCPWM peripheral.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "driver/mcpwm_timer.h"
|
||||
#include "driver/mcpwm_oper.h"
|
||||
#include "driver/mcpwm_cmpr.h"
|
||||
#include "driver/mcpwm_gen.h"
|
||||
#include "driver/mcpwm_fault.h"
|
||||
#include "driver/mcpwm_sync.h"
|
||||
#include "driver/mcpwm_cap.h"
|
114
components/driver/include/driver/mcpwm_sync.h
Normal file
114
components/driver/include/driver/mcpwm_sync.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MCPWM timer sync source configuration
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_timer_event_t timer_event; /*!< Timer event, upon which MCPWM timer will generate the sync signal */
|
||||
struct {
|
||||
uint32_t propagate_input_sync: 1; /*!< The input sync signal would be routed to its sync output */
|
||||
} flags; /*!< Extra configuration flags for timer sync source */
|
||||
} mcpwm_timer_sync_src_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create MCPWM timer sync source
|
||||
*
|
||||
* @param[in] timer MCPWM timer handle, allocated by `mcpwm_new_timer()`
|
||||
* @param[in] config MCPWM timer sync source configuration
|
||||
* @param[out] ret_sync Returned MCPWM sync handle
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM timer sync source successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM timer sync source failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM timer sync source failed because out of memory
|
||||
* - ESP_ERR_INVALID_STATE: Create MCPWM timer sync source failed because the timer has created a sync source before
|
||||
* - ESP_FAIL: Create MCPWM timer sync source failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_timer_sync_src(mcpwm_timer_handle_t timer, const mcpwm_timer_sync_src_config_t *config, mcpwm_sync_handle_t *ret_sync);
|
||||
|
||||
/**
|
||||
* @brief MCPWM GPIO sync source configuration
|
||||
*/
|
||||
typedef struct {
|
||||
int group_id; /*!< MCPWM group ID */
|
||||
int gpio_num; /*!< GPIO used by sync source */
|
||||
struct {
|
||||
uint32_t active_neg: 1; /*!< Whether the sync signal is active on negedge, by default, the sync signal's posedge is treated as active */
|
||||
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
|
||||
uint32_t pull_up: 1; /*!< Whether to pull up internally */
|
||||
uint32_t pull_down: 1; /*!< Whether to pull down internally */
|
||||
} flags; /*!< Extra configuration flags for GPIO sync source */
|
||||
} mcpwm_gpio_sync_src_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create MCPWM GPIO sync source
|
||||
*
|
||||
* @param[in] config MCPWM GPIO sync source configuration
|
||||
* @param[out] ret_sync Returned MCPWM GPIO sync handle
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM GPIO sync source successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM GPIO sync source failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM GPIO sync source failed because out of memory
|
||||
* - ESP_ERR_NOT_FOUND: Create MCPWM GPIO sync source failed because can't find free resource
|
||||
* - ESP_FAIL: Create MCPWM GPIO sync source failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_gpio_sync_src(const mcpwm_gpio_sync_src_config_t *config, mcpwm_sync_handle_t *ret_sync);
|
||||
|
||||
/**
|
||||
* @brief MCPWM software sync configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
} mcpwm_soft_sync_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create MCPWM software sync source
|
||||
*
|
||||
* @param[in] config MCPWM software sync source configuration
|
||||
* @param[out] ret_sync Returned software sync handle
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM software sync successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM software sync failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM software sync failed because out of memory
|
||||
* - ESP_FAIL: Create MCPWM software sync failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_soft_sync_src(const mcpwm_soft_sync_config_t *config, mcpwm_sync_handle_t *ret_sync);
|
||||
|
||||
/**
|
||||
* @brief Delete MCPWM sync source
|
||||
*
|
||||
* @param[in] sync MCPWM sync handle, allocated by `mcpwm_new_timer_sync_src()` or `mcpwm_new_gpio_sync_src()` or `mcpwm_new_soft_sync_src()`
|
||||
* @return
|
||||
* - ESP_OK: Delete MCPWM sync source successfully
|
||||
* - ESP_ERR_INVALID_ARG: Delete MCPWM sync source failed because of invalid argument
|
||||
* - ESP_FAIL: Delete MCPWM sync source failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_del_sync_src(mcpwm_sync_handle_t sync);
|
||||
|
||||
/**
|
||||
* @brief Activate the software sync, trigger the sync event for once
|
||||
*
|
||||
* @param[in] sync MCPWM soft sync handle, allocated by `mcpwm_new_soft_sync_src()`
|
||||
* @return
|
||||
* - ESP_OK: Trigger MCPWM software sync event successfully
|
||||
* - ESP_ERR_INVALID_ARG: Trigger MCPWM software sync event failed because of invalid argument
|
||||
* - ESP_FAIL: Trigger MCPWM software sync event failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_soft_sync_activate(mcpwm_sync_handle_t sync);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
144
components/driver/include/driver/mcpwm_timer.h
Normal file
144
components/driver/include/driver/mcpwm_timer.h
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Group of supported MCPWM timer event callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_timer_event_cb_t on_full; /*!< callback function when MCPWM timer counts to peak value */
|
||||
mcpwm_timer_event_cb_t on_empty; /*!< callback function when MCPWM timer counts to zero */
|
||||
mcpwm_timer_event_cb_t on_stop; /*!< callback function when MCPWM timer stops */
|
||||
} mcpwm_timer_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief MCPWM timer configuration
|
||||
*/
|
||||
typedef struct {
|
||||
int group_id; /*!< Specify from which group to allocate the MCPWM timer */
|
||||
mcpwm_timer_clock_source_t clk_src; /*!< MCPWM timer clock source */
|
||||
uint32_t resolution_hz; /*!< Counter resolution in Hz, ranges from around 300KHz to 80MHz.
|
||||
The step size of each count tick equals to (1 / resolution_hz) seconds */
|
||||
mcpwm_timer_count_mode_t count_mode; /*!< Count mode */
|
||||
uint32_t period_ticks; /*!< Number of count ticks within a period */
|
||||
struct {
|
||||
uint32_t update_period_on_empty: 1; /*!< Whether to update period when timer counts to zero */
|
||||
uint32_t update_period_on_sync: 1; /*!< Whether to update period on sync event */
|
||||
} flags; /*!< Extra configuration flags for timer */
|
||||
} mcpwm_timer_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create MCPWM timer
|
||||
*
|
||||
* @param[in] config MCPWM timer configuration
|
||||
* @param[out] ret_timer Returned MCPWM timer handle
|
||||
* @return
|
||||
* - ESP_OK: Create MCPWM timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Create MCPWM timer failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create MCPWM timer failed because out of memory
|
||||
* - ESP_ERR_NOT_FOUND: Create MCPWM timer failed because all hardware timers are used up and no more free one
|
||||
* - ESP_FAIL: Create MCPWM timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle_t *ret_timer);
|
||||
|
||||
/**
|
||||
* @brief Delete MCPWM timer
|
||||
*
|
||||
* @param[in] timer MCPWM timer handle, allocated by `mcpwm_new_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Delete MCPWM timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Delete MCPWM timer failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Delete MCPWM timer failed because timer is not in init state
|
||||
* - ESP_FAIL: Delete MCPWM timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_del_timer(mcpwm_timer_handle_t timer);
|
||||
|
||||
/**
|
||||
* @brief Enable MCPWM timer
|
||||
*
|
||||
* @param[in] timer MCPWM timer handle, allocated by `mcpwm_new_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Enable MCPWM timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Enable MCPWM timer failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Enable MCPWM timer failed because timer is enabled already
|
||||
* - ESP_FAIL: Enable MCPWM timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_timer_enable(mcpwm_timer_handle_t timer);
|
||||
|
||||
/**
|
||||
* @brief Disable MCPWM timer
|
||||
*
|
||||
* @param[in] timer MCPWM timer handle, allocated by `mcpwm_new_timer()`
|
||||
* @return
|
||||
* - ESP_OK: Disable MCPWM timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Disable MCPWM timer failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Disable MCPWM timer failed because timer is disabled already
|
||||
* - ESP_FAIL: Disable MCPWM timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_timer_disable(mcpwm_timer_handle_t timer);
|
||||
|
||||
/**
|
||||
* @brief Send specific start/stop commands to MCPWM timer
|
||||
*
|
||||
* @param[in] timer MCPWM timer handle, allocated by `mcpwm_new_timer()`
|
||||
* @param[in] command Supported command list for MCPWM timer
|
||||
* @return
|
||||
* - ESP_OK: Start or stop MCPWM timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Start or stop MCPWM timer failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Start or stop MCPWM timer failed because timer is not enabled
|
||||
* - ESP_FAIL: Start or stop MCPWM timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_timer_start_stop(mcpwm_timer_handle_t timer, mcpwm_timer_start_stop_cmd_t command);
|
||||
|
||||
/**
|
||||
* @brief Set event callbacks for MCPWM timer
|
||||
*
|
||||
* @param[in] timer MCPWM timer handle, allocated by `mcpwm_new_timer()`
|
||||
* @param[in] cbs Group of callback functions
|
||||
* @param[in] user_data User data, which will be passed to callback functions directly
|
||||
* @return
|
||||
* - ESP_OK: Set event callbacks successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Set event callbacks failed because timer is not in init state
|
||||
* - ESP_FAIL: Set event callbacks failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_timer_register_event_callbacks(mcpwm_timer_handle_t timer, const mcpwm_timer_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief MCPWM Timer sync phase configuration
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_sync_handle_t sync_src; /*!< The sync event source. Set to NULL will disable the timer being synced by others */
|
||||
uint32_t count_value; /*!< The count value that should lock to upon sync event */
|
||||
mcpwm_timer_direction_t direction; /*!< The count direction that should lock to upon sync event */
|
||||
} mcpwm_timer_sync_phase_config_t;
|
||||
|
||||
/**
|
||||
* @brief Set sync phase for MCPWM timer
|
||||
*
|
||||
* @param[in] timer MCPWM timer handle, allocated by `mcpwm_new_timer()`
|
||||
* @param[in] config MCPWM timer sync phase configuration
|
||||
* @return
|
||||
* - ESP_OK: Set sync phase for MCPWM timer successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set sync phase for MCPWM timer failed because of invalid argument
|
||||
* - ESP_FAIL: Set sync phase for MCPWM timer failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_timer_set_phase_on_sync(mcpwm_timer_handle_t timer, const mcpwm_timer_sync_phase_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
145
components/driver/include/driver/mcpwm_types.h
Normal file
145
components/driver/include/driver/mcpwm_types.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "hal/mcpwm_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of MCPWM timer handle
|
||||
*/
|
||||
typedef struct mcpwm_timer_t *mcpwm_timer_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of MCPWM operator handle
|
||||
*/
|
||||
typedef struct mcpwm_oper_t *mcpwm_oper_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of MCPWM comparator handle
|
||||
*/
|
||||
typedef struct mcpwm_cmpr_t *mcpwm_cmpr_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of MCPWM generator handle
|
||||
*/
|
||||
typedef struct mcpwm_gen_t *mcpwm_gen_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of MCPWM fault handle
|
||||
*/
|
||||
typedef struct mcpwm_fault_t *mcpwm_fault_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of MCPWM sync handle
|
||||
*/
|
||||
typedef struct mcpwm_sync_t *mcpwm_sync_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of MCPWM capture timer handle
|
||||
*/
|
||||
typedef struct mcpwm_cap_timer_t *mcpwm_cap_timer_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of MCPWM capture channel handle
|
||||
*/
|
||||
typedef struct mcpwm_cap_channel_t *mcpwm_cap_channel_handle_t;
|
||||
|
||||
/**
|
||||
* @brief MCPWM timer event data
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t count_value; /*!< MCPWM timer count value */
|
||||
mcpwm_timer_direction_t direction; /*!< MCPWM timer count direction */
|
||||
} mcpwm_timer_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief MCPWM timer event callback function
|
||||
*
|
||||
* @param[in] timer MCPWM timer handle
|
||||
* @param[in] edata MCPWM timer event data, fed by driver
|
||||
* @param[in] user_ctx User data, set in `mcpwm_timer_register_event_callbacks()`
|
||||
* @return Whether a high priority task has been waken up by this function
|
||||
*/
|
||||
typedef bool (*mcpwm_timer_event_cb_t)(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief MCPWM brake event data
|
||||
*/
|
||||
typedef struct {
|
||||
} mcpwm_brake_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief MCPWM operator brake event callback function
|
||||
*
|
||||
* @param[in] operator MCPWM operator handle
|
||||
* @param[in] edata MCPWM brake event data, fed by driver
|
||||
* @param[in] user_ctx User data, set in `mcpwm_operator_register_event_callbacks()`
|
||||
* @return Whether a high priority task has been waken up by this function
|
||||
*/
|
||||
typedef bool (*mcpwm_brake_event_cb_t)(mcpwm_oper_handle_t operator, const mcpwm_brake_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief MCPWM fault event data
|
||||
*/
|
||||
typedef struct {
|
||||
} mcpwm_fault_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief MCPWM fault event callback function
|
||||
*
|
||||
* @param fault MCPWM fault handle
|
||||
* @param ev_data MCPWM fault event data, fed by driver
|
||||
* @param user_ctx User data, set in `mcpwm_fault_register_event_callbacks()`
|
||||
* @return whether a task switch is needed after the callback returns
|
||||
*/
|
||||
typedef bool (*mcpwm_fault_event_cb_t)(mcpwm_fault_handle_t fault, const mcpwm_fault_event_data_t *ev_data, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief MCPWM compare event data
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t compare_ticks; /*!< Compare value */
|
||||
mcpwm_timer_direction_t direction; /*!< Count direction */
|
||||
} mcpwm_compare_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief MCPWM comparator event callback function
|
||||
*
|
||||
* @param comparator MCPWM comparator handle
|
||||
* @param edata MCPWM comparator event data, fed by driver
|
||||
* @param user_ctx User data, set in `mcpwm_comparator_register_event_callbacks()`
|
||||
* @return Whether a high priority task has been waken up by this function
|
||||
*/
|
||||
typedef bool (*mcpwm_compare_event_cb_t)(mcpwm_cmpr_handle_t comparator, const mcpwm_compare_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief MCPWM capture event data
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t cap_value; /*!< Captured value */
|
||||
mcpwm_capture_edge_t cap_edge; /*!< Capture edge */
|
||||
} mcpwm_capture_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief MCPWM capture event callback function
|
||||
*
|
||||
* @param cap_channel MCPWM capture channel handle
|
||||
* @param ev_data MCPWM capture event data, fed by driver
|
||||
* @param user_ctx User data, set in `mcpwm_capture_channel_register_event_callbacks()`
|
||||
* @return Whether a high priority task has been waken up by this function
|
||||
*/
|
||||
typedef bool (*mcpwm_capture_event_cb_t)(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_data_t *edata, void *user_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
36
components/driver/include/esp_private/mcpwm.h
Normal file
36
components/driver/include/esp_private/mcpwm.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// DO NOT USE THESE APIS IN YOUR APPLICATIONS
|
||||
// The following APIs are for internal use, public to other IDF components, but not for users' applications.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get MCPWM timer phase
|
||||
*
|
||||
* @param[in] timer MCPWM timer handle, allocated by `mcpwm_new_timer()`
|
||||
* @param[out] count_value Returned MCPWM timer phase
|
||||
* @param[out] direction Returned MCPWM timer counting direction
|
||||
* @return
|
||||
* - ESP_OK: Get MCPWM timer status successfully
|
||||
* - ESP_ERR_INVALID_ARG: Get MCPWM timer status failed because of invalid argument
|
||||
* - ESP_FAIL: Get MCPWM timer status failed because of other error
|
||||
*/
|
||||
esp_err_t mcpwm_timer_get_phase(mcpwm_timer_handle_t timer, uint32_t *count_value, mcpwm_timer_direction_t *direction);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
420
components/driver/mcpwm/mcpwm_cap.c
Normal file
420
components/driver/mcpwm/mcpwm_cap.c
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/mcpwm_cap.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static void mcpwm_capture_default_isr(void *args);
|
||||
|
||||
static esp_err_t mcpwm_cap_timer_register_to_group(mcpwm_cap_timer_t *cap_timer, int group_id)
|
||||
{
|
||||
mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id);
|
||||
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id);
|
||||
|
||||
bool new_timer = false;
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
if (!group->cap_timer) {
|
||||
group->cap_timer = cap_timer;
|
||||
new_timer = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
if (!new_timer) {
|
||||
mcpwm_release_group_handle(group);
|
||||
group = NULL;
|
||||
} else {
|
||||
cap_timer->group = group;
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(new_timer, ESP_ERR_NOT_FOUND, TAG, "no free cap timer in group (%d)", group_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mcpwm_cap_timer_unregister_from_group(mcpwm_cap_timer_t *cap_timer)
|
||||
{
|
||||
mcpwm_group_t *group = cap_timer->group;
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
group->cap_timer = NULL;
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
// capture timer has a reference on group, release it now
|
||||
mcpwm_release_group_handle(group);
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_cap_timer_destory(mcpwm_cap_timer_t *cap_timer)
|
||||
{
|
||||
if (cap_timer->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(cap_timer->pm_lock), TAG, "delete pm_lock failed");
|
||||
}
|
||||
if (cap_timer->group) {
|
||||
mcpwm_cap_timer_unregister_from_group(cap_timer);
|
||||
}
|
||||
free(cap_timer);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mcpwm_cap_timer_handle_t *ret_cap_timer)
|
||||
{
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_cap_timer_t *cap_timer = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_cap_timer, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->group_id < SOC_MCPWM_GROUPS && config->group_id >= 0, ESP_ERR_INVALID_ARG,
|
||||
err, TAG, "invalid group ID:%d", config->group_id);
|
||||
|
||||
cap_timer = heap_caps_calloc(1, sizeof(mcpwm_cap_timer_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(cap_timer, ESP_ERR_NO_MEM, err, TAG, "no mem for capture timer");
|
||||
|
||||
switch (config->clk_src) {
|
||||
case MCPWM_CAPTURE_CLK_SRC_APB:
|
||||
cap_timer->resolution_hz = esp_clk_apb_freq();
|
||||
#if CONFIG_PM_ENABLE
|
||||
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "mcpwm_cap_timer", &cap_timer->pm_lock);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "create ESP_PM_APB_FREQ_MAX lock failed");
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid clock source:%d", config->clk_src);
|
||||
}
|
||||
|
||||
ESP_GOTO_ON_ERROR(mcpwm_cap_timer_register_to_group(cap_timer, config->group_id), err, TAG, "register timer failed");
|
||||
mcpwm_group_t *group = cap_timer->group;
|
||||
int group_id = group->group_id;
|
||||
|
||||
// fill in other capture timer specific members
|
||||
cap_timer->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
cap_timer->fsm = MCPWM_CAP_TIMER_FSM_INIT;
|
||||
*ret_cap_timer = cap_timer;
|
||||
ESP_LOGD(TAG, "new capture timer at %p, in group (%d)", cap_timer, group_id);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (cap_timer) {
|
||||
mcpwm_cap_timer_destory(cap_timer);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_del_capture_timer(mcpwm_cap_timer_handle_t cap_timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cap_timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(cap_timer->fsm == MCPWM_CAP_TIMER_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "timer not in init state");
|
||||
for (int i = 0; i < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER; i++) {
|
||||
ESP_RETURN_ON_FALSE(!cap_timer->cap_channels[i], ESP_ERR_INVALID_STATE, TAG, "cap channel still in working");
|
||||
}
|
||||
mcpwm_group_t *group = cap_timer->group;
|
||||
|
||||
ESP_LOGD(TAG, "del capture timer in group %d", group->group_id);
|
||||
// recycle memory resource
|
||||
ESP_RETURN_ON_ERROR(mcpwm_cap_timer_destory(cap_timer), TAG, "destory capture timer failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_capture_timer_enable(mcpwm_cap_timer_handle_t cap_timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cap_timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(cap_timer->fsm == MCPWM_CAP_TIMER_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "timer not in init state");
|
||||
if (cap_timer->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(cap_timer->pm_lock), TAG, "acquire pm_lock failed");
|
||||
}
|
||||
cap_timer->fsm = MCPWM_CAP_TIMER_FSM_ENABLE;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_capture_timer_disable(mcpwm_cap_timer_handle_t cap_timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cap_timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(cap_timer->fsm == MCPWM_CAP_TIMER_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "timer not in enable state");
|
||||
if (cap_timer->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_release(cap_timer->pm_lock), TAG, "release pm_lock failed");
|
||||
}
|
||||
cap_timer->fsm = MCPWM_CAP_TIMER_FSM_INIT;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_capture_timer_start(mcpwm_cap_timer_handle_t cap_timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cap_timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(cap_timer->fsm == MCPWM_CAP_TIMER_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "timer not enabled yet");
|
||||
mcpwm_group_t *group = cap_timer->group;
|
||||
|
||||
portENTER_CRITICAL_SAFE(&cap_timer->spinlock);
|
||||
mcpwm_ll_capture_enable_timer(group->hal.dev, true);
|
||||
portEXIT_CRITICAL_SAFE(&cap_timer->spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_capture_timer_stop(mcpwm_cap_timer_handle_t cap_timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cap_timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(cap_timer->fsm == MCPWM_CAP_TIMER_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "timer not enabled yet");
|
||||
mcpwm_group_t *group = cap_timer->group;
|
||||
|
||||
portENTER_CRITICAL_SAFE(&cap_timer->spinlock);
|
||||
mcpwm_ll_capture_enable_timer(group->hal.dev, false);
|
||||
portEXIT_CRITICAL_SAFE(&cap_timer->spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_capture_channel_register_to_timer(mcpwm_cap_channel_t *cap_channel, mcpwm_cap_timer_t *cap_timer)
|
||||
{
|
||||
int cap_chan_id = -1;
|
||||
portENTER_CRITICAL(&cap_timer->spinlock);
|
||||
for (int i = 0; i < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER; i++) {
|
||||
if (!cap_timer->cap_channels[i]) {
|
||||
cap_timer->cap_channels[i] = cap_channel;
|
||||
cap_chan_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&cap_timer->spinlock);
|
||||
ESP_RETURN_ON_FALSE(cap_chan_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free channel in the timer (%d)", cap_timer->group->group_id);
|
||||
|
||||
cap_channel->cap_chan_id = cap_chan_id;
|
||||
cap_channel->cap_timer = cap_timer;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mcpwm_capture_channel_unregister_from_timer(mcpwm_cap_channel_t *cap_chan)
|
||||
{
|
||||
mcpwm_cap_timer_t *cap_timer = cap_chan->cap_timer;
|
||||
int cap_chan_id = cap_chan->cap_chan_id;
|
||||
|
||||
portENTER_CRITICAL(&cap_timer->spinlock);
|
||||
cap_timer->cap_channels[cap_chan_id] = NULL;
|
||||
portEXIT_CRITICAL(&cap_timer->spinlock);
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_capture_channel_destory(mcpwm_cap_channel_t *cap_chan)
|
||||
{
|
||||
if (cap_chan->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_free(cap_chan->intr), TAG, "delete interrupt service failed");
|
||||
}
|
||||
if (cap_chan->cap_timer) {
|
||||
mcpwm_capture_channel_unregister_from_timer(cap_chan);
|
||||
}
|
||||
free(cap_chan);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_capture_channel(mcpwm_cap_timer_handle_t cap_timer, const mcpwm_capture_channel_config_t *config, mcpwm_cap_channel_handle_t *ret_cap_channel)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_cap_channel_t *cap_chan = NULL;
|
||||
ESP_GOTO_ON_FALSE(cap_timer && config && ret_cap_channel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->prescale && config->prescale <= MCPWM_LL_MAX_CAPTURE_PRESCALE, ESP_ERR_INVALID_ARG, err, TAG, "invalid prescale");
|
||||
|
||||
// create instance firstly, then install onto platform
|
||||
cap_chan = calloc(1, sizeof(mcpwm_cap_channel_t));
|
||||
ESP_GOTO_ON_FALSE(cap_chan, ESP_ERR_NO_MEM, err, TAG, "no mem for capture channel");
|
||||
|
||||
ESP_GOTO_ON_ERROR(mcpwm_capture_channel_register_to_timer(cap_chan, cap_timer), err, TAG, "register channel failed");
|
||||
mcpwm_group_t *group = cap_timer->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int cap_chan_id = cap_chan->cap_chan_id;
|
||||
|
||||
mcpwm_ll_capture_enable_channel(hal->dev, cap_chan_id, true); // enable channel
|
||||
mcpwm_ll_capture_enable_negedge(hal->dev, cap_chan_id, config->flags.neg_edge);
|
||||
mcpwm_ll_capture_enable_posedge(hal->dev, cap_chan_id, config->flags.pos_edge);
|
||||
mcpwm_ll_invert_input(hal->dev, cap_chan_id, config->flags.invert_cap_signal);
|
||||
mcpwm_ll_capture_set_prescale(hal->dev, cap_chan_id, config->prescale);
|
||||
|
||||
if (config->gpio_num >= 0) {
|
||||
// GPIO configuration
|
||||
gpio_config_t gpio_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
|
||||
.pin_bit_mask = (1ULL << config->gpio_num),
|
||||
.pull_down_en = config->flags.pull_down,
|
||||
.pull_up_en = config->flags.pull_up,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config capture GPIO failed");
|
||||
esp_rom_gpio_connect_in_signal(config->gpio_num, mcpwm_periph_signals.groups[group->group_id].captures[cap_chan_id].cap_sig, 0);
|
||||
}
|
||||
|
||||
cap_chan->gpio_num = config->gpio_num;
|
||||
*ret_cap_channel = cap_chan;
|
||||
ESP_LOGD(TAG, "new capture channel (%d,%d) at %p", group->group_id, cap_chan_id, cap_chan);
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (cap_chan) {
|
||||
mcpwm_capture_channel_destory(cap_chan);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_del_capture_channel(mcpwm_cap_channel_handle_t cap_channel)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cap_channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_cap_timer_t *cap_timer = cap_channel->cap_timer;
|
||||
mcpwm_group_t *group = cap_timer->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int cap_chan_id = cap_channel->cap_chan_id;
|
||||
|
||||
ESP_LOGD(TAG, "del capture channel (%d,%d)", group->group_id, cap_channel->cap_chan_id);
|
||||
if (cap_channel->gpio_num >= 0) {
|
||||
gpio_reset_pin(cap_channel->gpio_num);
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_CAPTURE(cap_chan_id), false);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, MCPWM_LL_EVENT_CAPTURE(cap_chan_id));
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
// disable capture channel
|
||||
mcpwm_ll_capture_enable_channel(group->hal.dev, cap_channel->cap_chan_id, false);
|
||||
|
||||
// recycle memory resource
|
||||
ESP_RETURN_ON_ERROR(mcpwm_capture_channel_destory(cap_channel), TAG, "destory capture channel failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_capture_channel_register_event_callbacks(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cap_channel && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_group_t *group = cap_channel->cap_timer->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int group_id = group->group_id;
|
||||
int cap_chan_id = cap_channel->cap_chan_id;
|
||||
|
||||
#if CONFIG_MCWPM_ISR_IRAM_SAFE
|
||||
if (cbs->on_cap) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_cap), ESP_ERR_INVALID_ARG, TAG, "on_cap callback not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
// lazy install interrupt service
|
||||
if (!cap_channel->intr) {
|
||||
// we want the interrupt servie to be enabled after allocation successfully
|
||||
int isr_flags = MCPWM_INTR_ALLOC_FLAG & ~ ESP_INTR_FLAG_INTRDISABLED;
|
||||
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(mcpwm_periph_signals.groups[group_id].irq_id, isr_flags,
|
||||
(uint32_t)mcpwm_ll_intr_get_status_reg(hal->dev), MCPWM_LL_EVENT_CAPTURE(cap_chan_id),
|
||||
mcpwm_capture_default_isr, cap_channel, &cap_channel->intr), TAG, "install interrupt service for cap channel failed");
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_CAPTURE(cap_chan_id), cbs->on_cap != NULL);
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
cap_channel->on_cap = cbs->on_cap;
|
||||
cap_channel->user_data = user_data;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_capture_channel_trigger_soft_catch(mcpwm_cap_channel_handle_t cap_channel)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cap_channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_cap_timer_t *cap_timer = cap_channel->cap_timer;
|
||||
mcpwm_group_t *group = cap_timer->group;
|
||||
|
||||
// note: soft capture can also triggers the interrupt routine
|
||||
mcpwm_ll_trigger_soft_capture(group->hal.dev, cap_channel->cap_chan_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_capture_timer_set_phase_on_sync(mcpwm_cap_timer_handle_t cap_timer, const mcpwm_capture_timer_sync_phase_config_t *config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cap_timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
// capture timer only support count up
|
||||
ESP_RETURN_ON_FALSE(config->direction == MCPWM_TIMER_DIRECTION_UP, ESP_ERR_INVALID_ARG, TAG, "invalid sync direction");
|
||||
mcpwm_group_t *group = cap_timer->group;
|
||||
mcpwm_sync_t *sync_source = config->sync_src;
|
||||
|
||||
// a non-NULL sync_src means to enable sync feature
|
||||
if (sync_source) {
|
||||
switch (sync_source->type) {
|
||||
case MCPWM_SYNC_TYPE_GPIO: {
|
||||
ESP_RETURN_ON_FALSE(group == sync_source->group, ESP_ERR_INVALID_ARG, TAG, "capture timer and sync source are not in the same group");
|
||||
mcpwm_gpio_sync_src_t *gpio_sync_src = __containerof(sync_source, mcpwm_gpio_sync_src_t, base);
|
||||
mcpwm_ll_capture_set_gpio_sync(group->hal.dev, gpio_sync_src->sync_id);
|
||||
ESP_LOGD(TAG, "enable sync to GPIO (%d,%d) for cap timer (%d)",
|
||||
group->group_id, gpio_sync_src->sync_id, group->group_id);
|
||||
break;
|
||||
}
|
||||
case MCPWM_SYNC_TYPE_TIMER: {
|
||||
ESP_RETURN_ON_FALSE(group == sync_source->group, ESP_ERR_INVALID_ARG, TAG, "capture timer and sync source are not in the same group");
|
||||
mcpwm_timer_sync_src_t *timer_sync_src = __containerof(sync_source, mcpwm_timer_sync_src_t, base);
|
||||
mcpwm_ll_capture_set_timer_sync(group->hal.dev, timer_sync_src->timer->timer_id);
|
||||
ESP_LOGD(TAG, "enable sync to pwm timer (%d,%d) for cap timer (%d)",
|
||||
group->group_id, timer_sync_src->timer->timer_id, group->group_id);
|
||||
break;
|
||||
}
|
||||
case MCPWM_SYNC_TYPE_SOFT: {
|
||||
mcpwm_soft_sync_src_t *soft_sync = __containerof(sync_source, mcpwm_soft_sync_src_t, base);
|
||||
soft_sync->soft_sync_from = MCPWM_SOFT_SYNC_FROM_CAP;
|
||||
soft_sync->cap_timer = cap_timer;
|
||||
soft_sync->base.group = group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mcpwm_ll_capture_enable_timer_sync(group->hal.dev, true);
|
||||
mcpwm_ll_capture_set_sync_phase_value(group->hal.dev, config->count_value);
|
||||
} else { // disable sync feature
|
||||
mcpwm_ll_capture_enable_timer_sync(group->hal.dev, false);
|
||||
ESP_LOGD(TAG, "disable sync for cap timer (%d)", group->group_id);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
IRAM_ATTR static void mcpwm_capture_default_isr(void *args)
|
||||
{
|
||||
mcpwm_cap_channel_t *cap_chan = (mcpwm_cap_channel_t *)args;
|
||||
mcpwm_group_t *group = cap_chan->cap_timer->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int cap_id = cap_chan->cap_chan_id;
|
||||
bool need_yield = false;
|
||||
|
||||
uint32_t status = mcpwm_ll_intr_get_status(hal->dev);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, status & MCPWM_LL_EVENT_CAPTURE(cap_id));
|
||||
|
||||
// read capture value and pass to user
|
||||
mcpwm_capture_event_data_t data = {
|
||||
.cap_value = mcpwm_ll_capture_get_value(hal->dev, cap_id),
|
||||
.cap_edge = mcpwm_ll_capture_get_edge(hal->dev, cap_id),
|
||||
};
|
||||
if (status & MCPWM_LL_EVENT_CAPTURE(cap_id)) {
|
||||
mcpwm_capture_event_cb_t cb = cap_chan->on_cap;
|
||||
if (cb) {
|
||||
if (cb(cap_chan, &data, cap_chan->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
210
components/driver/mcpwm/mcpwm_cmpr.c
Normal file
210
components/driver/mcpwm/mcpwm_cmpr.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/mcpwm_cmpr.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static void mcpwm_comparator_default_isr(void *args);
|
||||
|
||||
static esp_err_t mcpwm_comparator_register_to_operator(mcpwm_cmpr_t *cmpr, mcpwm_oper_t *oper)
|
||||
{
|
||||
int cmpr_id = -1;
|
||||
portENTER_CRITICAL(&oper->spinlock);
|
||||
for (int i = 0; i < SOC_MCPWM_COMPARATORS_PER_OPERATOR; i++) {
|
||||
if (!oper->comparators[i]) {
|
||||
oper->comparators[i] = cmpr;
|
||||
cmpr_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&oper->spinlock);
|
||||
ESP_RETURN_ON_FALSE(cmpr_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free comparator in operator (%d,%d)", oper->group->group_id, oper->oper_id);
|
||||
|
||||
cmpr->cmpr_id = cmpr_id;
|
||||
cmpr->operator = oper;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mcpwm_comparator_unregister_from_operator(mcpwm_cmpr_t *cmpr)
|
||||
{
|
||||
mcpwm_oper_t *oper = cmpr->operator;
|
||||
int cmpr_id = cmpr->cmpr_id;
|
||||
|
||||
portENTER_CRITICAL(&oper->spinlock);
|
||||
oper->comparators[cmpr_id] = NULL;
|
||||
portEXIT_CRITICAL(&oper->spinlock);
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_comparator_destory(mcpwm_cmpr_t *cmpr)
|
||||
{
|
||||
if (cmpr->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_free(cmpr->intr), TAG, "uninstall interrupt service failed");
|
||||
}
|
||||
if (cmpr->operator) {
|
||||
mcpwm_comparator_unregister_from_operator(cmpr);
|
||||
}
|
||||
free(cmpr);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_comparator(mcpwm_oper_handle_t oper, const mcpwm_comparator_config_t *config, mcpwm_cmpr_handle_t *ret_cmpr)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_cmpr_t *cmpr = NULL;
|
||||
ESP_GOTO_ON_FALSE(oper && config && ret_cmpr, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
cmpr = heap_caps_calloc(1, sizeof(mcpwm_cmpr_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(cmpr, ESP_ERR_NO_MEM, err, TAG, "no mem for comparator");
|
||||
|
||||
ESP_GOTO_ON_ERROR(mcpwm_comparator_register_to_operator(cmpr, oper), err, TAG, "register comparator failed");
|
||||
mcpwm_group_t *group = oper->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int oper_id = oper->oper_id;
|
||||
int cmpr_id = cmpr->cmpr_id;
|
||||
|
||||
mcpwm_ll_operator_enable_update_compare_on_tez(hal->dev, oper_id, cmpr_id, config->flags.update_cmp_on_tez);
|
||||
mcpwm_ll_operator_enable_update_compare_on_tep(hal->dev, oper_id, cmpr_id, config->flags.update_cmp_on_tep);
|
||||
mcpwm_ll_operator_enable_update_compare_on_sync(hal->dev, oper_id, cmpr_id, config->flags.update_cmp_on_sync);
|
||||
|
||||
// fill in other comparator members
|
||||
cmpr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
*ret_cmpr = cmpr;
|
||||
ESP_LOGD(TAG, "new comparator (%d,%d,%d) at %p", group->group_id, oper_id, cmpr_id, cmpr);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (cmpr) {
|
||||
mcpwm_comparator_destory(cmpr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_del_comparator(mcpwm_cmpr_handle_t cmpr)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cmpr, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_oper_t *operator= cmpr->operator;
|
||||
mcpwm_group_t *group = operator->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int oper_id = operator->oper_id;
|
||||
int cmpr_id = cmpr->cmpr_id;
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id), false);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id));
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
ESP_LOGD(TAG, "del comparator (%d,%d,%d)", group->group_id, oper_id, cmpr_id);
|
||||
// recycle memory resource
|
||||
ESP_RETURN_ON_ERROR(mcpwm_comparator_destory(cmpr), TAG, "destory comparator failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_comparator_set_compare_value(mcpwm_cmpr_handle_t cmpr, uint32_t cmp_ticks)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cmpr, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_oper_t *oper = cmpr->operator;
|
||||
mcpwm_group_t *group = oper->group;
|
||||
mcpwm_timer_t *timer = oper->timer;
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_STATE, TAG, "timer and operator are not connected");
|
||||
ESP_RETURN_ON_FALSE(cmp_ticks < timer->peak_ticks, ESP_ERR_INVALID_ARG, TAG, "compare value out of range");
|
||||
|
||||
portENTER_CRITICAL_SAFE(&cmpr->spinlock);
|
||||
mcpwm_ll_operator_set_compare_value(group->hal.dev, oper->oper_id, cmpr->cmpr_id, cmp_ticks);
|
||||
portEXIT_CRITICAL_SAFE(&cmpr->spinlock);
|
||||
|
||||
cmpr->compare_ticks = cmp_ticks;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_comparator_register_event_callbacks(mcpwm_cmpr_handle_t cmpr, const mcpwm_comparator_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(cmpr && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_oper_t *oper = cmpr->operator;
|
||||
mcpwm_group_t *group = oper->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int group_id = group->group_id;
|
||||
int oper_id = oper->oper_id;
|
||||
int cmpr_id = cmpr->cmpr_id;
|
||||
|
||||
#if CONFIG_MCWPM_ISR_IRAM_SAFE
|
||||
if (cbs->on_reach) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_reach), ESP_ERR_INVALID_ARG, TAG, "on_reach callback not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
// lazy install interrupt service
|
||||
if (!cmpr->intr) {
|
||||
// we want the interrupt servie to be enabled after allocation successfully
|
||||
int isr_flags = MCPWM_INTR_ALLOC_FLAG & ~ ESP_INTR_FLAG_INTRDISABLED;
|
||||
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(mcpwm_periph_signals.groups[group_id].irq_id, isr_flags,
|
||||
(uint32_t)mcpwm_ll_intr_get_status_reg(hal->dev), MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id),
|
||||
mcpwm_comparator_default_isr, cmpr, &cmpr->intr), TAG, "install interrupt service for comparator failed");
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id), cbs->on_reach != NULL);
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
cmpr->on_reach = cbs->on_reach;
|
||||
cmpr->user_data = user_data;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR mcpwm_comparator_default_isr(void *args)
|
||||
{
|
||||
mcpwm_cmpr_t *cmpr = (mcpwm_cmpr_t *)args;
|
||||
mcpwm_oper_t *oper = cmpr->operator;
|
||||
mcpwm_group_t *group = oper->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int oper_id = oper->oper_id;
|
||||
int cmpr_id = cmpr->cmpr_id;
|
||||
bool need_yield = false;
|
||||
|
||||
uint32_t status = mcpwm_ll_intr_get_status(hal->dev);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, status & MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id));
|
||||
|
||||
mcpwm_compare_event_data_t edata = {
|
||||
.compare_ticks = cmpr->compare_ticks,
|
||||
// .direction = TODO
|
||||
};
|
||||
|
||||
if (status & MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id)) {
|
||||
mcpwm_compare_event_cb_t cb = cmpr->on_reach;
|
||||
if (cb) {
|
||||
if (cb(cmpr, &edata, cmpr->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
135
components/driver/mcpwm/mcpwm_com.c
Normal file
135
components/driver/mcpwm/mcpwm_com.c
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/lock.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
typedef struct {
|
||||
_lock_t mutex; // platform level mutex lock
|
||||
mcpwm_group_t *groups[SOC_MCPWM_GROUPS]; // array of MCPWM group instances
|
||||
int group_ref_counts[SOC_MCPWM_GROUPS]; // reference count used to protect group install/uninstall
|
||||
} mcpwm_platform_t;
|
||||
|
||||
static mcpwm_platform_t s_platform; // singleton platform
|
||||
|
||||
mcpwm_group_t *mcpwm_acquire_group_handle(int group_id)
|
||||
{
|
||||
bool new_group = false;
|
||||
mcpwm_group_t *group = NULL;
|
||||
|
||||
// prevent install mcpwm group concurrently
|
||||
_lock_acquire(&s_platform.mutex);
|
||||
if (!s_platform.groups[group_id]) {
|
||||
group = heap_caps_calloc(1, sizeof(mcpwm_group_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
if (group) {
|
||||
new_group = true;
|
||||
s_platform.groups[group_id] = group;
|
||||
group->group_id = group_id;
|
||||
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
// enable APB to access MCPWM registers
|
||||
periph_module_enable(mcpwm_periph_signals.groups[group_id].module);
|
||||
periph_module_reset(mcpwm_periph_signals.groups[group_id].module);
|
||||
// initialize HAL context
|
||||
mcpwm_hal_init_config_t hal_config = {
|
||||
.group_id = group_id
|
||||
};
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
mcpwm_hal_init(hal, &hal_config);
|
||||
// disable all interrupts and clear pending status
|
||||
mcpwm_ll_intr_enable(hal->dev, UINT32_MAX, false);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, UINT32_MAX);
|
||||
}
|
||||
} else { // group already install
|
||||
group = s_platform.groups[group_id];
|
||||
}
|
||||
if (group) {
|
||||
// someone acquired the group handle means we have a new object that refer to this group
|
||||
s_platform.group_ref_counts[group_id]++;
|
||||
}
|
||||
_lock_release(&s_platform.mutex);
|
||||
|
||||
if (new_group) {
|
||||
ESP_LOGD(TAG, "new group(%d) at %p", group_id, group);
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
void mcpwm_release_group_handle(mcpwm_group_t *group)
|
||||
{
|
||||
int group_id = group->group_id;
|
||||
bool do_deinitialize = false;
|
||||
|
||||
_lock_acquire(&s_platform.mutex);
|
||||
s_platform.group_ref_counts[group_id]--;
|
||||
if (s_platform.group_ref_counts[group_id] == 0) {
|
||||
do_deinitialize = true;
|
||||
s_platform.groups[group_id] = NULL; // deregister from platfrom
|
||||
// hal layer deinitialize
|
||||
mcpwm_hal_deinit(&group->hal);
|
||||
periph_module_disable(mcpwm_periph_signals.groups[group_id].module);
|
||||
free(group);
|
||||
}
|
||||
_lock_release(&s_platform.mutex);
|
||||
|
||||
if (do_deinitialize) {
|
||||
ESP_LOGD(TAG, "del group(%d)", group_id);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, mcpwm_timer_clock_source_t clk_src)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint32_t periph_src_clk_hz = 0;
|
||||
bool clock_selection_conflict = false;
|
||||
bool do_clock_init = false;
|
||||
// check if we need to update the group clock source, group clock source is shared by all mcpwm objects
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
if (group->clk_src == 0) {
|
||||
group->clk_src = clk_src;
|
||||
do_clock_init = true;
|
||||
} else {
|
||||
clock_selection_conflict = (group->clk_src != clk_src);
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
ESP_RETURN_ON_FALSE(!clock_selection_conflict, ESP_ERR_INVALID_STATE, TAG,
|
||||
"group clock conflict, already is %d but attempt to %d", group->clk_src, clk_src);
|
||||
|
||||
if (do_clock_init) {
|
||||
// [clk_tree] ToDo: replace the following switch-case table by clock_tree APIs
|
||||
switch (clk_src) {
|
||||
case MCPWM_TIMER_CLK_SRC_DEFAULT:
|
||||
periph_src_clk_hz = 160000000;
|
||||
#if CONFIG_PM_ENABLE
|
||||
sprintf(group->pm_lock_name, "mcpwm_%d", group->group_id); // e.g. mcpwm_0
|
||||
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, group->pm_lock_name, &group->pm_lock);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "create ESP_PM_APB_FREQ_MAX lock failed");
|
||||
ESP_LOGD(TAG, "install ESP_PM_APB_FREQ_MAX lock for MCPWM group(%d)", group->group_id);
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
break;
|
||||
default:
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "clock source %d is not supported", clk_src);
|
||||
break;
|
||||
}
|
||||
mcpwm_ll_group_set_clock_prescale(group->hal.dev, MCPWM_PERIPH_CLOCK_PRE_SCALE);
|
||||
group->resolution_hz = periph_src_clk_hz / MCPWM_PERIPH_CLOCK_PRE_SCALE;
|
||||
ESP_LOGD(TAG, "group (%d) clock resolution:%uHz", group->group_id, group->resolution_hz);
|
||||
}
|
||||
return ret;
|
||||
}
|
302
components/driver/mcpwm/mcpwm_fault.c
Normal file
302
components/driver/mcpwm/mcpwm_fault.c
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/mcpwm_fault.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static void mcpwm_gpio_fault_default_isr(void *args);
|
||||
static esp_err_t mcpwm_del_gpio_fault(mcpwm_fault_handle_t fault);
|
||||
static esp_err_t mcpwm_del_soft_fault(mcpwm_fault_handle_t fault);
|
||||
|
||||
static esp_err_t mcpwm_gpio_fault_register_to_group(mcpwm_gpio_fault_t *fault, int group_id)
|
||||
{
|
||||
mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id);
|
||||
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id);
|
||||
|
||||
int fault_id = -1;
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
for (int i = 0; i < SOC_MCPWM_GPIO_FAULTS_PER_GROUP; i++) {
|
||||
if (!group->gpio_faults[i]) {
|
||||
fault_id = i;
|
||||
group->gpio_faults[i] = fault;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
if (fault_id < 0) {
|
||||
mcpwm_release_group_handle(group);
|
||||
group = NULL;
|
||||
} else {
|
||||
fault->base.group = group;
|
||||
fault->fault_id = fault_id;
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(fault_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free gpio fault in group (%d)", group_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mcpwm_gpio_fault_unregister_from_group(mcpwm_gpio_fault_t *fault)
|
||||
{
|
||||
mcpwm_group_t *group = fault->base.group;
|
||||
int fault_id = fault->fault_id;
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
group->gpio_faults[fault_id] = NULL;
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
// fault has a reference on group, release it now
|
||||
mcpwm_release_group_handle(group);
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_gpio_fault_destory(mcpwm_gpio_fault_t *fault)
|
||||
{
|
||||
if (fault->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_free(fault->intr), TAG, "uninstall interrupt service failed");
|
||||
}
|
||||
if (fault->base.group) {
|
||||
mcpwm_gpio_fault_unregister_from_group(fault);
|
||||
}
|
||||
free(fault);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_gpio_fault(const mcpwm_gpio_fault_config_t *config, mcpwm_fault_handle_t *ret_fault)
|
||||
{
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_gpio_fault_t *fault = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_fault, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->group_id < SOC_MCPWM_GROUPS && config->group_id >= 0, ESP_ERR_INVALID_ARG,
|
||||
err, TAG, "invalid group ID:%d", config->group_id);
|
||||
|
||||
fault = heap_caps_calloc(1, sizeof(mcpwm_gpio_fault_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(fault, ESP_ERR_NO_MEM, err, TAG, "no mem for gpio fault");
|
||||
|
||||
ESP_GOTO_ON_ERROR(mcpwm_gpio_fault_register_to_group(fault, config->group_id), err, TAG, "register gpio fault failed");
|
||||
mcpwm_group_t *group = fault->base.group;
|
||||
int group_id = group->group_id;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int fault_id = fault->fault_id;
|
||||
|
||||
// GPIO configuration
|
||||
gpio_config_t gpio_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
|
||||
.pin_bit_mask = (1ULL << config->gpio_num),
|
||||
.pull_down_en = config->flags.pull_down,
|
||||
.pull_up_en = config->flags.pull_up,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config fault GPIO failed");
|
||||
esp_rom_gpio_connect_in_signal(config->gpio_num, mcpwm_periph_signals.groups[group_id].gpio_faults[fault_id].fault_sig, 0);
|
||||
|
||||
// set fault detection polarity
|
||||
// different gpio faults share the same config register, using a group level spin lock
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_fault_set_active_level(hal->dev, fault_id, config->flags.active_level);
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
// enable fault detection
|
||||
mcpwm_ll_fault_enable_detection(hal->dev, fault_id, true);
|
||||
|
||||
// fill in other operator members
|
||||
fault->base.type = MCPWM_FAULT_TYPE_GPIO;
|
||||
fault->gpio_num = config->gpio_num;
|
||||
fault->base.del = mcpwm_del_gpio_fault;
|
||||
*ret_fault = &fault->base;
|
||||
ESP_LOGD(TAG, "new gpio fault (%d,%d) at %p, GPIO: %d", group_id, fault_id, fault, config->gpio_num);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (fault) {
|
||||
mcpwm_gpio_fault_destory(fault);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_del_gpio_fault(mcpwm_fault_handle_t fault)
|
||||
{
|
||||
mcpwm_gpio_fault_t *gpio_fault = __containerof(fault, mcpwm_gpio_fault_t, base);
|
||||
mcpwm_group_t *group = fault->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int fault_id = gpio_fault->fault_id;
|
||||
|
||||
ESP_LOGD(TAG, "del GPIO fault (%d,%d)", group->group_id, fault_id);
|
||||
gpio_reset_pin(gpio_fault->gpio_num);
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_FAULT_MASK(fault_id), false);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, MCPWM_LL_EVENT_FAULT_MASK(fault_id));
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
// disable fault detection
|
||||
mcpwm_ll_fault_enable_detection(hal->dev, fault_id, false);
|
||||
|
||||
// recycle memory resource
|
||||
ESP_RETURN_ON_ERROR(mcpwm_gpio_fault_destory(gpio_fault), TAG, "destory GPIO fault failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_soft_fault(const mcpwm_soft_fault_config_t *config, mcpwm_fault_handle_t *ret_fault)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_soft_fault_t *soft_fault = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_fault, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
soft_fault = heap_caps_calloc(1, sizeof(mcpwm_soft_fault_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(soft_fault, ESP_ERR_NO_MEM, err, TAG, "no mem for soft fault");
|
||||
|
||||
// fill in other fault members
|
||||
soft_fault->base.type = MCPWM_FAULT_TYPE_SOFT;
|
||||
soft_fault->base.del = mcpwm_del_soft_fault;
|
||||
*ret_fault = &soft_fault->base;
|
||||
ESP_LOGD(TAG, "new soft fault at %p", soft_fault);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (soft_fault) {
|
||||
free(soft_fault);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_del_soft_fault(mcpwm_fault_handle_t fault)
|
||||
{
|
||||
mcpwm_soft_fault_t *soft_fault = __containerof(fault, mcpwm_soft_fault_t, base);
|
||||
ESP_LOGD(TAG, "del soft fault %p", soft_fault);
|
||||
free(soft_fault);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_soft_fault_activate(mcpwm_fault_handle_t fault)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(fault, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(fault->type == MCPWM_FAULT_TYPE_SOFT, ESP_ERR_INVALID_ARG, TAG, "not a valid soft fault");
|
||||
mcpwm_group_t *group = fault->group;
|
||||
mcpwm_soft_fault_t *soft_fault = __containerof(fault, mcpwm_soft_fault_t, base);
|
||||
mcpwm_oper_t *operator = soft_fault->operator;
|
||||
ESP_RETURN_ON_FALSE(operator, ESP_ERR_INVALID_STATE, TAG, "no operator is assigned to the fault");
|
||||
|
||||
switch (operator->brake_mode_on_soft_fault) {
|
||||
case MCPWM_OPER_BRAKE_MODE_CBC:
|
||||
mcpwm_ll_brake_trigger_soft_cbc(group->hal.dev, operator->oper_id);
|
||||
break;
|
||||
case MCPWM_OPER_BRAKE_MODE_OST:
|
||||
mcpwm_ll_brake_trigger_soft_ost(group->hal.dev, operator->oper_id);
|
||||
break;
|
||||
default:
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "unknown brake mode:%d", operator->brake_mode_on_soft_fault);
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_del_fault(mcpwm_fault_handle_t fault)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(fault, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return fault->del(fault);
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_fault_register_event_callbacks(mcpwm_fault_handle_t fault, const mcpwm_fault_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(fault && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(fault->type == MCPWM_FAULT_TYPE_GPIO, ESP_ERR_INVALID_ARG, TAG, "only gpio fault can register event callback");
|
||||
mcpwm_gpio_fault_t *gpio_fault = __containerof(fault, mcpwm_gpio_fault_t, base);
|
||||
mcpwm_group_t *group = fault->group;
|
||||
int group_id = group->group_id;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int fault_id = gpio_fault->fault_id;
|
||||
|
||||
#if CONFIG_MCWPM_ISR_IRAM_SAFE
|
||||
if (cbs->on_fault_enter) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_fault_enter), ESP_ERR_INVALID_ARG, TAG, "on_fault_enter callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_fault_exit) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_fault_exit), ESP_ERR_INVALID_ARG, TAG, "on_fault_exit callback not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
// lazy install interrupt service
|
||||
if (!gpio_fault->intr) {
|
||||
// we want the interrupt servie to be enabled after allocation successfully
|
||||
int isr_flags = MCPWM_INTR_ALLOC_FLAG & ~ESP_INTR_FLAG_INTRDISABLED;
|
||||
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(mcpwm_periph_signals.groups[group_id].irq_id, isr_flags,
|
||||
(uint32_t)mcpwm_ll_intr_get_status_reg(hal->dev), MCPWM_LL_EVENT_FAULT_MASK(fault_id),
|
||||
mcpwm_gpio_fault_default_isr, gpio_fault, &gpio_fault->intr), TAG, "install interrupt service for gpio fault failed");
|
||||
}
|
||||
|
||||
// different mcpwm events share the same interrupt control register
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_FAULT_ENTER(fault_id), cbs->on_fault_enter != NULL);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_FAULT_EXIT(fault_id), cbs->on_fault_exit != NULL);
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
gpio_fault->on_fault_enter = cbs->on_fault_enter;
|
||||
gpio_fault->on_fault_exit = cbs->on_fault_exit;
|
||||
gpio_fault->user_data = user_data;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR mcpwm_gpio_fault_default_isr(void *args)
|
||||
{
|
||||
mcpwm_gpio_fault_t *fault = (mcpwm_gpio_fault_t *)args;
|
||||
mcpwm_group_t *group = fault->base.group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int fault_id = fault->fault_id;
|
||||
bool need_yield = false;
|
||||
|
||||
uint32_t status = mcpwm_ll_intr_get_status(hal->dev);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, status & MCPWM_LL_EVENT_FAULT_MASK(fault_id));
|
||||
|
||||
mcpwm_fault_event_data_t edata = {
|
||||
// TODO
|
||||
};
|
||||
|
||||
if (status & MCPWM_LL_EVENT_FAULT_ENTER(fault_id)) {
|
||||
mcpwm_fault_event_cb_t cb = fault->on_fault_enter;
|
||||
if (cb) {
|
||||
if (cb(&fault->base, &edata, fault->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status & MCPWM_LL_EVENT_FAULT_EXIT(fault_id)) {
|
||||
mcpwm_fault_event_cb_t cb = fault->on_fault_exit;
|
||||
if (cb) {
|
||||
if (cb(&fault->base, &edata, fault->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
264
components/driver/mcpwm/mcpwm_gen.c
Normal file
264
components/driver/mcpwm/mcpwm_gen.c
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/mcpwm_gen.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static esp_err_t mcpwm_generator_register_to_operator(mcpwm_gen_t *gen, mcpwm_oper_t *oper)
|
||||
{
|
||||
int gen_id = -1;
|
||||
portENTER_CRITICAL(&oper->spinlock);
|
||||
for (int i = 0; i < SOC_MCPWM_GENERATORS_PER_OPERATOR; i++) {
|
||||
if (!oper->generators[i]) {
|
||||
oper->generators[i] = gen;
|
||||
gen_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&oper->spinlock);
|
||||
ESP_RETURN_ON_FALSE(gen_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free generator in operator (%d,%d)", oper->group->group_id, oper->oper_id);
|
||||
|
||||
gen->gen_id = gen_id;
|
||||
gen->operator = oper;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mcpwm_generator_unregister_from_operator(mcpwm_gen_t *gen)
|
||||
{
|
||||
mcpwm_oper_t *oper = gen->operator;
|
||||
int gen_id = gen->gen_id;
|
||||
|
||||
portENTER_CRITICAL(&oper->spinlock);
|
||||
oper->generators[gen_id] = NULL;
|
||||
portEXIT_CRITICAL(&oper->spinlock);
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_generator_destory(mcpwm_gen_t *gen)
|
||||
{
|
||||
if (gen->operator) {
|
||||
mcpwm_generator_unregister_from_operator(gen);
|
||||
}
|
||||
free(gen);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_generator(mcpwm_oper_handle_t oper, const mcpwm_generator_config_t *config, mcpwm_gen_handle_t *ret_gen)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_gen_t *gen = NULL;
|
||||
ESP_GOTO_ON_FALSE(oper && config && ret_gen, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
gen = heap_caps_calloc(1, sizeof(mcpwm_gen_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(gen, ESP_ERR_NO_MEM, err, TAG, "no mem for generator");
|
||||
|
||||
ESP_GOTO_ON_ERROR(mcpwm_generator_register_to_operator(gen, oper), err, TAG, "register generator failed");
|
||||
mcpwm_group_t *group = oper->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int oper_id = oper->oper_id;
|
||||
int gen_id = gen->gen_id;
|
||||
|
||||
// reset generator
|
||||
mcpwm_hal_generator_reset(hal, oper_id, gen_id);
|
||||
|
||||
// GPIO configuration
|
||||
gpio_config_t gpio_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT | (config->flags.io_loop_back ? GPIO_MODE_INPUT : 0), // also enable the input path if `io_loop_back` is enabled
|
||||
.pin_bit_mask = (1ULL << config->gen_gpio_num),
|
||||
.pull_down_en = false,
|
||||
.pull_up_en = true,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config gen GPIO failed");
|
||||
esp_rom_gpio_connect_out_signal(config->gen_gpio_num,
|
||||
mcpwm_periph_signals.groups[group->group_id].operators[oper_id].generators[gen_id].pwm_sig,
|
||||
config->flags.invert_pwm, 0);
|
||||
|
||||
// fill in other generator members
|
||||
gen->gen_gpio_num = config->gen_gpio_num;
|
||||
gen->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
*ret_gen = gen;
|
||||
ESP_LOGD(TAG, "new generator (%d,%d,%d) at %p, GPIO %d", group->group_id, oper_id, gen_id, gen, gen->gen_gpio_num);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (gen) {
|
||||
mcpwm_generator_destory(gen);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_del_generator(mcpwm_gen_handle_t gen)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(gen, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_oper_t *oper = gen->operator;
|
||||
mcpwm_group_t *group = oper->group;
|
||||
|
||||
ESP_LOGD(TAG, "del generator (%d,%d,%d)", group->group_id, oper->oper_id, gen->gen_id);
|
||||
// recycle memory resource
|
||||
ESP_RETURN_ON_ERROR(mcpwm_generator_destory(gen), TAG, "destory generator failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_generator_set_force_level(mcpwm_gen_handle_t gen, int level, bool hold_on)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(gen && level <= 1, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_oper_t *oper = gen->operator;
|
||||
mcpwm_group_t *group = oper->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int oper_id = oper->oper_id;
|
||||
int gen_id = gen->gen_id;
|
||||
|
||||
if (level < 0) { // to remove the force level
|
||||
if (hold_on) {
|
||||
mcpwm_ll_gen_disable_continue_force_action(hal->dev, oper_id, gen_id);
|
||||
} else {
|
||||
mcpwm_ll_gen_disable_noncontinue_force_action(hal->dev, oper_id, gen_id);
|
||||
}
|
||||
} else { // to enable the force output level
|
||||
if (hold_on) {
|
||||
mcpwm_ll_gen_set_continue_force_level(hal->dev, oper_id, gen_id, level);
|
||||
} else {
|
||||
mcpwm_ll_gen_set_noncontinue_force_level(hal->dev, oper_id, gen_id, level);
|
||||
mcpwm_ll_gen_trigger_noncontinue_force_action(hal->dev, oper_id, gen_id);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_generator_set_actions_on_timer_event(mcpwm_gen_handle_t gen, mcpwm_gen_timer_event_action_t ev_act, ...)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(gen, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_oper_t *operator= gen->operator;
|
||||
mcpwm_group_t *group = operator->group;
|
||||
mcpwm_timer_t *timer = operator->timer;
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_STATE, TAG, "no timer is connected to the operator");
|
||||
mcpwm_gen_timer_event_action_t ev_act_itor = ev_act;
|
||||
bool invalid_utep = false;
|
||||
bool invalid_dtez = false;
|
||||
va_list it;
|
||||
va_start(it, ev_act);
|
||||
while (ev_act_itor.event != MCPWM_TIMER_EVENT_INVALID) {
|
||||
invalid_utep = (timer->count_mode == MCPWM_TIMER_COUNT_MODE_UP_DOWN) &&
|
||||
(ev_act_itor.direction == MCPWM_TIMER_DIRECTION_UP) &&
|
||||
(ev_act_itor.event == MCPWM_TIMER_EVENT_FULL);
|
||||
invalid_dtez = (timer->count_mode == MCPWM_TIMER_COUNT_MODE_UP_DOWN) &&
|
||||
(ev_act_itor.direction == MCPWM_TIMER_DIRECTION_DOWN) &&
|
||||
(ev_act_itor.event == MCPWM_TIMER_EVENT_EMPTY);
|
||||
if (invalid_utep || invalid_dtez) {
|
||||
va_end(it);
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "UTEP and DTEZ can't be reached under MCPWM_TIMER_COUNT_MODE_UP_DOWN mode");
|
||||
}
|
||||
mcpwm_ll_generator_set_action_on_timer_event(group->hal.dev, operator->oper_id, gen->gen_id,
|
||||
ev_act_itor.direction, ev_act_itor.event, ev_act_itor.action);
|
||||
ev_act_itor = va_arg(it, mcpwm_gen_timer_event_action_t);
|
||||
}
|
||||
va_end(it);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_generator_set_actions_on_compare_event(mcpwm_gen_handle_t gen, mcpwm_gen_compare_event_action_t ev_act, ...)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(gen, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_oper_t *operator= gen->operator;
|
||||
mcpwm_group_t *group = operator->group;
|
||||
mcpwm_gen_compare_event_action_t ev_act_itor = ev_act;
|
||||
va_list it;
|
||||
va_start(it, ev_act);
|
||||
while (ev_act_itor.comparator) {
|
||||
mcpwm_ll_generator_set_action_on_compare_event(group->hal.dev, operator->oper_id, gen->gen_id,
|
||||
ev_act_itor.direction, ev_act_itor.comparator->cmpr_id, ev_act_itor.action);
|
||||
ev_act_itor = va_arg(it, mcpwm_gen_compare_event_action_t);
|
||||
}
|
||||
va_end(it);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_generator_set_actions_on_brake_event(mcpwm_gen_handle_t gen, mcpwm_gen_brake_event_action_t ev_act, ...)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(gen, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_oper_t *operator= gen->operator;
|
||||
mcpwm_group_t *group = operator->group;
|
||||
mcpwm_gen_brake_event_action_t ev_act_itor = ev_act;
|
||||
va_list it;
|
||||
va_start(it, ev_act);
|
||||
while (ev_act_itor.brake_mode != MCPWM_OPER_BRAKE_MODE_INVALID) {
|
||||
mcpwm_ll_generator_set_action_on_brake_event(group->hal.dev, operator->oper_id, gen->gen_id,
|
||||
ev_act_itor.direction, ev_act_itor.brake_mode, ev_act_itor.action);
|
||||
ev_act_itor = va_arg(it, mcpwm_gen_brake_event_action_t);
|
||||
}
|
||||
va_end(it);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_generator_set_dead_time(mcpwm_gen_handle_t in_generator, mcpwm_gen_handle_t out_generator, const mcpwm_dead_time_config_t *config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(in_generator && out_generator && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(in_generator->operator == out_generator->operator, ESP_ERR_INVALID_ARG, TAG, "in/out generator are not derived from the same operator");
|
||||
ESP_RETURN_ON_FALSE(config->negedge_delay_ticks < MCPWM_LL_MAX_DEAD_DELAY && config->posedge_delay_ticks < MCPWM_LL_MAX_DEAD_DELAY,
|
||||
ESP_ERR_INVALID_ARG, TAG, "delay time out of range");
|
||||
mcpwm_oper_t *operator= in_generator->operator;
|
||||
mcpwm_group_t *group = operator->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int oper_id = operator->oper_id;
|
||||
|
||||
// Note: to better understand the following code, you should read the deadtime module topology diagram in the TRM
|
||||
// check if we want to bypass the deadtime module
|
||||
bool bypass = (config->negedge_delay_ticks == 0) && (config->posedge_delay_ticks == 0);
|
||||
// check is we want to delay on the both edge
|
||||
bool delay_on_both_edge = config->posedge_delay_ticks && config->negedge_delay_ticks;
|
||||
int out_path_id = -1;
|
||||
if (bypass) {
|
||||
// out path is same to the input path of generator
|
||||
out_path_id = in_generator->gen_id;
|
||||
} else if (config->negedge_delay_ticks) {
|
||||
out_path_id = 1; // FED path
|
||||
} else {
|
||||
out_path_id = 0; // RED path
|
||||
}
|
||||
bool swap_path = out_path_id != out_generator->gen_id;
|
||||
mcpwm_ll_deadtime_bypass_path(hal->dev, oper_id, out_path_id, bypass); // S0/1
|
||||
if (!bypass) {
|
||||
if (config->posedge_delay_ticks) {
|
||||
mcpwm_ll_deadtime_red_select_generator(hal->dev, oper_id, in_generator->gen_id); // S4
|
||||
} else {
|
||||
mcpwm_ll_deadtime_fed_select_generator(hal->dev, oper_id, in_generator->gen_id); // S5
|
||||
}
|
||||
mcpwm_ll_deadtime_enable_deb(hal->dev, oper_id, delay_on_both_edge); // S8
|
||||
mcpwm_ll_deadtime_invert_outpath(hal->dev, oper_id, out_path_id, config->flags.invert_output); // S2/3
|
||||
mcpwm_ll_deadtime_swap_out_path(hal->dev, oper_id, out_generator->gen_id, swap_path); // S6/S7
|
||||
}
|
||||
// set delay time
|
||||
if (config->posedge_delay_ticks) {
|
||||
mcpwm_ll_deadtime_set_rising_delay(hal->dev, oper_id, config->posedge_delay_ticks);
|
||||
}
|
||||
if (config->negedge_delay_ticks) {
|
||||
mcpwm_ll_deadtime_set_falling_delay(hal->dev, oper_id, config->negedge_delay_ticks);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "operator (%d,%d) dead time (R:%u,F:%u), topology code:%x", group->group_id, oper_id,
|
||||
config->posedge_delay_ticks, config->negedge_delay_ticks, mcpwm_ll_deadtime_get_switch_topology(hal->dev, oper_id));
|
||||
return ESP_OK;
|
||||
}
|
363
components/driver/mcpwm/mcpwm_oper.c
Normal file
363
components/driver/mcpwm/mcpwm_oper.c
Normal file
@ -0,0 +1,363 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/mcpwm_oper.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static void mcpwm_operator_default_isr(void *args);
|
||||
|
||||
static esp_err_t mcpwm_operator_register_to_group(mcpwm_oper_t *oper, int group_id)
|
||||
{
|
||||
mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id);
|
||||
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id);
|
||||
|
||||
int oper_id = -1;
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
for (int i = 0; i < SOC_MCPWM_OPERATORS_PER_GROUP; i++) {
|
||||
if (!group->operators[i]) {
|
||||
oper_id = i;
|
||||
group->operators[i] = oper;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
if (oper_id < 0) {
|
||||
mcpwm_release_group_handle(group);
|
||||
group = NULL;
|
||||
} else {
|
||||
oper->group = group;
|
||||
oper->oper_id = oper_id;
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(oper_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free operators in group (%d)", group_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mcpwm_operator_unregister_from_group(mcpwm_oper_t *oper)
|
||||
{
|
||||
mcpwm_group_t *group = oper->group;
|
||||
int oper_id = oper->oper_id;
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
group->operators[oper_id] = NULL;
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
// operator has a reference on group, release it now
|
||||
mcpwm_release_group_handle(group);
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_operator_destory(mcpwm_oper_t *oper)
|
||||
{
|
||||
if (oper->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_free(oper->intr), TAG, "uninstall interrupt service failed");
|
||||
}
|
||||
if (oper->group) {
|
||||
mcpwm_operator_unregister_from_group(oper);
|
||||
}
|
||||
free(oper);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_operator(const mcpwm_operator_config_t *config, mcpwm_oper_handle_t *ret_oper)
|
||||
{
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_oper_t *operator= NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_oper, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->group_id < SOC_MCPWM_GROUPS && config->group_id >= 0, ESP_ERR_INVALID_ARG,
|
||||
err, TAG, "invalid group ID:%d", config->group_id);
|
||||
|
||||
operator= heap_caps_calloc(1, sizeof(mcpwm_oper_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(operator, ESP_ERR_NO_MEM, err, TAG, "no mem for operator");
|
||||
|
||||
ESP_GOTO_ON_ERROR(mcpwm_operator_register_to_group(operator, config->group_id), err, TAG, "register operator failed");
|
||||
mcpwm_group_t *group = operator->group;
|
||||
int group_id = group->group_id;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int oper_id = operator->oper_id;
|
||||
|
||||
// reset MCPWM operator
|
||||
mcpwm_hal_operator_reset(hal, oper_id);
|
||||
|
||||
// set the time point that the generator can update the action
|
||||
mcpwm_ll_operator_enable_update_action_on_tez(hal->dev, oper_id, config->flags.update_gen_action_on_tez);
|
||||
mcpwm_ll_operator_enable_update_action_on_tep(hal->dev, oper_id, config->flags.update_gen_action_on_tep);
|
||||
mcpwm_ll_operator_enable_update_action_on_sync(hal->dev, oper_id, config->flags.update_gen_action_on_sync);
|
||||
// set the time point that the deadtime can update the delay parameter
|
||||
mcpwm_ll_deadtime_enable_update_delay_on_tez(hal->dev, oper_id, config->flags.update_dead_time_on_tez);
|
||||
mcpwm_ll_deadtime_enable_update_delay_on_tep(hal->dev, oper_id, config->flags.update_dead_time_on_tep);
|
||||
mcpwm_ll_deadtime_enable_update_delay_on_sync(hal->dev, oper_id, config->flags.update_dead_time_on_sync);
|
||||
// set the clock source for dead time submodule, the resolution is the same to the MCPWM group
|
||||
mcpwm_ll_operator_set_deadtime_clock_src(hal->dev, oper_id, MCPWM_LL_DEADTIME_CLK_SRC_GROUP);
|
||||
operator->deadtime_resolution_hz = group->resolution_hz;
|
||||
|
||||
// fill in other operator members
|
||||
operator->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
*ret_oper = operator;
|
||||
ESP_LOGD(TAG, "new operator (%d,%d) at %p", group_id, oper_id, operator);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (operator) {
|
||||
mcpwm_operator_destory(operator);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_del_operator(mcpwm_oper_handle_t oper)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(oper, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
for (int i = 0; i < SOC_MCPWM_COMPARATORS_PER_OPERATOR; i++) {
|
||||
ESP_RETURN_ON_FALSE(!oper->comparators[i], ESP_ERR_INVALID_STATE, TAG, "comparator still in working");
|
||||
}
|
||||
for (int i = 0; i < SOC_MCPWM_GENERATORS_PER_OPERATOR; i++) {
|
||||
ESP_RETURN_ON_FALSE(!oper->generators[i], ESP_ERR_INVALID_STATE, TAG, "generator still in working");
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(!oper->soft_fault, ESP_ERR_INVALID_STATE, TAG, "soft fault still in working");
|
||||
mcpwm_group_t *group = oper->group;
|
||||
int oper_id = oper->oper_id;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_OPER_MASK(oper_id), false);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, MCPWM_LL_EVENT_OPER_MASK(oper_id));
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
ESP_LOGD(TAG, "del operator (%d,%d)", group->group_id, oper_id);
|
||||
// recycle memory resource
|
||||
ESP_RETURN_ON_ERROR(mcpwm_operator_destory(oper), TAG, "destory operator failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_operator_connect_timer(mcpwm_oper_handle_t oper, mcpwm_timer_handle_t timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(oper && timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(oper->group == timer->group, ESP_ERR_INVALID_ARG, TAG, "operator and timer should reside in the same group");
|
||||
mcpwm_group_t *group = oper->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
|
||||
// connect operator and timer
|
||||
mcpwm_ll_operator_connect_timer(hal->dev, oper->oper_id, timer->timer_id);
|
||||
// change the the clock source of deadtime submodule to use MCPWM timer
|
||||
mcpwm_ll_operator_set_deadtime_clock_src(hal->dev, oper->oper_id, MCPWM_LL_DEADTIME_CLK_SRC_TIMER);
|
||||
oper->deadtime_resolution_hz = timer->resolution_hz;
|
||||
|
||||
oper->timer = timer;
|
||||
ESP_LOGD(TAG, "connect operator (%d) and timer (%d) in group (%d)", oper->oper_id, timer->timer_id, group->group_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_operator_apply_carrier(mcpwm_oper_handle_t oper, const mcpwm_carrier_config_t *config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(oper, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_group_t *group = oper->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int oper_id = oper->oper_id;
|
||||
uint32_t real_frequency = 0;
|
||||
uint32_t real_fpd = 0;
|
||||
float real_duty = 0.0;
|
||||
|
||||
if (config && config->frequency_hz) {
|
||||
uint8_t pre_scale = group->resolution_hz / 8 / config->frequency_hz;
|
||||
mcpwm_ll_carrier_set_prescale(hal->dev, oper_id, pre_scale);
|
||||
real_frequency = group->resolution_hz / 8 / pre_scale;
|
||||
|
||||
uint8_t duty = (uint8_t)(config->duty_cycle * 8);
|
||||
mcpwm_ll_carrier_set_duty(hal->dev, oper_id, duty);
|
||||
real_duty = (float) duty / 8.0F;
|
||||
|
||||
uint8_t first_pulse_ticks = (uint8_t)(config->first_pulse_duration_us * real_frequency / 1000000UL);
|
||||
ESP_RETURN_ON_FALSE(first_pulse_ticks > 0 && first_pulse_ticks <= MCPWM_LL_MAX_CARRIER_ONESHOT,
|
||||
ESP_ERR_INVALID_ARG, TAG, "invalid first pulse duration");
|
||||
mcpwm_ll_carrier_set_first_pulse_width(hal->dev, oper_id, first_pulse_ticks);
|
||||
real_fpd = first_pulse_ticks * 1000000UL / real_frequency;
|
||||
|
||||
mcpwm_ll_carrier_in_invert(hal->dev, oper_id, config->flags.invert_before_modulate);
|
||||
mcpwm_ll_carrier_out_invert(hal->dev, oper_id, config->flags.invert_after_modulate);
|
||||
}
|
||||
|
||||
mcpwm_ll_carrier_enable(hal->dev, oper_id, real_frequency > 0);
|
||||
|
||||
if (real_frequency > 0) {
|
||||
ESP_LOGD(TAG, "enable carrier modulation for operator(%d,%d), freq=%uHz, duty=%.2f, FPD=%dus",
|
||||
group->group_id, oper_id, real_frequency, real_duty, real_fpd);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "disable carrier for operator (%d,%d)", group->group_id, oper_id);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_operator_register_event_callbacks(mcpwm_oper_handle_t oper, const mcpwm_operator_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(oper && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_group_t *group = oper->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int group_id = group->group_id;
|
||||
int oper_id = oper->oper_id;
|
||||
|
||||
#if CONFIG_MCWPM_ISR_IRAM_SAFE
|
||||
if (cbs->on_brake_cbc) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_brake_cbc), ESP_ERR_INVALID_ARG, TAG, "on_brake_cbc callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_brake_ost) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_brake_ost), ESP_ERR_INVALID_ARG, TAG, "on_brake_ost callback not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
// lazy install interrupt service
|
||||
if (!oper->intr) {
|
||||
// we want the interrupt servie to be enabled after allocation successfully
|
||||
int isr_flags = MCPWM_INTR_ALLOC_FLAG & ~ ESP_INTR_FLAG_INTRDISABLED;
|
||||
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(mcpwm_periph_signals.groups[group_id].irq_id, isr_flags,
|
||||
(uint32_t)mcpwm_ll_intr_get_status_reg(hal->dev), MCPWM_LL_EVENT_OPER_MASK(oper_id),
|
||||
mcpwm_operator_default_isr, oper, &oper->intr), TAG, "install interrupt service for operator failed");
|
||||
}
|
||||
|
||||
// enable/disable interrupt events
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_OPER_BRAKE_CBC(oper_id), cbs->on_brake_cbc != NULL);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_OPER_BRAKE_OST(oper_id), cbs->on_brake_ost != NULL);
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
oper->on_brake_cbc = cbs->on_brake_cbc;
|
||||
oper->on_brake_ost = cbs->on_brake_ost;
|
||||
oper->user_data = user_data;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_operator_set_brake_on_fault(mcpwm_oper_handle_t operator, const mcpwm_brake_config_t *config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(operator && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_group_t *group = operator->group;
|
||||
mcpwm_fault_t *fault = config->fault;
|
||||
|
||||
int oper_id = operator->oper_id;
|
||||
mcpwm_ll_brake_enable_cbc_refresh_on_tez(group->hal.dev, oper_id, config->flags.cbc_recover_on_tez);
|
||||
mcpwm_ll_fault_enable_cbc_refresh_on_tep(group->hal.dev, oper_id, config->flags.cbc_recover_on_tep);
|
||||
|
||||
switch (fault->type) {
|
||||
case MCPWM_FAULT_TYPE_GPIO: {
|
||||
ESP_RETURN_ON_FALSE(group == fault->group, ESP_ERR_INVALID_ARG, TAG, "fault and operator not in the same group");
|
||||
mcpwm_gpio_fault_t *gpio_fault = __containerof(fault, mcpwm_gpio_fault_t, base);
|
||||
mcpwm_ll_brake_enable_cbc_mode(group->hal.dev, oper_id, gpio_fault->fault_id, config->brake_mode == MCPWM_OPER_BRAKE_MODE_CBC);
|
||||
mcpwm_ll_brake_enable_oneshot_mode(group->hal.dev, oper_id, gpio_fault->fault_id, config->brake_mode == MCPWM_OPER_BRAKE_MODE_OST);
|
||||
operator->brake_mode_on_gpio_fault[gpio_fault->fault_id] = config->brake_mode;
|
||||
break;
|
||||
}
|
||||
case MCPWM_FAULT_TYPE_SOFT: {
|
||||
mcpwm_soft_fault_t *soft_fault = __containerof(fault, mcpwm_soft_fault_t, base);
|
||||
ESP_RETURN_ON_FALSE(!soft_fault->operator || soft_fault->operator == operator, ESP_ERR_INVALID_STATE, TAG, "soft fault already used by another operator");
|
||||
soft_fault->operator = operator;
|
||||
soft_fault->base.group = operator->group;
|
||||
mcpwm_ll_brake_enable_soft_cbc(group->hal.dev, oper_id, config->brake_mode == MCPWM_OPER_BRAKE_MODE_CBC);
|
||||
mcpwm_ll_brake_enable_soft_ost(group->hal.dev, oper_id, config->brake_mode == MCPWM_OPER_BRAKE_MODE_OST);
|
||||
operator->brake_mode_on_soft_fault = config->brake_mode;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "unknown fault type:%d", fault->type);
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_operator_recover_from_fault(mcpwm_oper_handle_t operator, mcpwm_fault_handle_t fault)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(operator && fault, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_group_t *group = operator->group;
|
||||
mcpwm_operator_brake_mode_t brake_mode;
|
||||
|
||||
// check the brake mode on the fault event
|
||||
switch (fault->type) {
|
||||
case MCPWM_FAULT_TYPE_GPIO: {
|
||||
mcpwm_gpio_fault_t *gpio_fault = __containerof(fault, mcpwm_gpio_fault_t, base);
|
||||
brake_mode = operator->brake_mode_on_gpio_fault[gpio_fault->fault_id];
|
||||
break;
|
||||
}
|
||||
case MCPWM_FAULT_TYPE_SOFT:
|
||||
brake_mode = operator->brake_mode_on_soft_fault;
|
||||
break;
|
||||
default:
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "unknown fault type:%d", fault->type);
|
||||
break;
|
||||
}
|
||||
|
||||
bool fault_signal_is_active = false;
|
||||
if (brake_mode == MCPWM_OPER_BRAKE_MODE_OST) {
|
||||
fault_signal_is_active = mcpwm_ll_ost_brake_active(group->hal.dev, operator->oper_id);
|
||||
// OST brake can't recover automatically, need to manually recovery the operator
|
||||
if (!fault_signal_is_active) {
|
||||
mcpwm_ll_brake_clear_ost(group->hal.dev, operator->oper_id);
|
||||
}
|
||||
} else {
|
||||
fault_signal_is_active = mcpwm_ll_cbc_brake_active(group->hal.dev, operator->oper_id);
|
||||
// CBC brake can recover automatically after deactivating the fault signal
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_FALSE(!fault_signal_is_active, ESP_ERR_INVALID_STATE, TAG, "recover fail, fault signal still active");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR mcpwm_operator_default_isr(void *args)
|
||||
{
|
||||
mcpwm_oper_t *oper = (mcpwm_oper_t *)args;
|
||||
mcpwm_group_t *group = oper->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int oper_id = oper->oper_id;
|
||||
bool need_yield = false;
|
||||
|
||||
uint32_t status = mcpwm_ll_intr_get_status(hal->dev);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, status & MCPWM_LL_EVENT_OPER_MASK(oper_id));
|
||||
|
||||
mcpwm_brake_event_data_t edata = {};
|
||||
|
||||
if (status & MCPWM_LL_EVENT_OPER_BRAKE_CBC(oper_id)) {
|
||||
mcpwm_brake_event_cb_t cb = oper->on_brake_cbc;
|
||||
if (cb) {
|
||||
if (cb(oper, &edata, oper->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status & MCPWM_LL_EVENT_OPER_BRAKE_OST(oper_id)) {
|
||||
mcpwm_brake_event_cb_t cb = oper->on_brake_ost;
|
||||
if (cb) {
|
||||
if (cb(oper, &edata, oper->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
221
components/driver/mcpwm/mcpwm_private.h
Normal file
221
components/driver/mcpwm/mcpwm_private.h
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_pm.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/mcpwm_hal.h"
|
||||
#include "hal/mcpwm_types.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#define MCPWM_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define MCPWM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#endif
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#define MCPWM_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM)
|
||||
#else
|
||||
#define MCPWM_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED)
|
||||
#endif
|
||||
|
||||
#define MCPWM_PERIPH_CLOCK_PRE_SCALE (2)
|
||||
#define MCPWM_PM_LOCK_NAME_LEN_MAX 16
|
||||
|
||||
typedef struct mcpwm_group_t mcpwm_group_t;
|
||||
typedef struct mcpwm_timer_t mcpwm_timer_t;
|
||||
typedef struct mcpwm_cap_timer_t mcpwm_cap_timer_t;
|
||||
typedef struct mcpwm_oper_t mcpwm_oper_t;
|
||||
typedef struct mcpwm_cmpr_t mcpwm_cmpr_t;
|
||||
typedef struct mcpwm_gen_t mcpwm_gen_t;
|
||||
typedef struct mcpwm_fault_t mcpwm_fault_t;
|
||||
typedef struct mcpwm_gpio_fault_t mcpwm_gpio_fault_t;
|
||||
typedef struct mcpwm_soft_fault_t mcpwm_soft_fault_t;
|
||||
typedef struct mcpwm_sync_t mcpwm_sync_t;
|
||||
typedef struct mcpwm_gpio_sync_src_t mcpwm_gpio_sync_src_t;
|
||||
typedef struct mcpwm_timer_sync_src_t mcpwm_timer_sync_src_t;
|
||||
typedef struct mcpwm_soft_sync_src_t mcpwm_soft_sync_src_t;
|
||||
typedef struct mcpwm_cap_channel_t mcpwm_cap_channel_t;
|
||||
|
||||
struct mcpwm_group_t {
|
||||
int group_id; // group ID, index from 0
|
||||
mcpwm_hal_context_t hal; // HAL instance is at group level
|
||||
portMUX_TYPE spinlock; // group level spinlock
|
||||
uint32_t resolution_hz; // MCPWM group clock resolution
|
||||
esp_pm_lock_handle_t pm_lock; // power management lock
|
||||
mcpwm_timer_clock_source_t clk_src; // source clock
|
||||
mcpwm_cap_timer_t *cap_timer; // mcpwm capture timers
|
||||
mcpwm_timer_t *timers[SOC_MCPWM_TIMERS_PER_GROUP]; // mcpwm timer array
|
||||
mcpwm_oper_t *operators[SOC_MCPWM_OPERATORS_PER_GROUP]; // mcpwm operator array
|
||||
mcpwm_gpio_fault_t *gpio_faults[SOC_MCPWM_GPIO_FAULTS_PER_GROUP]; // mcpwm fault detectors array
|
||||
mcpwm_gpio_sync_src_t *gpio_sync_srcs[SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP]; // mcpwm gpio sync array
|
||||
#if CONFIG_PM_ENABLE
|
||||
char pm_lock_name[MCPWM_PM_LOCK_NAME_LEN_MAX]; // pm lock name
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MCPWM_TIMER_FSM_INIT,
|
||||
MCPWM_TIMER_FSM_ENABLE,
|
||||
} mcpwm_timer_fsm_t;
|
||||
|
||||
struct mcpwm_timer_t {
|
||||
int timer_id; // timer ID, index from 0
|
||||
mcpwm_group_t *group; // which group the timer belongs to
|
||||
mcpwm_timer_fsm_t fsm; // driver FSM
|
||||
portMUX_TYPE spinlock; // spin lock
|
||||
intr_handle_t intr; // interrupt handle
|
||||
uint32_t resolution_hz; // resolution of the timer
|
||||
uint32_t peak_ticks; // peak ticks that the timer could reach to
|
||||
mcpwm_timer_sync_src_t *sync_src; // timer sync_src
|
||||
mcpwm_timer_count_mode_t count_mode; // count mode
|
||||
mcpwm_timer_event_cb_t on_full; // callback function when MCPWM timer counts to peak value
|
||||
mcpwm_timer_event_cb_t on_empty; // callback function when MCPWM timer counts to zero
|
||||
mcpwm_timer_event_cb_t on_stop; // callback function when MCPWM timer stops
|
||||
void *user_data; // user data which would be passed to the timer callbacks
|
||||
};
|
||||
|
||||
struct mcpwm_oper_t {
|
||||
int oper_id; // operator ID, index from 0
|
||||
mcpwm_group_t *group; // which group the timer belongs to
|
||||
mcpwm_timer_t *timer; // which timer is connected to this operator
|
||||
portMUX_TYPE spinlock; // spin lock
|
||||
intr_handle_t intr; // interrupt handle
|
||||
mcpwm_gen_t *generators[SOC_MCPWM_GENERATORS_PER_OPERATOR]; // mcpwm generator array
|
||||
mcpwm_cmpr_t *comparators[SOC_MCPWM_COMPARATORS_PER_OPERATOR]; // mcpwm comparator array
|
||||
mcpwm_soft_fault_t *soft_fault; // mcpwm software fault
|
||||
mcpwm_operator_brake_mode_t brake_mode_on_soft_fault; // brake mode on software triggered fault
|
||||
mcpwm_operator_brake_mode_t brake_mode_on_gpio_fault[SOC_MCPWM_GPIO_FAULTS_PER_GROUP]; // brake mode on GPIO triggered faults
|
||||
uint32_t deadtime_resolution_hz; // resolution of deadtime submodule
|
||||
mcpwm_brake_event_cb_t on_brake_cbc; // callback function which would be invoked when mcpwm operator goes into trip zone
|
||||
mcpwm_brake_event_cb_t on_brake_ost; // callback function which would be invoked when mcpwm operator goes into trip zone
|
||||
void *user_data; // user data which would be passed to the trip zone callback
|
||||
};
|
||||
|
||||
struct mcpwm_cmpr_t {
|
||||
int cmpr_id; // comparator ID, index from 0
|
||||
mcpwm_oper_t *operator; // which operator that the comparator resides in
|
||||
intr_handle_t intr; // interrupt handle
|
||||
portMUX_TYPE spinlock; // spin lock
|
||||
uint32_t compare_ticks; // compare value of this comparator
|
||||
mcpwm_compare_event_cb_t on_reach; // ISR callback function which would be invoked on timer counter reaches compare value
|
||||
void *user_data; // user data which would be passed to the comparator callbacks
|
||||
};
|
||||
|
||||
struct mcpwm_gen_t {
|
||||
int gen_id; // generator ID, index from 0
|
||||
mcpwm_oper_t *operator; // which operator that the generator resides in
|
||||
int gen_gpio_num; // GPIO number used by the generator
|
||||
portMUX_TYPE spinlock; // spin lock
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MCPWM_FAULT_TYPE_GPIO, // external GPIO fault
|
||||
MCPWM_FAULT_TYPE_SOFT, // software fault
|
||||
} mcpwm_fault_type_t;
|
||||
|
||||
struct mcpwm_fault_t {
|
||||
mcpwm_group_t *group; // which group the fault belongs to
|
||||
mcpwm_fault_type_t type; // fault type
|
||||
esp_err_t (*del)(mcpwm_fault_t *fault);
|
||||
};
|
||||
|
||||
struct mcpwm_gpio_fault_t {
|
||||
mcpwm_fault_t base; // base class
|
||||
int fault_id; // fault detector ID, index from 0
|
||||
int gpio_num; // GPIO number of fault detector
|
||||
intr_handle_t intr; // interrupt handle
|
||||
mcpwm_fault_event_cb_t on_fault_enter; // ISR callback function that would be invoked when fault signal got triggered
|
||||
mcpwm_fault_event_cb_t on_fault_exit; // ISR callback function that would be invoked when fault signal got clear
|
||||
void *user_data; // user data which would be passed to the isr_cb
|
||||
};
|
||||
|
||||
struct mcpwm_soft_fault_t {
|
||||
mcpwm_fault_t base; // base class
|
||||
mcpwm_oper_t *operator; // the operator where the soft fault allocated from
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MCPWM_SYNC_TYPE_TIMER, // sync event generated by MCPWM timer count event
|
||||
MCPWM_SYNC_TYPE_GPIO, // sync event generated by GPIO
|
||||
MCPWM_SYNC_TYPE_SOFT, // sync event generated by software
|
||||
} mcpwm_sync_src_type_t;
|
||||
|
||||
struct mcpwm_sync_t {
|
||||
mcpwm_group_t *group; // which group the sync_src belongs to
|
||||
mcpwm_sync_src_type_t type; // sync_src type
|
||||
esp_err_t (*del)(mcpwm_sync_t *sync_src);
|
||||
};
|
||||
|
||||
struct mcpwm_gpio_sync_src_t {
|
||||
mcpwm_sync_t base; // base class
|
||||
int sync_id; // sync signal ID
|
||||
int gpio_num; // GPIO number
|
||||
};
|
||||
|
||||
struct mcpwm_timer_sync_src_t {
|
||||
mcpwm_sync_t base; // base class
|
||||
mcpwm_timer_t *timer; // timer handle, where this sync_src allocated from
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MCPWM_SOFT_SYNC_FROM_NONE, // the software sync event generator has not been assigned
|
||||
MCPWM_SOFT_SYNC_FROM_TIMER, // the software sync event is generated by MCPWM timer
|
||||
MCPWM_SOFT_SYNC_FROM_CAP, // the software sync event is generated by MCPWM capture timer
|
||||
} mcpwm_soft_sync_source_t;
|
||||
|
||||
struct mcpwm_soft_sync_src_t {
|
||||
mcpwm_sync_t base; // base class
|
||||
mcpwm_soft_sync_source_t soft_sync_from; // where the software sync event is generated by
|
||||
union {
|
||||
mcpwm_timer_t *timer; // soft sync is generated by which MCPWM timer
|
||||
mcpwm_cap_timer_t *cap_timer; // soft sync is generated by which MCPWM capture timer
|
||||
};
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MCPWM_CAP_TIMER_FSM_INIT,
|
||||
MCPWM_CAP_TIMER_FSM_ENABLE,
|
||||
} mcpwm_cap_timer_fsm_t;
|
||||
|
||||
struct mcpwm_cap_timer_t {
|
||||
mcpwm_group_t *group; // which group the capture timer belongs to
|
||||
portMUX_TYPE spinlock; // spin lock, to prevent concurrently accessing capture timer level resources, including registers
|
||||
uint32_t resolution_hz; // resolution of capture timer
|
||||
mcpwm_cap_timer_fsm_t fsm; // driver FSM
|
||||
esp_pm_lock_handle_t pm_lock; // power management lock
|
||||
mcpwm_cap_channel_t *cap_channels[SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER]; // capture channel array
|
||||
};
|
||||
|
||||
struct mcpwm_cap_channel_t {
|
||||
int cap_chan_id; // capture channel ID, index from 0
|
||||
mcpwm_cap_timer_t *cap_timer; // which capture timer that the channel resides in
|
||||
uint32_t prescale; // prescale of capture signal
|
||||
int gpio_num; // GPIO number used by the channel
|
||||
intr_handle_t intr; // Interrupt handle
|
||||
mcpwm_capture_event_cb_t on_cap; // Callback function which would be invoked in capture interrupt routine
|
||||
void *user_data; // user data which would be passed to the capture callback
|
||||
};
|
||||
|
||||
mcpwm_group_t *mcpwm_acquire_group_handle(int group_id);
|
||||
void mcpwm_release_group_handle(mcpwm_group_t *group);
|
||||
esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, mcpwm_timer_clock_source_t clk_src);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
297
components/driver/mcpwm/mcpwm_sync.c
Normal file
297
components/driver/mcpwm/mcpwm_sync.c
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/mcpwm_sync.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static esp_err_t mcpwm_del_timer_sync_src(mcpwm_sync_t *sync_src);
|
||||
static esp_err_t mcpwm_del_gpio_sync_src(mcpwm_sync_t *sync_src);
|
||||
static esp_err_t mcpwm_del_soft_sync_src(mcpwm_sync_t *sync_src);
|
||||
|
||||
static esp_err_t mcpwm_timer_sync_src_register_to_timer(mcpwm_timer_sync_src_t *timer_sync_src, mcpwm_timer_t *timer)
|
||||
{
|
||||
bool new_sync = false;
|
||||
portENTER_CRITICAL(&timer->spinlock);
|
||||
if (!timer->sync_src) {
|
||||
new_sync = true;
|
||||
timer->sync_src = timer_sync_src;
|
||||
}
|
||||
portEXIT_CRITICAL(&timer->spinlock);
|
||||
ESP_RETURN_ON_FALSE(new_sync, ESP_ERR_INVALID_STATE, TAG, "timer sync_src already installed for timer (%d,%d)",
|
||||
timer->group->group_id, timer->timer_id);
|
||||
|
||||
timer_sync_src->timer = timer;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mcpwm_timer_sync_src_unregister_from_timer(mcpwm_timer_sync_src_t *timer_sync_src)
|
||||
{
|
||||
mcpwm_timer_t *timer = timer_sync_src->timer;
|
||||
|
||||
portENTER_CRITICAL(&timer->spinlock);
|
||||
timer->sync_src = NULL;
|
||||
portEXIT_CRITICAL(&timer->spinlock);
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_timer_sync_src_destory(mcpwm_timer_sync_src_t *timer_sync_src)
|
||||
{
|
||||
if (timer_sync_src->timer) {
|
||||
mcpwm_timer_sync_src_unregister_from_timer(timer_sync_src);
|
||||
}
|
||||
free(timer_sync_src);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_timer_sync_src(mcpwm_timer_handle_t timer, const mcpwm_timer_sync_src_config_t *config, mcpwm_sync_handle_t *ret_sync)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_timer_sync_src_t *timer_sync_src = NULL;
|
||||
ESP_GOTO_ON_FALSE(timer && config && ret_sync, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
timer_sync_src = heap_caps_calloc(1, sizeof(mcpwm_timer_sync_src_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(timer_sync_src, ESP_ERR_NO_MEM, err, TAG, "no mem for timer sync_src");
|
||||
|
||||
ESP_GOTO_ON_ERROR(mcpwm_timer_sync_src_register_to_timer(timer_sync_src, timer), err, TAG, "register timer sync_src failed");
|
||||
mcpwm_group_t *group = timer->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int timer_id = timer->timer_id;
|
||||
|
||||
if (config->flags.propagate_input_sync) {
|
||||
mcpwm_ll_timer_propagate_input_sync(hal->dev, timer_id);
|
||||
} else {
|
||||
switch (config->timer_event) {
|
||||
case MCPWM_TIMER_EVENT_EMPTY:
|
||||
mcpwm_ll_timer_sync_out_on_timer_event(hal->dev, timer_id, MCPWM_TIMER_EVENT_EMPTY);
|
||||
break;
|
||||
case MCPWM_TIMER_EVENT_FULL:
|
||||
mcpwm_ll_timer_sync_out_on_timer_event(hal->dev, timer_id, MCPWM_TIMER_EVENT_FULL);
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown timer sync event:%d", config->timer_event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
timer_sync_src->base.group = group;
|
||||
timer_sync_src->base.type = MCPWM_SYNC_TYPE_TIMER;
|
||||
timer_sync_src->base.del = mcpwm_del_timer_sync_src;
|
||||
*ret_sync = &timer_sync_src->base;
|
||||
ESP_LOGD(TAG, "new timer sync_src at %p in timer (%d,%d), event:%c", timer_sync_src, group->group_id, timer_id, "EP?"[config->timer_event]);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (timer_sync_src) {
|
||||
mcpwm_timer_sync_src_destory(timer_sync_src);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_del_timer_sync_src(mcpwm_sync_t *sync_src)
|
||||
{
|
||||
mcpwm_timer_sync_src_t *timer_sync_src = __containerof(sync_src, mcpwm_timer_sync_src_t, base);
|
||||
mcpwm_timer_t *timer = timer_sync_src->timer;
|
||||
int timer_id = timer->timer_id;
|
||||
mcpwm_group_t *group = sync_src->group;
|
||||
|
||||
mcpwm_ll_timer_disable_sync_out(group->hal.dev, timer_id);
|
||||
ESP_LOGD(TAG, "del timer sync_src in timer (%d,%d)", group->group_id, timer_id);
|
||||
ESP_RETURN_ON_ERROR(mcpwm_timer_sync_src_destory(timer_sync_src), TAG, "destory timer sync_src failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_gpio_sync_src_register_to_group(mcpwm_gpio_sync_src_t *gpio_sync_src, int group_id)
|
||||
{
|
||||
mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id);
|
||||
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id);
|
||||
|
||||
int sync_id = -1;
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
for (int i = 0; i < SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP; i++) {
|
||||
if (!group->gpio_sync_srcs[i]) {
|
||||
sync_id = i;
|
||||
group->gpio_sync_srcs[i] = gpio_sync_src;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
if (sync_id < 0) {
|
||||
mcpwm_release_group_handle(group);
|
||||
group = NULL;
|
||||
} else {
|
||||
gpio_sync_src->base.group = group;
|
||||
gpio_sync_src->sync_id = sync_id;
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(sync_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free gpio sync_src in group (%d)", group_id);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mcpwm_gpio_sync_src_unregister_from_group(mcpwm_gpio_sync_src_t *gpio_sync_src)
|
||||
{
|
||||
mcpwm_group_t *group = gpio_sync_src->base.group;
|
||||
int sync_id = gpio_sync_src->sync_id;
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
group->gpio_sync_srcs[sync_id] = NULL;
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
// sync_src has a reference on group, release it now
|
||||
mcpwm_release_group_handle(group);
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_gpio_sync_src_destory(mcpwm_gpio_sync_src_t *gpio_sync_src)
|
||||
{
|
||||
if (gpio_sync_src->base.group) {
|
||||
mcpwm_gpio_sync_src_unregister_from_group(gpio_sync_src);
|
||||
}
|
||||
free(gpio_sync_src);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_gpio_sync_src(const mcpwm_gpio_sync_src_config_t *config, mcpwm_sync_handle_t *ret_sync)
|
||||
{
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_gpio_sync_src_t *gpio_sync_src = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_sync, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->group_id < SOC_MCPWM_GROUPS && config->group_id >= 0, ESP_ERR_INVALID_ARG,
|
||||
err, TAG, "invalid group ID:%d", config->group_id);
|
||||
|
||||
gpio_sync_src = heap_caps_calloc(1, sizeof(mcpwm_gpio_sync_src_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(gpio_sync_src, ESP_ERR_NO_MEM, err, TAG, "no mem for gpio sync_src");
|
||||
|
||||
ESP_GOTO_ON_ERROR(mcpwm_gpio_sync_src_register_to_group(gpio_sync_src, config->group_id), err, TAG, "register gpio sync_src failed");
|
||||
mcpwm_group_t *group = gpio_sync_src->base.group;
|
||||
int group_id = group->group_id;
|
||||
int sync_id = gpio_sync_src->sync_id;
|
||||
|
||||
// GPIO configuration
|
||||
gpio_config_t gpio_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
|
||||
.pin_bit_mask = (1ULL << config->gpio_num),
|
||||
.pull_down_en = config->flags.pull_down,
|
||||
.pull_up_en = config->flags.pull_up,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config sync GPIO failed");
|
||||
esp_rom_gpio_connect_in_signal(config->gpio_num, mcpwm_periph_signals.groups[group_id].gpio_synchros[sync_id].sync_sig, 0);
|
||||
|
||||
// different ext sync share the same config register, using a group level spin lock
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_invert_gpio_sync_input(group->hal.dev, sync_id, config->flags.active_neg);
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
// fill in other operator members
|
||||
gpio_sync_src->base.type = MCPWM_SYNC_TYPE_GPIO;
|
||||
gpio_sync_src->gpio_num = config->gpio_num;
|
||||
gpio_sync_src->base.del = mcpwm_del_gpio_sync_src;
|
||||
*ret_sync = &gpio_sync_src->base;
|
||||
ESP_LOGD(TAG, "new gpio sync_src (%d,%d) at %p, GPIO:%d", group_id, sync_id, gpio_sync_src, config->gpio_num);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (gpio_sync_src) {
|
||||
mcpwm_gpio_sync_src_destory(gpio_sync_src);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_del_gpio_sync_src(mcpwm_sync_t *sync_src)
|
||||
{
|
||||
mcpwm_gpio_sync_src_t *gpio_sync_src = __containerof(sync_src, mcpwm_gpio_sync_src_t, base);
|
||||
mcpwm_group_t *group = sync_src->group;
|
||||
|
||||
ESP_LOGD(TAG, "del gpio sync_src (%d,%d)", group->group_id, gpio_sync_src->sync_id);
|
||||
gpio_reset_pin(gpio_sync_src->gpio_num);
|
||||
|
||||
// recycle memory resource
|
||||
ESP_RETURN_ON_ERROR(mcpwm_gpio_sync_src_destory(gpio_sync_src), TAG, "destory GPIO sync_src failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_soft_sync_src(const mcpwm_soft_sync_config_t *config, mcpwm_sync_handle_t *ret_sync)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_soft_sync_src_t *soft_sync = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_sync, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
soft_sync = heap_caps_calloc(1, sizeof(mcpwm_soft_sync_src_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(soft_sync, ESP_ERR_NO_MEM, err, TAG, "no mem for soft sync");
|
||||
|
||||
// fill in other sync member
|
||||
soft_sync->soft_sync_from = MCPWM_SOFT_SYNC_FROM_NONE;
|
||||
soft_sync->base.type = MCPWM_SYNC_TYPE_SOFT;
|
||||
soft_sync->base.del = mcpwm_del_soft_sync_src;
|
||||
*ret_sync = &soft_sync->base;
|
||||
ESP_LOGD(TAG, "new soft sync at %p", soft_sync);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (soft_sync) {
|
||||
free(soft_sync);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_del_soft_sync_src(mcpwm_sync_t *sync_src)
|
||||
{
|
||||
mcpwm_soft_sync_src_t *soft_sync = __containerof(sync_src, mcpwm_soft_sync_src_t, base);
|
||||
ESP_LOGD(TAG, "del soft sync %p", soft_sync);
|
||||
free(soft_sync);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_del_sync_src(mcpwm_sync_handle_t sync_src)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(sync_src, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return sync_src->del(sync_src);
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_soft_sync_activate(mcpwm_sync_handle_t sync_src)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(sync_src, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(sync_src->type == MCPWM_SYNC_TYPE_SOFT, ESP_ERR_INVALID_ARG, TAG, "not a valid soft sync");
|
||||
mcpwm_group_t *group = sync_src->group;
|
||||
mcpwm_soft_sync_src_t *soft_sync = __containerof(sync_src, mcpwm_soft_sync_src_t, base);
|
||||
|
||||
switch (soft_sync->soft_sync_from) {
|
||||
case MCPWM_SOFT_SYNC_FROM_TIMER: {
|
||||
mcpwm_timer_t *timer = soft_sync->timer;
|
||||
mcpwm_ll_timer_trigger_soft_sync(group->hal.dev, timer->timer_id);
|
||||
break;
|
||||
}
|
||||
case MCPWM_SOFT_SYNC_FROM_CAP: {
|
||||
mcpwm_ll_capture_trigger_sw_sync(group->hal.dev);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "no soft sync generator is assigned");
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
364
components/driver/mcpwm/mcpwm_timer.c
Normal file
364
components/driver/mcpwm/mcpwm_timer.c
Normal file
@ -0,0 +1,364 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/mcpwm_ll.h"
|
||||
#include "driver/mcpwm_timer.h"
|
||||
#include "esp_private/mcpwm.h"
|
||||
#include "mcpwm_private.h"
|
||||
|
||||
static const char *TAG = "mcpwm";
|
||||
|
||||
static void mcpwm_timer_default_isr(void *args);
|
||||
|
||||
static esp_err_t mcpwm_timer_register_to_group(mcpwm_timer_t *timer, int group_id)
|
||||
{
|
||||
mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id);
|
||||
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id);
|
||||
|
||||
int timer_id = -1;
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
for (int i = 0; i < SOC_MCPWM_TIMERS_PER_GROUP; i++) {
|
||||
if (!group->timers[i]) {
|
||||
timer_id = i;
|
||||
group->timers[i] = timer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
if (timer_id < 0) {
|
||||
mcpwm_release_group_handle(group);
|
||||
group = NULL;
|
||||
} else {
|
||||
timer->group = group;
|
||||
timer->timer_id = timer_id;
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(timer_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free timer in group (%d)", group_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mcpwm_timer_unregister_from_group(mcpwm_timer_t *timer)
|
||||
{
|
||||
mcpwm_group_t *group = timer->group;
|
||||
int timer_id = timer->timer_id;
|
||||
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
group->timers[timer_id] = NULL;
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
// timer has a reference on group, release it now
|
||||
mcpwm_release_group_handle(group);
|
||||
}
|
||||
|
||||
static esp_err_t mcpwm_timer_destory(mcpwm_timer_t *timer)
|
||||
{
|
||||
if (timer->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_free(timer->intr), TAG, "uninstall interrupt service failed");
|
||||
}
|
||||
if (timer->group) {
|
||||
mcpwm_timer_unregister_from_group(timer);
|
||||
}
|
||||
free(timer);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle_t *ret_timer)
|
||||
{
|
||||
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
mcpwm_timer_t *timer = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_timer, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->group_id < SOC_MCPWM_GROUPS && config->group_id >= 0, ESP_ERR_INVALID_ARG,
|
||||
err, TAG, "invalid group ID:%d", config->group_id);
|
||||
|
||||
timer = heap_caps_calloc(1, sizeof(mcpwm_timer_t), MCPWM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(timer, ESP_ERR_NO_MEM, err, TAG, "no mem for timer");
|
||||
|
||||
ESP_GOTO_ON_ERROR(mcpwm_timer_register_to_group(timer, config->group_id), err, TAG, "register timer failed");
|
||||
mcpwm_group_t *group = timer->group;
|
||||
int group_id = group->group_id;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int timer_id = timer->timer_id;
|
||||
// select the clock source
|
||||
ESP_GOTO_ON_ERROR(mcpwm_select_periph_clock(group, config->clk_src), err, TAG, "set group clock failed");
|
||||
// reset the timer to a determined state
|
||||
mcpwm_hal_timer_reset(hal, timer_id);
|
||||
// set timer resolution
|
||||
uint32_t prescale = group->resolution_hz / config->resolution_hz;
|
||||
mcpwm_ll_timer_set_clock_prescale(hal->dev, timer_id, prescale);
|
||||
timer->resolution_hz = group->resolution_hz / prescale;
|
||||
if (timer->resolution_hz != config->resolution_hz) {
|
||||
ESP_LOGW(TAG, "adjust timer resolution to %uHz", timer->resolution_hz);
|
||||
}
|
||||
|
||||
// set the peak tickes that the timer can reach to
|
||||
timer->count_mode = config->count_mode;
|
||||
uint32_t peak_ticks = config->period_ticks;
|
||||
if (timer->count_mode == MCPWM_TIMER_COUNT_MODE_UP_DOWN) {
|
||||
peak_ticks /= 2; // in symmetric mode, peak_ticks = period_ticks / 2
|
||||
}
|
||||
timer->peak_ticks = peak_ticks;
|
||||
mcpwm_ll_timer_set_peak(hal->dev, timer_id, peak_ticks, timer->count_mode == MCPWM_TIMER_COUNT_MODE_UP_DOWN);
|
||||
// set count direction
|
||||
mcpwm_ll_timer_set_count_mode(hal->dev, timer_id, timer->count_mode);
|
||||
// what time is allowed to update the period
|
||||
mcpwm_ll_timer_enable_update_period_on_sync(hal->dev, timer_id, config->flags.update_period_on_sync);
|
||||
mcpwm_ll_timer_enable_update_period_on_tez(hal->dev, timer_id, config->flags.update_period_on_empty);
|
||||
|
||||
// fill in other timer specific members
|
||||
timer->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
timer->fsm = MCPWM_TIMER_FSM_INIT;
|
||||
*ret_timer = timer;
|
||||
ESP_LOGD(TAG, "new timer(%d,%d) at %p, resolution:%uHz, peak:%u, count_mod:%c",
|
||||
group_id, timer_id, timer, timer->resolution_hz, timer->peak_ticks, "SUDB"[timer->count_mode]);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (timer) {
|
||||
mcpwm_timer_destory(timer);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_del_timer(mcpwm_timer_handle_t timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
// check child resources are in free state
|
||||
ESP_RETURN_ON_FALSE(!timer->sync_src, ESP_ERR_INVALID_STATE, TAG, "timer sync_src still in working");
|
||||
ESP_RETURN_ON_FALSE(timer->fsm == MCPWM_TIMER_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "timer not in init state");
|
||||
mcpwm_group_t *group = timer->group;
|
||||
int timer_id = timer->timer_id;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
|
||||
// disable and clear the pending interrupt
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_TIMER_MASK(timer_id), false);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, MCPWM_LL_EVENT_TIMER_MASK(timer_id));
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
ESP_LOGD(TAG, "del timer (%d,%d)", group->group_id, timer_id);
|
||||
// recycle memory resource
|
||||
ESP_RETURN_ON_ERROR(mcpwm_timer_destory(timer), TAG, "destory timer failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_timer_register_event_callbacks(mcpwm_timer_handle_t timer, const mcpwm_timer_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(timer->fsm == MCPWM_TIMER_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "timer not in init state");
|
||||
mcpwm_group_t *group = timer->group;
|
||||
int group_id = group->group_id;
|
||||
int timer_id = timer->timer_id;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
|
||||
#if CONFIG_MCWPM_ISR_IRAM_SAFE
|
||||
if (cbs->on_empty) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_empty), ESP_ERR_INVALID_ARG, TAG, "on_empty callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_full) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_full), ESP_ERR_INVALID_ARG, TAG, "on_full callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_stop) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_stop), ESP_ERR_INVALID_ARG, TAG, "on_stop callback not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
// lazy install interrupt service
|
||||
if (!timer->intr) {
|
||||
int isr_flags = MCPWM_INTR_ALLOC_FLAG;
|
||||
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(mcpwm_periph_signals.groups[group_id].irq_id, isr_flags,
|
||||
(uint32_t)mcpwm_ll_intr_get_status_reg(hal->dev), MCPWM_LL_EVENT_TIMER_MASK(timer_id),
|
||||
mcpwm_timer_default_isr, timer, &timer->intr), TAG, "install interrupt service for timer failed");
|
||||
}
|
||||
|
||||
// enable/disable interrupt events
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_TIMER_FULL(timer_id), cbs->on_full != NULL);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_TIMER_EMPTY(timer_id), cbs->on_empty != NULL);
|
||||
mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_TIMER_STOP(timer_id), cbs->on_stop != NULL);
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
timer->on_stop = cbs->on_stop;
|
||||
timer->on_full = cbs->on_full;
|
||||
timer->on_empty = cbs->on_empty;
|
||||
timer->user_data = user_data;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_timer_get_phase(mcpwm_timer_handle_t timer, uint32_t *count_value, mcpwm_timer_direction_t *direction)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer && count_value && direction, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_group_t *group = timer->group;
|
||||
int timer_id = timer->timer_id;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
|
||||
portENTER_CRITICAL(&timer->spinlock);
|
||||
*count_value = mcpwm_ll_timer_get_count_value(hal->dev, timer_id);
|
||||
*direction = mcpwm_ll_timer_get_count_direction(hal->dev, timer_id);
|
||||
portEXIT_CRITICAL(&timer->spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_timer_enable(mcpwm_timer_handle_t timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(timer->fsm == MCPWM_TIMER_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "timer not in init state");
|
||||
mcpwm_group_t *group = timer->group;
|
||||
if (timer->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_enable(timer->intr), TAG, "enable interrupt failed");
|
||||
}
|
||||
if (group->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(group->pm_lock), TAG, "acquire pm lock failed");
|
||||
}
|
||||
timer->fsm = MCPWM_TIMER_FSM_ENABLE;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_timer_disable(mcpwm_timer_handle_t timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(timer->fsm == MCPWM_TIMER_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "timer not in enable state");
|
||||
mcpwm_group_t *group = timer->group;
|
||||
if (timer->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_disable(timer->intr), TAG, "disable interrupt failed");
|
||||
}
|
||||
if (group->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_release(group->pm_lock), TAG, "acquire pm lock failed");
|
||||
}
|
||||
timer->fsm = MCPWM_TIMER_FSM_INIT;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_timer_start_stop(mcpwm_timer_handle_t timer, mcpwm_timer_start_stop_cmd_t command)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(timer->fsm == MCPWM_TIMER_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "timer not in enable state");
|
||||
mcpwm_group_t *group = timer->group;
|
||||
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
mcpwm_ll_timer_set_start_stop_command(group->hal.dev, timer->timer_id, command);
|
||||
portEXIT_CRITICAL_SAFE(&timer->spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_timer_set_phase_on_sync(mcpwm_timer_handle_t timer, const mcpwm_timer_sync_phase_config_t *config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
mcpwm_group_t *group = timer->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int group_id = group->group_id;
|
||||
int timer_id = timer->timer_id;
|
||||
mcpwm_sync_handle_t sync_source = config->sync_src;
|
||||
|
||||
// enable sync feature and set sync phase
|
||||
if (sync_source) {
|
||||
ESP_RETURN_ON_FALSE(config->count_value < MCPWM_LL_MAX_COUNT_VALUE, ESP_ERR_INVALID_ARG, TAG, "invalid sync count value");
|
||||
switch (sync_source->type) {
|
||||
case MCPWM_SYNC_TYPE_TIMER: {
|
||||
ESP_RETURN_ON_FALSE(group == sync_source->group, ESP_ERR_INVALID_ARG, TAG, "timer and sync source are not in the same group");
|
||||
mcpwm_timer_sync_src_t *timer_sync_src = __containerof(sync_source, mcpwm_timer_sync_src_t, base);
|
||||
mcpwm_ll_timer_set_timer_sync_input(hal->dev, timer_id, timer_sync_src->timer->timer_id);
|
||||
ESP_LOGD(TAG, "enable sync to timer (%d,%d) for timer (%d,%d)",
|
||||
group_id, timer_sync_src->timer->timer_id, group_id, timer_id);
|
||||
break;
|
||||
}
|
||||
case MCPWM_SYNC_TYPE_GPIO: {
|
||||
ESP_RETURN_ON_FALSE(group == sync_source->group, ESP_ERR_INVALID_ARG, TAG, "timer and sync source are not in the same group");
|
||||
mcpwm_gpio_sync_src_t *gpio_sync_src = __containerof(sync_source, mcpwm_gpio_sync_src_t, base);
|
||||
mcpwm_ll_timer_set_gpio_sync_input(hal->dev, timer_id, gpio_sync_src->sync_id);
|
||||
ESP_LOGD(TAG, "enable sync to gpio (%d) for timer (%d,%d)",
|
||||
gpio_sync_src->gpio_num, group_id, timer_id);
|
||||
break;
|
||||
}
|
||||
case MCPWM_SYNC_TYPE_SOFT: {
|
||||
mcpwm_soft_sync_src_t *soft_sync = __containerof(sync_source, mcpwm_soft_sync_src_t, base);
|
||||
if (soft_sync->soft_sync_from == MCPWM_SOFT_SYNC_FROM_TIMER && soft_sync->timer != timer) {
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "soft sync already used by another timer");
|
||||
}
|
||||
soft_sync->soft_sync_from = MCPWM_SOFT_SYNC_FROM_TIMER;
|
||||
soft_sync->timer = timer;
|
||||
soft_sync->base.group = group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mcpwm_ll_timer_set_sync_phase_direction(hal->dev, timer_id, config->direction);
|
||||
mcpwm_ll_timer_set_sync_phase_value(hal->dev, timer_id, config->count_value);
|
||||
mcpwm_ll_timer_enable_sync_input(hal->dev, timer_id, true);
|
||||
} else { // disable sync feature
|
||||
mcpwm_ll_timer_enable_sync_input(hal->dev, timer_id, false);
|
||||
ESP_LOGD(TAG, "disable sync for timer (%d,%d)", group_id, timer_id);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR mcpwm_timer_default_isr(void *args)
|
||||
{
|
||||
mcpwm_timer_t *timer = (mcpwm_timer_t *)args;
|
||||
mcpwm_group_t *group = timer->group;
|
||||
mcpwm_hal_context_t *hal = &group->hal;
|
||||
int timer_id = timer->timer_id;
|
||||
bool need_yield = false;
|
||||
uint32_t status = mcpwm_ll_intr_get_status(hal->dev);
|
||||
mcpwm_ll_intr_clear_status(hal->dev, status & MCPWM_LL_EVENT_TIMER_MASK(timer_id));
|
||||
|
||||
mcpwm_timer_event_data_t edata = {
|
||||
.direction = mcpwm_ll_timer_get_count_direction(hal->dev, timer_id),
|
||||
.count_value = mcpwm_ll_timer_get_count_value(hal->dev, timer_id),
|
||||
};
|
||||
|
||||
if (status & MCPWM_LL_EVENT_TIMER_STOP(timer_id)) {
|
||||
mcpwm_timer_event_cb_t cb = timer->on_stop;
|
||||
if (cb) {
|
||||
if (cb(timer, &edata, timer->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status & MCPWM_LL_EVENT_TIMER_FULL(timer_id)) {
|
||||
mcpwm_timer_event_cb_t cb = timer->on_full;
|
||||
if (cb) {
|
||||
if (cb(timer, &edata, timer->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status & MCPWM_LL_EVENT_TIMER_EMPTY(timer_id)) {
|
||||
mcpwm_timer_event_cb_t cb = timer->on_empty;
|
||||
if (cb) {
|
||||
if (cb(timer, &edata, timer->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
18
components/driver/test_apps/mcpwm/CMakeLists.txt
Normal file
18
components/driver/test_apps/mcpwm/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(mcpwm_test)
|
||||
|
||||
if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
||||
add_custom_target(check_test_app_sections ALL
|
||||
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
||||
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/
|
||||
--elf-file ${CMAKE_BINARY_DIR}/mcpwm_test.elf
|
||||
find-refs
|
||||
--from-sections=.iram0.text
|
||||
--to-sections=.flash.text,.flash.rodata
|
||||
--exit-code
|
||||
DEPENDS ${elf}
|
||||
)
|
||||
endif()
|
2
components/driver/test_apps/mcpwm/README.md
Normal file
2
components/driver/test_apps/mcpwm/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- |
|
14
components/driver/test_apps/mcpwm/main/CMakeLists.txt
Normal file
14
components/driver/test_apps/mcpwm/main/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_mcpwm_cap.c"
|
||||
"test_mcpwm_cmpr.c"
|
||||
"test_mcpwm_fault.c"
|
||||
"test_mcpwm_gen.c"
|
||||
"test_mcpwm_oper.c"
|
||||
"test_mcpwm_sync.c"
|
||||
"test_mcpwm_timer.c"
|
||||
"test_mcpwm_utils.c")
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(SRCS ${srcs}
|
||||
WHOLE_ARCHIVE)
|
51
components/driver/test_apps/mcpwm/main/test_app_main.c
Normal file
51
components/driver/test_apps/mcpwm/main/test_app_main.c
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// Some resources are lazy allocated in GPTimer driver, the threshold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// __ __ ____ ______ ____ __ _____ _
|
||||
// | \/ |/ ___| _ \ \ / / \/ | |_ _|__ ___| |_
|
||||
// | |\/| | | | |_) \ \ /\ / /| |\/| | | |/ _ \/ __| __|
|
||||
// | | | | |___| __/ \ V V / | | | | | | __/\__ \ |_
|
||||
// |_| |_|\____|_| \_/\_/ |_| |_| |_|\___||___/\__|
|
||||
printf(" __ __ ____ ______ ____ __ _____ _\r\n");
|
||||
printf("| \\/ |/ ___| _ \\ \\ / / \\/ | |_ _|__ ___| |_\r\n");
|
||||
printf("| |\\/| | | | |_) \\ \\ /\\ / /| |\\/| | | |/ _ \\/ __| __|\r\n");
|
||||
printf("| | | | |___| __/ \\ V V / | | | | | | __/\\__ \\ |_\r\n");
|
||||
printf("|_| |_|\\____|_| \\_/\\_/ |_| |_| |_|\\___||___/\\__|\r\n");
|
||||
unity_run_menu();
|
||||
}
|
238
components/driver/test_apps/mcpwm/main/test_mcpwm_cap.c
Normal file
238
components/driver/test_apps/mcpwm/main/test_mcpwm_cap.c
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "unity.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
#include "driver/mcpwm_cap.h"
|
||||
#include "driver/mcpwm_sync.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "test_mcpwm_utils.h"
|
||||
|
||||
TEST_CASE("mcpwm_capture_install_uninstall", "[mcpwm]")
|
||||
{
|
||||
printf("install mcpwm capture timers\r\n");
|
||||
mcpwm_capture_timer_config_t cap_timer_config = {
|
||||
.clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,
|
||||
};
|
||||
int total_cap_timers = SOC_MCPWM_GROUPS * SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP;
|
||||
mcpwm_cap_timer_handle_t cap_timers[total_cap_timers];
|
||||
int k = 0;
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
cap_timer_config.group_id = i;
|
||||
for (int j = 0; j < SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP; j++) {
|
||||
TEST_ESP_OK(mcpwm_new_capture_timer(&cap_timer_config, &cap_timers[k++]));
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, mcpwm_new_capture_timer(&cap_timer_config, &cap_timers[0]));
|
||||
}
|
||||
|
||||
printf("install mcpwm capture channels\r\n");
|
||||
mcpwm_capture_channel_config_t cap_chan_config = {
|
||||
.gpio_num = 0,
|
||||
.prescale = 2,
|
||||
.flags.pos_edge = true,
|
||||
.flags.pull_up = true,
|
||||
};
|
||||
mcpwm_cap_channel_handle_t cap_channels[total_cap_timers][SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER];
|
||||
for (int i = 0; i < total_cap_timers; i++) {
|
||||
for (int j = 0; j < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER; j++) {
|
||||
TEST_ESP_OK(mcpwm_new_capture_channel(cap_timers[i], &cap_chan_config, &cap_channels[i][j]));
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, mcpwm_new_capture_channel(cap_timers[i], &cap_chan_config, &cap_channels[i][0]));
|
||||
}
|
||||
|
||||
printf("uninstall mcpwm capture channels and timers\r\n");
|
||||
for (int i = 0; i < total_cap_timers; i++) {
|
||||
for (int j = 0; j < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER; j++) {
|
||||
TEST_ESP_OK(mcpwm_del_capture_channel(cap_channels[i][j]));
|
||||
}
|
||||
TEST_ESP_OK(mcpwm_del_capture_timer(cap_timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_MCPWM_CALLBACK_ATTR
|
||||
static bool test_capture_callback(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_data_t *edata, void *user_data)
|
||||
{
|
||||
uint32_t *cap_value = (uint32_t *)user_data;
|
||||
if (edata->cap_edge == MCPWM_CAP_EDGE_NEG) {
|
||||
cap_value[1] = edata->cap_value;
|
||||
} else {
|
||||
cap_value[0] = edata->cap_value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_capture_ext_gpio", "[mcpwm]")
|
||||
{
|
||||
printf("install mcpwm capture timer\r\n");
|
||||
mcpwm_cap_timer_handle_t cap_timer = NULL;
|
||||
mcpwm_capture_timer_config_t cap_timer_config = {
|
||||
.clk_src = MCPWM_CAPTURE_CLK_SRC_APB,
|
||||
.group_id = 0,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_capture_timer(&cap_timer_config, &cap_timer));
|
||||
|
||||
const int cap_gpio = 0;
|
||||
// put the GPIO into a preset state
|
||||
gpio_set_level(cap_gpio, 0);
|
||||
|
||||
printf("install mcpwm capture channel\r\n");
|
||||
mcpwm_cap_channel_handle_t pps_channel;
|
||||
mcpwm_capture_channel_config_t cap_chan_config = {
|
||||
.gpio_num = cap_gpio,
|
||||
.prescale = 1,
|
||||
.flags.pos_edge = true,
|
||||
.flags.neg_edge = true,
|
||||
.flags.io_loop_back = true, // so we can use GPIO functions to simulate the external capture signal
|
||||
.flags.pull_up = true,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_capture_channel(cap_timer, &cap_chan_config, &pps_channel));
|
||||
|
||||
printf("install callback for capture channel\r\n");
|
||||
mcpwm_capture_event_callbacks_t cbs = {
|
||||
.on_cap = test_capture_callback,
|
||||
};
|
||||
uint32_t cap_value[2] = {0};
|
||||
TEST_ESP_OK(mcpwm_capture_channel_register_event_callbacks(pps_channel, &cbs, cap_value));
|
||||
|
||||
printf("enable and start capture timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_capture_timer_enable(cap_timer));
|
||||
TEST_ESP_OK(mcpwm_capture_timer_start(cap_timer));
|
||||
|
||||
printf("simulate GPIO capture signal\r\n");
|
||||
gpio_set_level(cap_gpio, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(cap_gpio, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
printf("capture value: Pos=%u, Neg=%u\r\n", cap_value[0], cap_value[1]);
|
||||
// Capture timer is clocked from APB by default
|
||||
uint32_t clk_src_res = esp_clk_apb_freq();
|
||||
TEST_ASSERT_UINT_WITHIN(100000, clk_src_res / 10, cap_value[1] - cap_value[0]);
|
||||
|
||||
printf("uninstall capture channel and timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_del_capture_channel(pps_channel));
|
||||
TEST_ESP_OK(mcpwm_capture_timer_disable(cap_timer));
|
||||
TEST_ESP_OK(mcpwm_del_capture_timer(cap_timer));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t cap_data[2];
|
||||
int cap_data_index;
|
||||
} test_soft_catch_user_data_t;
|
||||
|
||||
TEST_MCPWM_CALLBACK_ATTR
|
||||
static bool soft_cap_callback(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_data_t *data, void *user_data)
|
||||
{
|
||||
test_soft_catch_user_data_t *cbdata = (test_soft_catch_user_data_t *)user_data;
|
||||
cbdata->cap_data[cbdata->cap_data_index++] = data->cap_value;
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_capture_software_catch", "[mcpwm]")
|
||||
{
|
||||
printf("install mcpwm capture timer\r\n");
|
||||
mcpwm_cap_timer_handle_t cap_timer = NULL;
|
||||
mcpwm_capture_timer_config_t cap_timer_config = {
|
||||
.clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,
|
||||
.group_id = 0,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_capture_timer(&cap_timer_config, &cap_timer));
|
||||
|
||||
printf("install mcpwm capture channel\r\n");
|
||||
mcpwm_cap_channel_handle_t cap_channel = NULL;
|
||||
mcpwm_capture_channel_config_t cap_chan_config = {
|
||||
.gpio_num = -1, // don't need any GPIO, we use software to trigger a catch
|
||||
.prescale = 2,
|
||||
};
|
||||
test_soft_catch_user_data_t test_callback_data = {};
|
||||
TEST_ESP_OK(mcpwm_new_capture_channel(cap_timer, &cap_chan_config, &cap_channel));
|
||||
|
||||
printf("register event callback for capture channel\r\n");
|
||||
mcpwm_capture_event_callbacks_t cbs = {
|
||||
.on_cap = soft_cap_callback,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_capture_channel_register_event_callbacks(cap_channel, &cbs, &test_callback_data));
|
||||
|
||||
printf("enable and start capture timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_capture_timer_enable(cap_timer));
|
||||
TEST_ESP_OK(mcpwm_capture_timer_start(cap_timer));
|
||||
|
||||
printf("trigger software catch\r\n");
|
||||
TEST_ESP_OK(mcpwm_capture_channel_trigger_soft_catch(cap_channel));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
TEST_ESP_OK(mcpwm_capture_channel_trigger_soft_catch(cap_channel));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
// check user data
|
||||
TEST_ASSERT_EQUAL(2, test_callback_data.cap_data_index);
|
||||
uint32_t delta = test_callback_data.cap_data[1] - test_callback_data.cap_data[0];
|
||||
esp_rom_printf("duration=%u ticks\r\n", delta);
|
||||
// Capture timer is clocked from APB by default
|
||||
uint32_t clk_src_res = esp_clk_apb_freq();
|
||||
TEST_ASSERT_UINT_WITHIN(80000, clk_src_res / 100, delta);
|
||||
|
||||
printf("uninstall capture channel and timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_capture_timer_disable(cap_timer));
|
||||
TEST_ESP_OK(mcpwm_del_capture_channel(cap_channel));
|
||||
TEST_ESP_OK(mcpwm_del_capture_timer(cap_timer));
|
||||
}
|
||||
|
||||
TEST_MCPWM_CALLBACK_ATTR
|
||||
static bool test_capture_after_sync_callback(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_data_t *data, void *user_data)
|
||||
{
|
||||
uint32_t *cap_data = (uint32_t *)user_data;
|
||||
*cap_data = data->cap_value;
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_capture_timer_sync_phase_lock", "[mcpwm]")
|
||||
{
|
||||
mcpwm_capture_timer_config_t cap_timer_config = {
|
||||
.group_id = 0,
|
||||
.clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,
|
||||
};
|
||||
mcpwm_cap_timer_handle_t cap_timer = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_capture_timer(&cap_timer_config, &cap_timer));
|
||||
|
||||
mcpwm_sync_handle_t soft_sync = NULL;
|
||||
mcpwm_soft_sync_config_t soft_sync_config = {};
|
||||
TEST_ESP_OK(mcpwm_new_soft_sync_src(&soft_sync_config, &soft_sync));
|
||||
|
||||
mcpwm_capture_timer_sync_phase_config_t sync_config = {
|
||||
.count_value = 1000,
|
||||
.direction = MCPWM_TIMER_DIRECTION_UP,
|
||||
.sync_src = soft_sync,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_capture_timer_set_phase_on_sync(cap_timer, &sync_config));
|
||||
mcpwm_cap_channel_handle_t cap_channel = NULL;
|
||||
mcpwm_capture_channel_config_t cap_chan_config = {
|
||||
.gpio_num = -1, // don't need any GPIO, we use software to trigger a catch
|
||||
.prescale = 1,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_capture_channel(cap_timer, &cap_chan_config, &cap_channel));
|
||||
|
||||
mcpwm_capture_event_callbacks_t cbs = {
|
||||
.on_cap = test_capture_after_sync_callback,
|
||||
};
|
||||
uint32_t cap_data;
|
||||
TEST_ESP_OK(mcpwm_capture_channel_register_event_callbacks(cap_channel, &cbs, &cap_data));
|
||||
|
||||
TEST_ESP_OK(mcpwm_capture_channel_trigger_soft_catch(cap_channel));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
printf("capture data before sync: %u\r\n", cap_data);
|
||||
|
||||
TEST_ESP_OK(mcpwm_soft_sync_activate(soft_sync));
|
||||
TEST_ESP_OK(mcpwm_capture_channel_trigger_soft_catch(cap_channel));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
printf("capture data after sync: %u\r\n", cap_data);
|
||||
TEST_ASSERT_EQUAL(1000, cap_data);
|
||||
TEST_ESP_OK(mcpwm_del_capture_channel(cap_channel));
|
||||
TEST_ESP_OK(mcpwm_del_capture_timer(cap_timer));
|
||||
TEST_ESP_OK(mcpwm_del_sync_src(soft_sync));
|
||||
}
|
113
components/driver/test_apps/mcpwm/main/test_mcpwm_cmpr.c
Normal file
113
components/driver/test_apps/mcpwm/main/test_mcpwm_cmpr.c
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/mcpwm_timer.h"
|
||||
#include "driver/mcpwm_oper.h"
|
||||
#include "driver/mcpwm_cmpr.h"
|
||||
|
||||
TEST_CASE("mcpwm_comparator_install_uninstall", "[mcpwm]")
|
||||
{
|
||||
mcpwm_timer_handle_t timer;
|
||||
mcpwm_oper_handle_t operator;
|
||||
mcpwm_cmpr_handle_t comparators[SOC_MCPWM_COMPARATORS_PER_OPERATOR];
|
||||
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.group_id = 0,
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
.period_ticks = 10 * 1000,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
};
|
||||
mcpwm_operator_config_t operator_config = {
|
||||
.group_id = 0,
|
||||
};
|
||||
printf("install timer and operator");
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &operator));
|
||||
|
||||
printf("install comparator\r\n");
|
||||
mcpwm_comparator_config_t comparator_config = {};
|
||||
for (int i = 0; i < SOC_MCPWM_COMPARATORS_PER_OPERATOR; i++) {
|
||||
TEST_ESP_OK(mcpwm_new_comparator(operator, &comparator_config, &comparators[i]));
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, mcpwm_new_comparator(operator, &comparator_config, &comparators[0]));
|
||||
|
||||
printf("connect MCPWM timer and operators\r\n");
|
||||
TEST_ESP_OK(mcpwm_operator_connect_timer(operator, timer));
|
||||
|
||||
printf("uninstall timer, operator and comparators\r\n");
|
||||
// can't delete operator if the comparators are still in working
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, mcpwm_del_operator(operator));
|
||||
for (int i = 0; i < SOC_MCPWM_COMPARATORS_PER_OPERATOR; i++) {
|
||||
TEST_ESP_OK(mcpwm_del_comparator(comparators[i]));
|
||||
}
|
||||
TEST_ESP_OK(mcpwm_del_operator(operator));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
}
|
||||
|
||||
static bool test_compare_on_reach(mcpwm_cmpr_handle_t cmpr, const mcpwm_compare_event_data_t *ev_data, void *user_data)
|
||||
{
|
||||
uint32_t *counts = (uint32_t *)user_data;
|
||||
(*counts)++;
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_comparator_event_callback", "[mcpwm]")
|
||||
{
|
||||
mcpwm_timer_handle_t timer;
|
||||
mcpwm_oper_handle_t operator;
|
||||
mcpwm_cmpr_handle_t comparator;
|
||||
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.group_id = 0,
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
.period_ticks = 10 * 1000, // 10ms
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
};
|
||||
mcpwm_operator_config_t operator_config = {
|
||||
.group_id = 0,
|
||||
};
|
||||
mcpwm_comparator_config_t comparator_config = {};
|
||||
printf("install timer, operator and comparator");
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &operator));
|
||||
TEST_ESP_OK(mcpwm_new_comparator(operator, &comparator_config, &comparator));
|
||||
|
||||
// set compare value before connecting timer and operator will fail
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, mcpwm_comparator_set_compare_value(comparator, 5000));
|
||||
printf("connect MCPWM timer and operators\r\n");
|
||||
TEST_ESP_OK(mcpwm_operator_connect_timer(operator, timer));
|
||||
// compare ticks can't exceed the timer's period ticks
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, mcpwm_comparator_set_compare_value(comparator, 20 * 1000));
|
||||
TEST_ESP_OK(mcpwm_comparator_set_compare_value(comparator, 5 * 1000));
|
||||
|
||||
printf("register compare event callback\r\n");
|
||||
uint32_t compare_counts = 0;
|
||||
mcpwm_comparator_event_callbacks_t cbs = {
|
||||
.on_reach = test_compare_on_reach,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_comparator_register_event_callbacks(comparator, &cbs, &compare_counts));
|
||||
|
||||
printf("start timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timer));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
|
||||
printf("compare_counts=%u\r\n", compare_counts);
|
||||
// the timer period is 10ms, the expected compare_counts = 1s/10ms = 100
|
||||
TEST_ASSERT_INT_WITHIN(1, 100, compare_counts);
|
||||
|
||||
printf("uninstall timer, operator and comparator\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timer));
|
||||
TEST_ESP_OK(mcpwm_del_comparator(comparator));
|
||||
TEST_ESP_OK(mcpwm_del_operator(operator));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
}
|
93
components/driver/test_apps/mcpwm/main/test_mcpwm_fault.c
Normal file
93
components/driver/test_apps/mcpwm/main/test_mcpwm_fault.c
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "driver/mcpwm_fault.h"
|
||||
#include "driver/mcpwm_oper.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
TEST_CASE("mcpwm_fault_install_uninstall", "[mcpwm]")
|
||||
{
|
||||
printf("install and uninstall gpio faults\r\n");
|
||||
mcpwm_gpio_fault_config_t gpio_fault_config = {
|
||||
.gpio_num = 0,
|
||||
};
|
||||
int total_gpio_faults = SOC_MCPWM_GPIO_FAULTS_PER_GROUP * SOC_MCPWM_GROUPS;
|
||||
mcpwm_fault_handle_t gpio_faults[total_gpio_faults];
|
||||
int fault_itor = 0;
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
gpio_fault_config.group_id = i;
|
||||
for (int j = 0; j < SOC_MCPWM_GPIO_FAULTS_PER_GROUP; j++) {
|
||||
TEST_ESP_OK(mcpwm_new_gpio_fault(&gpio_fault_config, &gpio_faults[fault_itor++]));
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, mcpwm_new_gpio_fault(&gpio_fault_config, &gpio_faults[0]));
|
||||
}
|
||||
for (int i = 0; i < total_gpio_faults; i++) {
|
||||
TEST_ESP_OK(mcpwm_del_fault(gpio_faults[i]));
|
||||
}
|
||||
|
||||
printf("install and uninstall software fault\r\n");
|
||||
mcpwm_soft_fault_config_t soft_fault_config = {};
|
||||
mcpwm_fault_handle_t soft_fault = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_soft_fault(&soft_fault_config, &soft_fault));
|
||||
TEST_ESP_OK(mcpwm_del_fault(soft_fault));
|
||||
}
|
||||
|
||||
static bool test_fault_enter_callback(mcpwm_fault_handle_t detector, const mcpwm_fault_event_data_t *status, void *user_data)
|
||||
{
|
||||
TaskHandle_t task_handle = (TaskHandle_t)user_data;
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
esp_rom_printf("fault found\r\n");
|
||||
vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
static bool test_fault_exit_callback(mcpwm_fault_handle_t detector, const mcpwm_fault_event_data_t *status, void *user_data)
|
||||
{
|
||||
TaskHandle_t task_handle = (TaskHandle_t)user_data;
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
esp_rom_printf("fault relieved\r\n");
|
||||
vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_gpio_fault_event_callbacks", "[mcpwm]")
|
||||
{
|
||||
printf("create gpio fault\r\n");
|
||||
const int fault_gpio = 0;
|
||||
mcpwm_fault_handle_t fault = NULL;
|
||||
mcpwm_gpio_fault_config_t gpio_fault_config = {
|
||||
.group_id = 0,
|
||||
.gpio_num = fault_gpio,
|
||||
.flags.active_level = true, // active on high level
|
||||
.flags.pull_down = true,
|
||||
.flags.io_loop_back = true, // for debug, so that we can use gpio_set_level to mimic a fault source
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_gpio_fault(&gpio_fault_config, &fault));
|
||||
|
||||
// put fault GPIO into a safe state
|
||||
gpio_set_level(fault_gpio, 0);
|
||||
|
||||
printf("register callback for the gpio fault\r\n");
|
||||
mcpwm_fault_event_callbacks_t cbs = {
|
||||
.on_fault_enter = test_fault_enter_callback,
|
||||
.on_fault_exit = test_fault_exit_callback,
|
||||
};
|
||||
TaskHandle_t task_to_notify = xTaskGetCurrentTaskHandle();
|
||||
TEST_ESP_OK(mcpwm_fault_register_event_callbacks(fault, &cbs, task_to_notify));
|
||||
TEST_ASSERT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)));
|
||||
|
||||
printf("trigget a fault event\r\n");
|
||||
gpio_set_level(fault_gpio, 1);
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(10)));
|
||||
|
||||
printf("remove the fault source\r\n");
|
||||
gpio_set_level(fault_gpio, 0);
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(10)));
|
||||
|
||||
TEST_ESP_OK(mcpwm_del_fault(fault));
|
||||
}
|
646
components/driver/test_apps/mcpwm/main/test_mcpwm_gen.c
Normal file
646
components/driver/test_apps/mcpwm/main/test_mcpwm_gen.c
Normal file
@ -0,0 +1,646 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/mcpwm_timer.h"
|
||||
#include "driver/mcpwm_oper.h"
|
||||
#include "driver/mcpwm_cmpr.h"
|
||||
#include "driver/mcpwm_gen.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
TEST_CASE("mcpwm_generator_install_uninstall", "[mcpwm]")
|
||||
{
|
||||
mcpwm_operator_config_t oper_config = {
|
||||
.group_id = 0,
|
||||
};
|
||||
mcpwm_oper_handle_t oper = NULL;
|
||||
printf("create a MCPWM operator\r\n");
|
||||
TEST_ESP_OK(mcpwm_new_operator(&oper_config, &oper));
|
||||
|
||||
printf("create MCPWM generators from that operator\r\n");
|
||||
mcpwm_gen_handle_t gens[SOC_MCPWM_GENERATORS_PER_OPERATOR];
|
||||
mcpwm_generator_config_t gen_config = {
|
||||
.gen_gpio_num = 0,
|
||||
};
|
||||
for (int i = 0; i < SOC_MCPWM_GENERATORS_PER_OPERATOR; i++) {
|
||||
TEST_ESP_OK(mcpwm_new_generator(oper, &gen_config, &gens[i]));
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, mcpwm_new_generator(oper, &gen_config, &gens[0]));
|
||||
|
||||
printf("delete generators and operator\r\n");
|
||||
// can't delete operator if the generator is till in working
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, mcpwm_del_operator(oper));
|
||||
for (int i = 0; i < SOC_MCPWM_GENERATORS_PER_OPERATOR; i++) {
|
||||
TEST_ESP_OK(mcpwm_del_generator(gens[i]));
|
||||
}
|
||||
TEST_ESP_OK(mcpwm_del_operator(oper));
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_generator_force_level_hold_on", "[mcpwm]")
|
||||
{
|
||||
// The operator can even work without the timer
|
||||
printf("create operator and generator\r\n");
|
||||
mcpwm_oper_handle_t operator = NULL;
|
||||
mcpwm_operator_config_t operator_config = {
|
||||
.group_id = 0,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &operator));
|
||||
|
||||
mcpwm_gen_handle_t generator = NULL;
|
||||
const int gen_gpio = 0;
|
||||
mcpwm_generator_config_t generator_config = {
|
||||
.gen_gpio_num = gen_gpio,
|
||||
.flags.io_loop_back = true, // loop back for test
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &generator));
|
||||
|
||||
printf("add force level to the generator, hold on");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 0, true));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(gen_gpio));
|
||||
TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 1, true));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(gen_gpio));
|
||||
}
|
||||
|
||||
printf("remove the force level\r\n");
|
||||
TEST_ESP_OK(mcpwm_generator_set_force_level(generator, -1, true));
|
||||
|
||||
printf("delete generator and operator\r\n");
|
||||
TEST_ESP_OK(mcpwm_del_generator(generator));
|
||||
TEST_ESP_OK(mcpwm_del_operator(operator));
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_generator_force_level_recovery", "[mcpwm]")
|
||||
{
|
||||
printf("create mcpwm timer\r\n");
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.group_id = 0,
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
.resolution_hz = 1000000,
|
||||
.period_ticks = 50000,
|
||||
};
|
||||
mcpwm_timer_handle_t timer = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timer));
|
||||
|
||||
printf("create operator\r\n");
|
||||
mcpwm_oper_handle_t operator = NULL;
|
||||
mcpwm_operator_config_t operator_config = {
|
||||
.group_id = 0,
|
||||
.flags.update_gen_action_on_tez = true,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &operator));
|
||||
TEST_ESP_OK(mcpwm_operator_connect_timer(operator, timer));
|
||||
|
||||
printf("create generator\r\n");
|
||||
mcpwm_gen_handle_t generator = NULL;
|
||||
const int gen_gpio = 0;
|
||||
mcpwm_generator_config_t generator_config = {
|
||||
.gen_gpio_num = gen_gpio,
|
||||
.flags.io_loop_back = true, // loop back for test
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &generator));
|
||||
|
||||
printf("add force level to the generator, and recovery by events");
|
||||
TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 0, false));
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(gen_gpio));
|
||||
TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 1, false));
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(gen_gpio));
|
||||
|
||||
TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 0, false));
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(gen_gpio));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(generator,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
// generator should output high level on tez event, the previous force level should disappear
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(gen_gpio));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 1, false));
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(gen_gpio));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(generator,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
// generator should output low level on tez event, the previous force level should disappear
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(gen_gpio));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
printf("delete generator, operator and timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timer));
|
||||
TEST_ESP_OK(mcpwm_del_generator(generator));
|
||||
TEST_ESP_OK(mcpwm_del_operator(operator));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_generator_action_on_timer_event", "[mcpwm]")
|
||||
{
|
||||
const int generator_gpio = 0;
|
||||
printf("create timer and operator\r\n");
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.group_id = 0,
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 1000000,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
.period_ticks = 1000,
|
||||
};
|
||||
mcpwm_timer_handle_t timer = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timer));
|
||||
|
||||
mcpwm_operator_config_t oper_config = {
|
||||
.group_id = 0,
|
||||
};
|
||||
mcpwm_oper_handle_t oper = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_operator(&oper_config, &oper));
|
||||
|
||||
printf("connect timer and operator\r\n");
|
||||
TEST_ESP_OK(mcpwm_operator_connect_timer(oper, timer));
|
||||
|
||||
printf("create generator\r\n");
|
||||
mcpwm_generator_config_t gen_config = {
|
||||
.gen_gpio_num = generator_gpio,
|
||||
.flags.io_loop_back = 1, // so that we can read the GPIO value by GPIO driver
|
||||
};
|
||||
mcpwm_gen_handle_t gen = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_generator(oper, &gen_config, &gen));
|
||||
|
||||
printf("set generator to output high on timer full\r\n");
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gen,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_KEEP),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
printf("start timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
printf("stop timer on full\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_FULL));
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(generator_gpio));
|
||||
|
||||
printf("set generator to output low on timer full\r\n");
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gen,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_KEEP),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
printf("start timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
printf("stop timer on full\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_FULL));
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(generator_gpio));
|
||||
|
||||
printf("delete timer, operator, generator\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timer));
|
||||
TEST_ESP_OK(mcpwm_del_generator(gen));
|
||||
TEST_ESP_OK(mcpwm_del_operator(oper));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
}
|
||||
|
||||
typedef void (*set_gen_actions_cb_t)(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb);
|
||||
|
||||
static void mcpwm_gen_action_test_template(uint32_t timer_resolution, uint32_t period, mcpwm_timer_count_mode_t count_mode,
|
||||
uint32_t cmpa, uint32_t cmpb, int gpioa, int gpiob, set_gen_actions_cb_t set_generator_actions)
|
||||
{
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.group_id = 0,
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.count_mode = count_mode,
|
||||
.resolution_hz = timer_resolution,
|
||||
.period_ticks = period,
|
||||
};
|
||||
mcpwm_timer_handle_t timer = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
|
||||
mcpwm_operator_config_t operator_config = {
|
||||
.group_id = 0,
|
||||
};
|
||||
mcpwm_oper_handle_t operator = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &operator));
|
||||
TEST_ESP_OK(mcpwm_operator_connect_timer(operator, timer));
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timer));
|
||||
|
||||
mcpwm_cmpr_handle_t comparator_a = NULL;
|
||||
mcpwm_cmpr_handle_t comparator_b = NULL;
|
||||
mcpwm_comparator_config_t comparator_config = {
|
||||
.flags.update_cmp_on_tez = true,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_comparator(operator, &comparator_config, &comparator_a));
|
||||
TEST_ESP_OK(mcpwm_new_comparator(operator, &comparator_config, &comparator_b));
|
||||
TEST_ESP_OK(mcpwm_comparator_set_compare_value(comparator_a, cmpa));
|
||||
TEST_ESP_OK(mcpwm_comparator_set_compare_value(comparator_b, cmpb));
|
||||
|
||||
mcpwm_gen_handle_t generator_a = NULL;
|
||||
mcpwm_gen_handle_t generator_b = NULL;
|
||||
mcpwm_generator_config_t generator_config = {
|
||||
.gen_gpio_num = gpioa,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &generator_a));
|
||||
generator_config.gen_gpio_num = gpiob;
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &generator_b));
|
||||
|
||||
set_generator_actions(generator_a, generator_b, comparator_a, comparator_b);
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timer));
|
||||
TEST_ESP_OK(mcpwm_del_generator(generator_a));
|
||||
TEST_ESP_OK(mcpwm_del_generator(generator_b));
|
||||
TEST_ESP_OK(mcpwm_del_comparator(comparator_a));
|
||||
TEST_ESP_OK(mcpwm_del_comparator(comparator_b));
|
||||
TEST_ESP_OK(mcpwm_del_operator(operator));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
}
|
||||
|
||||
static void single_edge_active_high(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gena,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(genb,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(genb,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void single_edge_active_low(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gena,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(genb,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(genb,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void pulse_placement(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(genb,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_TOGGLE),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void dual_edge_active_low_asym(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, cmpb, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(genb,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void dual_edge_active_low_sym(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, cmpa, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(genb,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, cmpb, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void dual_edge_complementary(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, cmpa, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(genb,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_DOWN, cmpb, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_generator_action_on_compare_event", "[mcpwm]")
|
||||
{
|
||||
printf("[Asymmetric, SingleEdge, ActiveHigh]\r\n");
|
||||
// PWMA: high = [1->350], low = [351->499,0]
|
||||
// PWMB: high = [1->200], low = [201->499,0]
|
||||
mcpwm_gen_action_test_template(1000000, 500, MCPWM_TIMER_COUNT_MODE_UP, 350, 200, 0, 2, single_edge_active_high);
|
||||
|
||||
printf("[Asymmetric, SingleEdge, ActiveLow]\r\n");
|
||||
// PWMA: low = [0->300], high = [301->499]
|
||||
// PWMB: low = [0->150], high = [151->499]
|
||||
mcpwm_gen_action_test_template(1000000, 500, MCPWM_TIMER_COUNT_MODE_UP, 300, 150, 0, 2, single_edge_active_low);
|
||||
|
||||
printf("[Asymmetric, PulsePlacement]\r\n");
|
||||
// PWMA: low = [0->200], high = [201->400], low = [401->599]
|
||||
// PWMB: high = [0->599], low = [0->599]
|
||||
mcpwm_gen_action_test_template(1000000, 600, MCPWM_TIMER_COUNT_MODE_UP, 200, 400, 0, 2, pulse_placement);
|
||||
|
||||
printf("[Asymmetric, DualEdge, ActiveLow]\r\n");
|
||||
// PWMA: low = [0->250], high = [251->599, 600->450], low = [451->1]
|
||||
// PWMB: low = [0->599], low = [600->1]
|
||||
mcpwm_gen_action_test_template(1000000, 1200, MCPWM_TIMER_COUNT_MODE_UP_DOWN, 250, 450, 0, 2, dual_edge_active_low_asym);
|
||||
|
||||
printf("[Symmetric, DualEdge, ActiveLow]\r\n");
|
||||
// PWMA: low = [0->400], high = [401->599, 600->400], low = [399->1]
|
||||
// PWMB: low = [0->500], high = [501->599, 600->500], low = [499->1]
|
||||
mcpwm_gen_action_test_template(1000000, 1200, MCPWM_TIMER_COUNT_MODE_UP_DOWN, 400, 500, 0, 2, dual_edge_active_low_sym);
|
||||
|
||||
printf("[Symmetric, DualEdge, Complementary]\r\n");
|
||||
// PWMA: low = [0->350], high = [351->599, 600->350], low = [349->1]
|
||||
// PWMB: low = [0->400], high = [401->599, 600->400], low = [399->1]
|
||||
mcpwm_gen_action_test_template(1000000, 1200, MCPWM_TIMER_COUNT_MODE_UP_DOWN, 350, 400, 0, 2, dual_edge_complementary);
|
||||
}
|
||||
|
||||
typedef void (*set_dead_time_cb_t)(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb);
|
||||
|
||||
static void mcpwm_deadtime_test_template(uint32_t timer_resolution, uint32_t period, uint32_t cmpa, uint32_t cmpb, int gpioa, int gpiob,
|
||||
set_gen_actions_cb_t set_generator_actions, set_dead_time_cb_t set_dead_time)
|
||||
{
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.group_id = 0,
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = timer_resolution,
|
||||
.period_ticks = period,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
};
|
||||
mcpwm_timer_handle_t timer = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
|
||||
mcpwm_operator_config_t operator_config = {
|
||||
.group_id = 0,
|
||||
};
|
||||
mcpwm_oper_handle_t operator = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &operator));
|
||||
TEST_ESP_OK(mcpwm_operator_connect_timer(operator, timer));
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timer));
|
||||
|
||||
mcpwm_cmpr_handle_t comparator_a = NULL;
|
||||
mcpwm_cmpr_handle_t comparator_b = NULL;
|
||||
mcpwm_comparator_config_t comparator_config = {
|
||||
.flags.update_cmp_on_tez = true,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_comparator(operator, &comparator_config, &comparator_a));
|
||||
TEST_ESP_OK(mcpwm_new_comparator(operator, &comparator_config, &comparator_b));
|
||||
TEST_ESP_OK(mcpwm_comparator_set_compare_value(comparator_a, cmpa));
|
||||
TEST_ESP_OK(mcpwm_comparator_set_compare_value(comparator_b, cmpb));
|
||||
|
||||
mcpwm_gen_handle_t generator_a = NULL;
|
||||
mcpwm_gen_handle_t generator_b = NULL;
|
||||
mcpwm_generator_config_t generator_config = {
|
||||
.gen_gpio_num = gpioa,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &generator_a));
|
||||
generator_config.gen_gpio_num = gpiob;
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &generator_b));
|
||||
|
||||
set_generator_actions(generator_a, generator_b, comparator_a, comparator_b);
|
||||
set_dead_time(generator_a, generator_b);
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timer));
|
||||
TEST_ESP_OK(mcpwm_del_generator(generator_a));
|
||||
TEST_ESP_OK(mcpwm_del_generator(generator_b));
|
||||
TEST_ESP_OK(mcpwm_del_comparator(comparator_a));
|
||||
TEST_ESP_OK(mcpwm_del_comparator(comparator_b));
|
||||
TEST_ESP_OK(mcpwm_del_operator(operator));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
}
|
||||
|
||||
static void ahc_set_generator_actions(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gena,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void ahc_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
|
||||
{
|
||||
mcpwm_dead_time_config_t dead_time_config = {
|
||||
.posedge_delay_ticks = 50,
|
||||
.negedge_delay_ticks = 0
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
|
||||
dead_time_config.posedge_delay_ticks = 0;
|
||||
dead_time_config.negedge_delay_ticks = 100;
|
||||
dead_time_config.flags.invert_output = true;
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, genb, &dead_time_config));
|
||||
}
|
||||
|
||||
static void alc_set_generator_actions(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gena,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void alc_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
|
||||
{
|
||||
mcpwm_dead_time_config_t dead_time_config = {
|
||||
.posedge_delay_ticks = 50,
|
||||
.negedge_delay_ticks = 0,
|
||||
.flags.invert_output = true
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
|
||||
dead_time_config.posedge_delay_ticks = 0;
|
||||
dead_time_config.negedge_delay_ticks = 100;
|
||||
dead_time_config.flags.invert_output = false;
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, genb, &dead_time_config));
|
||||
}
|
||||
|
||||
static void ah_set_generator_actions(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gena,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void ah_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
|
||||
{
|
||||
mcpwm_dead_time_config_t dead_time_config = {
|
||||
.posedge_delay_ticks = 50,
|
||||
.negedge_delay_ticks = 0,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
|
||||
dead_time_config.posedge_delay_ticks = 0;
|
||||
dead_time_config.negedge_delay_ticks = 100;
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, genb, &dead_time_config));
|
||||
}
|
||||
|
||||
static void al_set_generator_actions(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gena,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void al_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
|
||||
{
|
||||
mcpwm_dead_time_config_t dead_time_config = {
|
||||
.posedge_delay_ticks = 50,
|
||||
.negedge_delay_ticks = 0,
|
||||
.flags.invert_output = true
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
|
||||
dead_time_config.posedge_delay_ticks = 0;
|
||||
dead_time_config.negedge_delay_ticks = 100;
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, genb, &dead_time_config));
|
||||
}
|
||||
|
||||
static void reda_only_set_generator_actions(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gena,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(genb,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(genb,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void reda_only_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
|
||||
{
|
||||
mcpwm_dead_time_config_t dead_time_config = {
|
||||
.posedge_delay_ticks = 50,
|
||||
.negedge_delay_ticks = 0,
|
||||
};
|
||||
// apply deadtime to generator_a
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
|
||||
// bypass deadtime module for generator_b
|
||||
dead_time_config.posedge_delay_ticks = 0;
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
|
||||
}
|
||||
|
||||
static void fedb_only_set_generator_actions(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gena,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(genb,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(genb,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void fedb_only_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
|
||||
{
|
||||
mcpwm_dead_time_config_t dead_time_config = {
|
||||
.posedge_delay_ticks = 0,
|
||||
.negedge_delay_ticks = 0,
|
||||
};
|
||||
// generator_a bypass the deadtime module (no delay)
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
|
||||
// apply dead time to generator_b
|
||||
dead_time_config.negedge_delay_ticks = 50;
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
|
||||
|
||||
}
|
||||
|
||||
static void redfedb_only_set_generator_actions(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
|
||||
{
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gena,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(gena,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(genb,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_compare_event(genb,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION_END()));
|
||||
}
|
||||
|
||||
static void redfedb_only_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
|
||||
{
|
||||
mcpwm_dead_time_config_t dead_time_config = {
|
||||
.posedge_delay_ticks = 0,
|
||||
.negedge_delay_ticks = 0,
|
||||
};
|
||||
// generator_a bypass the deadtime module (no delay)
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
|
||||
// apply dead time on both edge for generator_b
|
||||
dead_time_config.negedge_delay_ticks = 50;
|
||||
dead_time_config.posedge_delay_ticks = 50;
|
||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_generator_deadtime_classical_configuration", "[mcpwm]")
|
||||
{
|
||||
printf("Active High Complementary\r\n");
|
||||
mcpwm_deadtime_test_template(1000000, 600, 200, 400, 0, 2, ahc_set_generator_actions, ahc_set_dead_time);
|
||||
|
||||
printf("Active Low Complementary\r\n");
|
||||
mcpwm_deadtime_test_template(1000000, 600, 200, 400, 0, 2, alc_set_generator_actions, alc_set_dead_time);
|
||||
|
||||
printf("Active High\r\n");
|
||||
mcpwm_deadtime_test_template(1000000, 600, 200, 400, 0, 2, ah_set_generator_actions, ah_set_dead_time);
|
||||
|
||||
printf("Active Low\r\n");
|
||||
mcpwm_deadtime_test_template(1000000, 600, 200, 400, 0, 2, al_set_generator_actions, al_set_dead_time);
|
||||
|
||||
printf("RED on A, Bypass B\r\n");
|
||||
mcpwm_deadtime_test_template(1000000, 500, 350, 350, 0, 2, reda_only_set_generator_actions, reda_only_set_dead_time);
|
||||
|
||||
printf("Bypass A, FED on B\r\n");
|
||||
mcpwm_deadtime_test_template(1000000, 500, 350, 350, 0, 2, fedb_only_set_generator_actions, fedb_only_set_dead_time);
|
||||
|
||||
printf("Bypass A, RED + FED on B\r\n");
|
||||
mcpwm_deadtime_test_template(1000000, 500, 350, 350, 0, 2, redfedb_only_set_generator_actions, redfedb_only_set_dead_time);
|
||||
}
|
380
components/driver/test_apps/mcpwm/main/test_mcpwm_oper.c
Normal file
380
components/driver/test_apps/mcpwm/main/test_mcpwm_oper.c
Normal file
@ -0,0 +1,380 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/mcpwm_oper.h"
|
||||
#include "driver/mcpwm_timer.h"
|
||||
#include "driver/mcpwm_gen.h"
|
||||
#include "driver/mcpwm_fault.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
TEST_CASE("mcpwm_operator_install_uninstall", "[mcpwm]")
|
||||
{
|
||||
const int total_operators = SOC_MCPWM_OPERATORS_PER_GROUP * SOC_MCPWM_GROUPS;
|
||||
mcpwm_timer_handle_t timers[SOC_MCPWM_GROUPS];
|
||||
mcpwm_oper_handle_t operators[total_operators];
|
||||
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 1 * 1000 * 1000,
|
||||
.period_ticks = 10 * 1000,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
};
|
||||
mcpwm_operator_config_t operator_config = {
|
||||
};
|
||||
printf("install one MCPWM timer for each group\r\n");
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
timer_config.group_id = i;
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timers[i]));
|
||||
}
|
||||
printf("install MCPWM operators for each group\r\n");
|
||||
int k = 0;
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
operator_config.group_id = i;
|
||||
for (int j = 0; j < SOC_MCPWM_OPERATORS_PER_GROUP; j++) {
|
||||
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &operators[k++]));
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, mcpwm_new_operator(&operator_config, &operators[0]));
|
||||
}
|
||||
printf("connect MCPWM timer and operators\r\n");
|
||||
k = 0;
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
for (int j = 0; j < SOC_MCPWM_OPERATORS_PER_GROUP; j++) {
|
||||
TEST_ESP_OK(mcpwm_operator_connect_timer(operators[k++], timers[i]));
|
||||
}
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, mcpwm_operator_connect_timer(operators[0], timers[1]));
|
||||
printf("uninstall operators and timers\r\n");
|
||||
for (int i = 0; i < total_operators; i++) {
|
||||
TEST_ESP_OK(mcpwm_del_operator(operators[i]));
|
||||
}
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
TEST_ESP_OK(mcpwm_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_operator_carrier", "[mcpwm]")
|
||||
{
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.group_id = 0,
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 1000000, // 1MHz, 1us per tick
|
||||
.period_ticks = 20000,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
};
|
||||
mcpwm_timer_handle_t timer = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
|
||||
mcpwm_operator_config_t operator_config = {
|
||||
.group_id = 0,
|
||||
};
|
||||
mcpwm_oper_handle_t operator = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &operator));
|
||||
TEST_ESP_OK(mcpwm_operator_connect_timer(operator, timer));
|
||||
|
||||
mcpwm_generator_config_t generator_config = {
|
||||
.gen_gpio_num = 0,
|
||||
};
|
||||
mcpwm_gen_handle_t generator = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &generator));
|
||||
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(generator,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_TOGGLE),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
|
||||
printf("add carrier to PWM wave\r\n");
|
||||
mcpwm_carrier_config_t carrier_config = {
|
||||
.frequency_hz = 1000000, // 1MHz carrier
|
||||
.duty_cycle = 0.5,
|
||||
.first_pulse_duration_us = 10,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_operator_apply_carrier(operator, &carrier_config));
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timer));
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
printf("remove carrier from PWM wave\r\n");
|
||||
carrier_config.frequency_hz = 0;
|
||||
TEST_ESP_OK(mcpwm_operator_apply_carrier(operator, &carrier_config));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timer));
|
||||
TEST_ESP_OK(mcpwm_del_generator(generator));
|
||||
TEST_ESP_OK(mcpwm_del_operator(operator));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
}
|
||||
|
||||
static bool test_cbc_brake_on_gpio_fault_callback(mcpwm_oper_handle_t operator, const mcpwm_brake_event_data_t *edata, void *user_data)
|
||||
{
|
||||
esp_rom_printf("cbc brake\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool test_ost_brake_on_gpio_fault_callback(mcpwm_oper_handle_t operator, const mcpwm_brake_event_data_t *edata, void *user_data)
|
||||
{
|
||||
esp_rom_printf("ost brake\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_operator_brake_on_gpio_fault", "[mcpwm]")
|
||||
{
|
||||
printf("install timer\r\n");
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.group_id = 0,
|
||||
.resolution_hz = 1000000, // 1MHz, 1us per tick
|
||||
.period_ticks = 20000,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
};
|
||||
mcpwm_timer_handle_t timer = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
|
||||
printf("install operator\r\n");
|
||||
mcpwm_operator_config_t operator_config = {
|
||||
.group_id = 0,
|
||||
};
|
||||
mcpwm_oper_handle_t operator = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &operator));
|
||||
TEST_ESP_OK(mcpwm_operator_connect_timer(operator, timer));
|
||||
|
||||
printf("set brake event callbacks for operator\r\n");
|
||||
mcpwm_operator_event_callbacks_t cbs = {
|
||||
.on_brake_cbc = test_cbc_brake_on_gpio_fault_callback,
|
||||
.on_brake_ost = test_ost_brake_on_gpio_fault_callback,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_operator_register_event_callbacks(operator, &cbs, NULL));
|
||||
|
||||
printf("install gpio fault\r\n");
|
||||
mcpwm_gpio_fault_config_t gpio_fault_config = {
|
||||
.group_id = 0,
|
||||
.flags.active_level = 1,
|
||||
.flags.io_loop_back = true,
|
||||
.flags.pull_down = true,
|
||||
};
|
||||
mcpwm_fault_handle_t gpio_cbc_fault = NULL;
|
||||
mcpwm_fault_handle_t gpio_ost_fault = NULL;
|
||||
const int cbc_fault_gpio = 4;
|
||||
const int ost_fault_gpio = 5;
|
||||
|
||||
gpio_fault_config.gpio_num = cbc_fault_gpio;
|
||||
TEST_ESP_OK(mcpwm_new_gpio_fault(&gpio_fault_config, &gpio_cbc_fault));
|
||||
gpio_fault_config.gpio_num = ost_fault_gpio;
|
||||
TEST_ESP_OK(mcpwm_new_gpio_fault(&gpio_fault_config, &gpio_ost_fault));
|
||||
|
||||
// put fault GPIO into a safe state
|
||||
gpio_set_level(cbc_fault_gpio, 0);
|
||||
gpio_set_level(ost_fault_gpio, 0);
|
||||
|
||||
printf("set brake mode on fault\r\n");
|
||||
mcpwm_brake_config_t brake_config = {
|
||||
.fault = gpio_cbc_fault,
|
||||
.brake_mode = MCPWM_OPER_BRAKE_MODE_CBC,
|
||||
.flags.cbc_recover_on_tez = true,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_operator_set_brake_on_fault(operator, &brake_config));
|
||||
brake_config.fault = gpio_ost_fault;
|
||||
brake_config.brake_mode = MCPWM_OPER_BRAKE_MODE_OST;
|
||||
TEST_ESP_OK(mcpwm_operator_set_brake_on_fault(operator, &brake_config));
|
||||
|
||||
printf("create generators\r\n");
|
||||
const int gen_a_gpio = 0;
|
||||
const int gen_b_gpio = 2;
|
||||
mcpwm_gen_handle_t gen_a = NULL;
|
||||
mcpwm_gen_handle_t gen_b = NULL;
|
||||
mcpwm_generator_config_t generator_config = {
|
||||
.flags.io_loop_back = true,
|
||||
};
|
||||
generator_config.gen_gpio_num = gen_a_gpio;
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &gen_a));
|
||||
generator_config.gen_gpio_num = gen_b_gpio;
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &gen_b));
|
||||
|
||||
printf("set generator actions on timer event\r\n");
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gen_a,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gen_b,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
|
||||
printf("set generator actions on brake event\r\n");
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_brake_event(gen_a,
|
||||
MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_CBC, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_BRAKE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_brake_event(gen_b,
|
||||
MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_OST, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_BRAKE_EVENT_ACTION_END()));
|
||||
|
||||
printf("enable and start timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timer));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
|
||||
printf("trigger GPIO fault signal, brake in CBC mode\r\n");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
gpio_set_level(cbc_fault_gpio, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(gen_a_gpio));
|
||||
// remove the fault signal
|
||||
gpio_set_level(cbc_fault_gpio, 0);
|
||||
// recovery
|
||||
TEST_ESP_OK(mcpwm_operator_recover_from_fault(operator, gpio_cbc_fault));
|
||||
vTaskDelay(pdMS_TO_TICKS(40));
|
||||
// should recovery automatically
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(gen_a_gpio));
|
||||
}
|
||||
|
||||
printf("trigger GPIO fault signal, brake in OST mode\r\n");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
gpio_set_level(ost_fault_gpio, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(gen_b_gpio));
|
||||
// can't recover because fault signal is still active
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, mcpwm_operator_recover_from_fault(operator, gpio_ost_fault));
|
||||
// remove the fault signal
|
||||
gpio_set_level(ost_fault_gpio, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(40));
|
||||
// for ost brake, the generator can't recover before we manually recover it
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(gen_b_gpio));
|
||||
// now it's safe to recover the operator
|
||||
TEST_ESP_OK(mcpwm_operator_recover_from_fault(operator, gpio_ost_fault));
|
||||
vTaskDelay(pdMS_TO_TICKS(40));
|
||||
// should recovery now
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(gen_b_gpio));
|
||||
}
|
||||
|
||||
printf("delete all mcpwm objects\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timer));
|
||||
TEST_ESP_OK(mcpwm_del_fault(gpio_cbc_fault));
|
||||
TEST_ESP_OK(mcpwm_del_fault(gpio_ost_fault));
|
||||
TEST_ESP_OK(mcpwm_del_generator(gen_a));
|
||||
TEST_ESP_OK(mcpwm_del_generator(gen_b));
|
||||
TEST_ESP_OK(mcpwm_del_operator(operator));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_operator_brake_on_soft_fault", "[mcpwm]")
|
||||
{
|
||||
printf("install timer\r\n");
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.group_id = 0,
|
||||
.resolution_hz = 1000000, // 1MHz, 1us per tick
|
||||
.period_ticks = 20000,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
};
|
||||
mcpwm_timer_handle_t timer = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
|
||||
printf("install operator\r\n");
|
||||
mcpwm_operator_config_t operator_config = {
|
||||
.group_id = 0,
|
||||
};
|
||||
mcpwm_oper_handle_t operator = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &operator));
|
||||
TEST_ESP_OK(mcpwm_operator_connect_timer(operator, timer));
|
||||
|
||||
printf("install soft fault\r\n");
|
||||
mcpwm_soft_fault_config_t soft_fault_config = {};
|
||||
mcpwm_fault_handle_t soft_fault = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_soft_fault(&soft_fault_config, &soft_fault));
|
||||
|
||||
printf("set brake mode on fault\r\n");
|
||||
mcpwm_brake_config_t brake_config = {
|
||||
.fault = soft_fault,
|
||||
.brake_mode = MCPWM_OPER_BRAKE_MODE_CBC,
|
||||
.flags.cbc_recover_on_tez = true,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_operator_set_brake_on_fault(operator, &brake_config));
|
||||
|
||||
printf("create generators\r\n");
|
||||
const int gen_a_gpio = 0;
|
||||
const int gen_b_gpio = 2;
|
||||
mcpwm_gen_handle_t gen_a = NULL;
|
||||
mcpwm_gen_handle_t gen_b = NULL;
|
||||
mcpwm_generator_config_t generator_config = {
|
||||
.flags.io_loop_back = true,
|
||||
};
|
||||
generator_config.gen_gpio_num = gen_a_gpio;
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &gen_a));
|
||||
generator_config.gen_gpio_num = gen_b_gpio;
|
||||
TEST_ESP_OK(mcpwm_new_generator(operator, &generator_config, &gen_b));
|
||||
|
||||
printf("set generator actions on timer event\r\n");
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gen_a,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_timer_event(gen_b,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW),
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION_END()));
|
||||
|
||||
printf("set generator actions on brake event\r\n");
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_brake_event(gen_a,
|
||||
MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_CBC, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_BRAKE_EVENT_ACTION_END()));
|
||||
TEST_ESP_OK(mcpwm_generator_set_actions_on_brake_event(gen_b,
|
||||
MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_OST, MCPWM_GEN_ACTION_HIGH),
|
||||
MCPWM_GEN_BRAKE_EVENT_ACTION_END()));
|
||||
|
||||
printf("enable and start timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timer));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
|
||||
printf("trigger soft fault signal, brake in CBC mode\r\n");
|
||||
for (int i = 0; i < 1; i++) {
|
||||
// stop the timer, so the operator can't recover from fault automatically
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
|
||||
vTaskDelay(pdMS_TO_TICKS(40));
|
||||
// check initial generator output
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(gen_a_gpio));
|
||||
TEST_ESP_OK(mcpwm_soft_fault_activate(soft_fault));
|
||||
// check generate output on fault event
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(gen_a_gpio));
|
||||
// start the timer, so that operator can recover at a specific event (e.g. tez)
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
// recover on tez
|
||||
TEST_ESP_OK(mcpwm_operator_recover_from_fault(operator, soft_fault));
|
||||
vTaskDelay(pdMS_TO_TICKS(40));
|
||||
// the generator output should be recoverd automatically
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(gen_a_gpio));
|
||||
}
|
||||
|
||||
printf("change the brake mode to ost\r\n");
|
||||
brake_config.brake_mode = MCPWM_OPER_BRAKE_MODE_OST;
|
||||
TEST_ESP_OK(mcpwm_operator_set_brake_on_fault(operator, &brake_config));
|
||||
|
||||
printf("trigger soft fault signal, brake in OST mode\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// check initial generator output
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(gen_b_gpio));
|
||||
TEST_ESP_OK(mcpwm_soft_fault_activate(soft_fault));
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(gen_b_gpio));
|
||||
vTaskDelay(pdMS_TO_TICKS(40));
|
||||
// don't recover without a manual recover
|
||||
TEST_ASSERT_EQUAL(1, gpio_get_level(gen_b_gpio));
|
||||
TEST_ESP_OK(mcpwm_operator_recover_from_fault(operator, soft_fault));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// should recovery now
|
||||
TEST_ASSERT_EQUAL(0, gpio_get_level(gen_b_gpio));
|
||||
}
|
||||
|
||||
printf("delete all mcpwm objects\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timer));
|
||||
TEST_ESP_OK(mcpwm_del_fault(soft_fault));
|
||||
TEST_ESP_OK(mcpwm_del_generator(gen_a));
|
||||
TEST_ESP_OK(mcpwm_del_generator(gen_b));
|
||||
TEST_ESP_OK(mcpwm_del_operator(operator));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
}
|
204
components/driver/test_apps/mcpwm/main/test_mcpwm_sync.c
Normal file
204
components/driver/test_apps/mcpwm/main/test_mcpwm_sync.c
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/mcpwm_timer.h"
|
||||
#include "driver/mcpwm_sync.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_private/mcpwm.h"
|
||||
#include "test_mcpwm_utils.h"
|
||||
|
||||
TEST_CASE("mcpwm_sync_source_install_uninstall", "[mcpwm]")
|
||||
{
|
||||
printf("install timer sync_src\r\n");
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 1000000, // 1MHz
|
||||
.period_ticks = 200,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
};
|
||||
const int total_timers = SOC_MCPWM_TIMERS_PER_GROUP * SOC_MCPWM_GROUPS;
|
||||
mcpwm_timer_handle_t timers[total_timers];
|
||||
int k = 0;
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
timer_config.group_id = i;
|
||||
for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) {
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timers[k++]));
|
||||
}
|
||||
}
|
||||
mcpwm_timer_sync_src_config_t timer_sync_src_config = {
|
||||
.timer_event = MCPWM_TIMER_EVENT_EMPTY,
|
||||
};
|
||||
mcpwm_sync_handle_t timer_syncs[total_timers];
|
||||
for (int i = 0; i < total_timers; i++) {
|
||||
TEST_ESP_OK(mcpwm_new_timer_sync_src(timers[i], &timer_sync_src_config, &timer_syncs[i]));
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, mcpwm_new_timer_sync_src(timers[0], &timer_sync_src_config, &timer_syncs[0]));
|
||||
|
||||
printf("install gpio sync_src\r\n");
|
||||
mcpwm_gpio_sync_src_config_t gpio_sync_config = {
|
||||
.gpio_num = 0,
|
||||
};
|
||||
const int total_gpio_sync_srcs = SOC_MCPWM_GROUPS * SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP;
|
||||
mcpwm_sync_handle_t gpio_sync_srcs[total_gpio_sync_srcs];
|
||||
k = 0;
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
gpio_sync_config.group_id = i;
|
||||
for (int j = 0; j < SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP; j++) {
|
||||
TEST_ESP_OK(mcpwm_new_gpio_sync_src(&gpio_sync_config, &gpio_sync_srcs[k++]));
|
||||
}
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, mcpwm_new_gpio_sync_src(&gpio_sync_config, &gpio_sync_srcs[0]));
|
||||
|
||||
printf("delete synchors\r\n");
|
||||
for (int i = 0; i < total_gpio_sync_srcs; i++) {
|
||||
TEST_ESP_OK(mcpwm_del_sync_src(gpio_sync_srcs[i]));
|
||||
}
|
||||
for (int i = 0; i < total_timers; i++) {
|
||||
TEST_ESP_OK(mcpwm_del_sync_src(timer_syncs[i]));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_soft_sync_timer_phase_lock", "[mcpwm]")
|
||||
{
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.group_id = 0,
|
||||
.resolution_hz = 1000000, // 1MHz
|
||||
.period_ticks = 200,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP_DOWN,
|
||||
};
|
||||
mcpwm_timer_handle_t timer = NULL;
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timer));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_STOP_FULL));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
check_mcpwm_timer_phase(&timer, 1, timer_config.period_ticks / 2, MCPWM_TIMER_DIRECTION_DOWN);
|
||||
|
||||
printf("install soft sync source\r\n");
|
||||
mcpwm_sync_handle_t soft_sync = NULL;
|
||||
mcpwm_soft_sync_config_t soft_sync_config = {};
|
||||
TEST_ESP_OK(mcpwm_new_soft_sync_src(&soft_sync_config, &soft_sync));
|
||||
|
||||
mcpwm_timer_sync_phase_config_t sync_phase_config = {
|
||||
.count_value = 77,
|
||||
.direction = MCPWM_TIMER_DIRECTION_UP,
|
||||
.sync_src = soft_sync,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_timer_set_phase_on_sync(timer, &sync_phase_config));
|
||||
TEST_ESP_OK(mcpwm_soft_sync_activate(soft_sync));
|
||||
check_mcpwm_timer_phase(&timer, 1, 77, MCPWM_TIMER_DIRECTION_UP);
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timer));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
TEST_ESP_OK(mcpwm_del_sync_src(soft_sync));
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_gpio_sync_timer_phase_lock", "[mcpwm]")
|
||||
{
|
||||
// GPIO
|
||||
// |
|
||||
// v
|
||||
// timer0-->timer1-->timer2
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.group_id = 0,
|
||||
.resolution_hz = 1000000, // 1MHz, 1us per tick
|
||||
.period_ticks = 500,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
};
|
||||
mcpwm_timer_sync_src_config_t sync_config = {
|
||||
.flags.propagate_input_sync = 1, // reuse the input sync source as the output sync trigger
|
||||
};
|
||||
mcpwm_timer_handle_t timers[SOC_MCPWM_TIMERS_PER_GROUP];
|
||||
mcpwm_sync_handle_t sync_srcs[SOC_MCPWM_TIMERS_PER_GROUP];
|
||||
for (int i = 0; i < SOC_MCPWM_TIMERS_PER_GROUP; i++) {
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timers[i]));
|
||||
TEST_ESP_OK(mcpwm_new_timer_sync_src(timers[i], &sync_config, &sync_srcs[i]));
|
||||
}
|
||||
mcpwm_timer_sync_phase_config_t sync_phase_config = {
|
||||
.count_value = 100,
|
||||
.direction = MCPWM_TIMER_DIRECTION_UP,
|
||||
};
|
||||
mcpwm_sync_handle_t gpio_sync_src;
|
||||
const int gpio_num = 0;
|
||||
mcpwm_gpio_sync_src_config_t gpio_sync_config = {
|
||||
.group_id = 0,
|
||||
.gpio_num = gpio_num,
|
||||
.flags.io_loop_back = true, // so that we can use gpio driver to simulate the sync signal
|
||||
.flags.pull_down = true, // internally pull down
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_new_gpio_sync_src(&gpio_sync_config, &gpio_sync_src));
|
||||
// put the GPIO into initial state
|
||||
gpio_set_level(gpio_num, 0);
|
||||
for (int i = 1; i < SOC_MCPWM_TIMERS_PER_GROUP; i++) {
|
||||
sync_phase_config.sync_src = sync_srcs[i - 1];
|
||||
TEST_ESP_OK(mcpwm_timer_set_phase_on_sync(timers[i], &sync_phase_config));
|
||||
}
|
||||
sync_phase_config.sync_src = gpio_sync_src;
|
||||
TEST_ESP_OK(mcpwm_timer_set_phase_on_sync(timers[0], &sync_phase_config));
|
||||
|
||||
// simulate an GPIO sync singal
|
||||
gpio_set_level(gpio_num, 1);
|
||||
gpio_set_level(gpio_num, 0);
|
||||
check_mcpwm_timer_phase(timers, SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP, 100, MCPWM_TIMER_DIRECTION_UP);
|
||||
|
||||
TEST_ESP_OK(mcpwm_del_sync_src(gpio_sync_src));
|
||||
for (int i = 0; i < SOC_MCPWM_TIMERS_PER_GROUP; i++) {
|
||||
TEST_ESP_OK(mcpwm_del_sync_src(sync_srcs[i]));
|
||||
TEST_ESP_OK(mcpwm_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_timer_sync_timer_phase_lock", "[mcpwm]")
|
||||
{
|
||||
// +->timer1
|
||||
// |
|
||||
// timer0---+
|
||||
// |
|
||||
// +->timer2
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.group_id = 0,
|
||||
.resolution_hz = 1000000, // 1MHz, 1us per tick
|
||||
.period_ticks = 500,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP_DOWN,
|
||||
};
|
||||
mcpwm_timer_handle_t timers[SOC_MCPWM_TIMERS_PER_GROUP];
|
||||
for (int i = 0; i < SOC_MCPWM_TIMERS_PER_GROUP; i++) {
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timers[i]));
|
||||
}
|
||||
|
||||
mcpwm_timer_sync_src_config_t sync_config = {
|
||||
.timer_event = MCPWM_TIMER_EVENT_FULL,
|
||||
};
|
||||
mcpwm_sync_handle_t sync_src;
|
||||
TEST_ESP_OK(mcpwm_new_timer_sync_src(timers[0], &sync_config, &sync_src));
|
||||
|
||||
mcpwm_timer_sync_phase_config_t sync_phase_config = {
|
||||
.count_value = 50,
|
||||
.direction = MCPWM_TIMER_DIRECTION_DOWN,
|
||||
.sync_src = sync_src,
|
||||
};
|
||||
for (int i = 1; i < SOC_MCPWM_TIMERS_PER_GROUP; i++) {
|
||||
TEST_ESP_OK(mcpwm_timer_set_phase_on_sync(timers[i], &sync_phase_config));
|
||||
}
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timers[0]));
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timers[0], MCPWM_TIMER_START_STOP_FULL));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
check_mcpwm_timer_phase(&timers[1], 2, 50, MCPWM_TIMER_DIRECTION_DOWN);
|
||||
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timers[0]));
|
||||
TEST_ESP_OK(mcpwm_del_sync_src(sync_src));
|
||||
for (int i = 0; i < SOC_MCPWM_TIMERS_PER_GROUP; i++) {
|
||||
TEST_ESP_OK(mcpwm_del_timer(timers[i]));
|
||||
}
|
||||
}
|
186
components/driver/test_apps/mcpwm/main/test_mcpwm_timer.c
Normal file
186
components/driver/test_apps/mcpwm/main/test_mcpwm_timer.c
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "unity.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/mcpwm_timer.h"
|
||||
#include "esp_private/mcpwm.h"
|
||||
#include "test_mcpwm_utils.h"
|
||||
|
||||
TEST_CASE("mcpwm_timer_start_stop", "[mcpwm]")
|
||||
{
|
||||
mcpwm_timer_config_t config = {
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 1000000, // 1MHz
|
||||
.period_ticks = 400,
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP_DOWN,
|
||||
};
|
||||
const int num_timers = SOC_MCPWM_TIMERS_PER_GROUP * SOC_MCPWM_GROUPS;
|
||||
|
||||
printf("create mcpwm timer instances\r\n");
|
||||
mcpwm_timer_handle_t timers[num_timers];
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) {
|
||||
config.group_id = i;
|
||||
TEST_ESP_OK(mcpwm_new_timer(&config, &timers[i * SOC_MCPWM_TIMERS_PER_GROUP + j]));
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, mcpwm_new_timer(&config, &timers[0]));
|
||||
}
|
||||
|
||||
// can't do start/stop control before enable
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, mcpwm_timer_start_stop(timers[0], MCPWM_TIMER_START_NO_STOP));
|
||||
|
||||
printf("enable timers\r\n");
|
||||
for (int i = 0; i < num_timers; i++) {
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timers[i]));
|
||||
}
|
||||
|
||||
printf("start timer and then stop when empty\r\n");
|
||||
for (int i = 0; i < num_timers; i++) {
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timers[i], MCPWM_TIMER_START_STOP_EMPTY));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
check_mcpwm_timer_phase(timers, num_timers, 0, MCPWM_TIMER_DIRECTION_UP);
|
||||
|
||||
printf("start timer and then stop when full\r\n");
|
||||
for (int i = 0; i < num_timers; i++) {
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timers[i], MCPWM_TIMER_START_STOP_FULL));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
check_mcpwm_timer_phase(timers, num_timers, config.period_ticks / 2, MCPWM_TIMER_DIRECTION_DOWN);
|
||||
|
||||
printf("start freely and stop manually when full\r\n");
|
||||
for (int i = 0; i < num_timers; i++) {
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timers[i], MCPWM_TIMER_START_NO_STOP));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// stop at next counter full
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timers[i], MCPWM_TIMER_STOP_FULL));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
check_mcpwm_timer_phase(timers, num_timers, config.period_ticks / 2, MCPWM_TIMER_DIRECTION_DOWN);
|
||||
|
||||
printf("start freely and stop manually when empty\r\n");
|
||||
for (int i = 0; i < num_timers; i++) {
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timers[i], MCPWM_TIMER_START_NO_STOP));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// stop at next counter empty
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timers[i], MCPWM_TIMER_STOP_EMPTY));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
check_mcpwm_timer_phase(timers, num_timers, 0, MCPWM_TIMER_DIRECTION_UP);
|
||||
|
||||
// can't delete timer before disable
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, mcpwm_del_timer(timers[0]));
|
||||
|
||||
printf("disable timers\r\n");
|
||||
for (int i = 0; i < num_timers; i++) {
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timers[i]));
|
||||
}
|
||||
|
||||
printf("delete timers\r\n");
|
||||
for (int i = 0; i < num_timers; i++) {
|
||||
TEST_ESP_OK(mcpwm_del_timer(timers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
#define TEST_MCPWM_TIMER_EVENT_BIT_FULL (1 << 0)
|
||||
#define TEST_MCPWM_TIMER_EVENT_BIT_EMPTY (1 << 1)
|
||||
#define TEST_MCPWM_TIMER_EVENT_BIT_STOP (1 << 2)
|
||||
|
||||
typedef struct {
|
||||
EventGroupHandle_t event_group;
|
||||
uint32_t expected_full_counts;
|
||||
uint32_t expected_empty_counts;
|
||||
uint32_t accumulate_full_counts;
|
||||
uint32_t accumulate_empty_counts;
|
||||
} test_mcpwm_timer_user_data_t;
|
||||
|
||||
static bool test_on_stop(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_data)
|
||||
{
|
||||
test_mcpwm_timer_user_data_t *udata = (test_mcpwm_timer_user_data_t *)user_data;
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
esp_rom_printf("timer stopped at %u\r\n", edata->count_value);
|
||||
TEST_ASSERT_EQUAL(0, edata->count_value);
|
||||
xEventGroupSetBitsFromISR(udata->event_group, TEST_MCPWM_TIMER_EVENT_BIT_STOP, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
static bool test_on_full(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_data)
|
||||
{
|
||||
test_mcpwm_timer_user_data_t *udata = (test_mcpwm_timer_user_data_t *)user_data;
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
udata->accumulate_full_counts++;
|
||||
if (udata->accumulate_full_counts >= udata->expected_full_counts) {
|
||||
udata->accumulate_full_counts = 0;
|
||||
xEventGroupSetBitsFromISR(udata->event_group, TEST_MCPWM_TIMER_EVENT_BIT_FULL, &high_task_wakeup);
|
||||
}
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
static bool test_on_empty(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_data)
|
||||
{
|
||||
test_mcpwm_timer_user_data_t *udata = (test_mcpwm_timer_user_data_t *)user_data;
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
udata->accumulate_empty_counts++;
|
||||
if (udata->accumulate_empty_counts >= udata->expected_empty_counts) {
|
||||
udata->accumulate_empty_counts = 0;
|
||||
xEventGroupSetBitsFromISR(udata->event_group, TEST_MCPWM_TIMER_EVENT_BIT_EMPTY, &high_task_wakeup);
|
||||
}
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("mcpwm_timer_event_callbacks", "[mcpwm]")
|
||||
{
|
||||
EventGroupHandle_t event_group = xEventGroupCreate();
|
||||
EventBits_t bits = 0;
|
||||
mcpwm_timer_config_t timer_config = {
|
||||
.group_id = 0,
|
||||
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1us per tick
|
||||
.period_ticks = 20 * 1000, // 20ms, 50Hz
|
||||
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
|
||||
};
|
||||
mcpwm_timer_handle_t timer = NULL;
|
||||
printf("create mcpwm timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
|
||||
|
||||
printf("register event callbacks\r\n");
|
||||
mcpwm_timer_event_callbacks_t cbs = {
|
||||
.on_stop = test_on_stop,
|
||||
.on_full = test_on_full,
|
||||
.on_empty = test_on_empty,
|
||||
};
|
||||
test_mcpwm_timer_user_data_t udata = {
|
||||
.event_group = event_group,
|
||||
.expected_empty_counts = 50,
|
||||
.expected_full_counts = 50,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_timer_register_event_callbacks(timer, &cbs, &udata));
|
||||
|
||||
printf("enable timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_enable(timer));
|
||||
|
||||
printf("start timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
|
||||
|
||||
printf("wait for full and empty events\r\n");
|
||||
bits = xEventGroupWaitBits(event_group, TEST_MCPWM_TIMER_EVENT_BIT_FULL | TEST_MCPWM_TIMER_EVENT_BIT_EMPTY, pdTRUE, pdTRUE, pdMS_TO_TICKS(1050));
|
||||
TEST_ASSERT_EQUAL(TEST_MCPWM_TIMER_EVENT_BIT_FULL | TEST_MCPWM_TIMER_EVENT_BIT_EMPTY, bits);
|
||||
|
||||
printf("stop timer and wait for event\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
|
||||
bits = xEventGroupWaitBits(event_group, TEST_MCPWM_TIMER_EVENT_BIT_STOP, pdTRUE, pdTRUE, pdMS_TO_TICKS(50));
|
||||
TEST_ASSERT_EQUAL(TEST_MCPWM_TIMER_EVENT_BIT_STOP, bits);
|
||||
|
||||
printf("disable timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_timer_disable(timer));
|
||||
|
||||
printf("delete timer\r\n");
|
||||
TEST_ESP_OK(mcpwm_del_timer(timer));
|
||||
vEventGroupDelete(event_group);
|
||||
}
|
20
components/driver/test_apps/mcpwm/main/test_mcpwm_utils.c
Normal file
20
components/driver/test_apps/mcpwm/main/test_mcpwm_utils.c
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "unity.h"
|
||||
#include "esp_private/mcpwm.h"
|
||||
#include "test_mcpwm_utils.h"
|
||||
|
||||
void check_mcpwm_timer_phase(mcpwm_timer_handle_t *timers, size_t num_timers,
|
||||
uint32_t expected_count, mcpwm_timer_direction_t expected_direction)
|
||||
{
|
||||
uint32_t count_value;
|
||||
mcpwm_timer_direction_t direction;
|
||||
for (size_t i = 0; i < num_timers; i++) {
|
||||
TEST_ESP_OK(mcpwm_timer_get_phase(timers[i], &count_value, &direction));
|
||||
TEST_ASSERT_INT_WITHIN(1, expected_count, count_value);
|
||||
TEST_ASSERT_EQUAL(expected_direction, direction);
|
||||
}
|
||||
}
|
17
components/driver/test_apps/mcpwm/main/test_mcpwm_utils.h
Normal file
17
components/driver/test_apps/mcpwm/main/test_mcpwm_utils.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_attr.h"
|
||||
#include "driver/mcpwm_types.h"
|
||||
|
||||
#if CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
#define TEST_MCPWM_CALLBACK_ATTR IRAM_ATTR
|
||||
#else
|
||||
#define TEST_MCPWM_CALLBACK_ATTR
|
||||
#endif // CONFIG_MCPWM_ISR_IRAM_SAFE
|
||||
|
||||
void check_mcpwm_timer_phase(mcpwm_timer_handle_t *timers, size_t num_timers,
|
||||
uint32_t expected_count, mcpwm_timer_direction_t expected_direction);
|
22
components/driver/test_apps/mcpwm/pytest_mcpwm.py
Normal file
22
components/driver/test_apps/mcpwm/pytest_mcpwm.py
Normal file
@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'release',
|
||||
'iram_safe',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_mcpwm(dut: Dut) -> None:
|
||||
dut.expect('Press ENTER to see the list of tests')
|
||||
dut.write('*')
|
||||
dut.expect_unity_test_output()
|
5
components/driver/test_apps/mcpwm/sdkconfig.ci.iram_safe
Normal file
5
components/driver/test_apps/mcpwm/sdkconfig.ci.iram_safe
Normal file
@ -0,0 +1,5 @@
|
||||
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||
CONFIG_MCPWM_ISR_IRAM_SAFE=y
|
||||
|
||||
# silent the error check, as the error string are stored in rodata, causing RTL check failure
|
||||
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
|
5
components/driver/test_apps/mcpwm/sdkconfig.ci.release
Normal file
5
components/driver/test_apps/mcpwm/sdkconfig.ci.release
Normal file
@ -0,0 +1,5 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
2
components/driver/test_apps/mcpwm/sdkconfig.defaults
Normal file
2
components/driver/test_apps/mcpwm/sdkconfig.defaults
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT=n
|
Loading…
x
Reference in New Issue
Block a user