mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
MCPWM/sync: expose API for configuring timer sync
removed example: mcpwm_basic_config Closes https://github.com/espressif/esp-idf/issues/5429 Co-authored-by: wubowen <wubowen@espressif.com>
This commit is contained in:
parent
66b26abc87
commit
932d4d13f0
@ -130,11 +130,30 @@ typedef enum {
|
||||
* @brief MCPWM select sync signal input
|
||||
*/
|
||||
typedef enum {
|
||||
MCPWM_SELECT_SYNC0 = 4, /*!<Select SYNC0 as input*/
|
||||
MCPWM_SELECT_SYNC1, /*!<Select SYNC1 as input*/
|
||||
MCPWM_SELECT_SYNC2, /*!<Select SYNC2 as input*/
|
||||
MCPWM_SELECT_NO_INPUT, /*!<No sync input selected*/
|
||||
MCPWM_SELECT_TIMER0_SYNC, /*!<Select software sync signal from timer0 as input*/
|
||||
MCPWM_SELECT_TIMER1_SYNC, /*!<Select software sync signal from timer1 as input*/
|
||||
MCPWM_SELECT_TIMER2_SYNC, /*!<Select software sync signal from timer2 as input*/
|
||||
MCPWM_SELECT_GPIO_SYNC0, /*!<Select GPIO SYNC0 as input*/
|
||||
MCPWM_SELECT_GPIO_SYNC1, /*!<Select GPIO SYNC1 as input*/
|
||||
MCPWM_SELECT_GPIO_SYNC2, /*!<Select GPIO SYNC2 as input*/
|
||||
} mcpwm_sync_signal_t;
|
||||
|
||||
// backward compatibility
|
||||
#define MCPWM_SELCT_SYNC0 MCPWM_SELCT_GPIO_SYNC0
|
||||
#define MCPWM_SELCT_SYNC1 MCPWM_SELCT_GPIO_SYNC1
|
||||
#define MCPWM_SELCT_SYNC2 MCPWM_SELCT_GPIO_SYNC2
|
||||
|
||||
/**
|
||||
* @brief MCPWM timer sync event trigger
|
||||
*/
|
||||
typedef enum {
|
||||
MCPWM_SWSYNC_SOURCE_SYNCIN, /*!<the input sync signal will be routed to its sync output path*/
|
||||
MCPWM_SWSYNC_SOURCE_TEZ, /*!<sync signal generated when timer counts to zero*/
|
||||
MCPWM_SWSYNC_SOURCE_TEP, /*!<sync signal generated when timer counts to peak*/
|
||||
MCPWM_SWSYNC_SOURCE_DISABLED, /*!<timer does not generate sync signals*/
|
||||
} mcpwm_timer_sync_trigger_t;
|
||||
|
||||
/**
|
||||
* @brief MCPWM select triggering level of fault signal
|
||||
*/
|
||||
@ -294,6 +313,15 @@ typedef struct {
|
||||
void *user_data; /*!<User defined ISR callback function args*/
|
||||
} mcpwm_capture_config_t;
|
||||
|
||||
/**
|
||||
* @brief MCPWM config sync structure
|
||||
*/
|
||||
typedef struct {
|
||||
mcpwm_sync_signal_t sync_sig; /*!<Set sync input signal that will cause timer to sync*/
|
||||
uint32_t timer_val; /*!<Counter value to be set after sync, in 0 ~ 999, unit: 1 / 1000 * peak*/
|
||||
mcpwm_timer_direction_t count_direction; /*!<Counting direction to be set after sync */
|
||||
} mcpwm_sync_config_t;
|
||||
|
||||
/**
|
||||
* @brief This function initializes each gpio signal for MCPWM
|
||||
*
|
||||
@ -793,20 +821,36 @@ uint32_t mcpwm_capture_signal_get_value(mcpwm_unit_t mcpwm_num, mcpwm_capture_si
|
||||
uint32_t mcpwm_capture_signal_get_edge(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t cap_sig);
|
||||
|
||||
/**
|
||||
* @brief Initialize sync submodule
|
||||
* @brief Initialize sync submodule and sets the signal that will cause the timer be loaded with pre-defined value
|
||||
*
|
||||
* @param mcpwm_num set MCPWM unit(0-1)
|
||||
* @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers
|
||||
* @param sync_sig set the synchronization pin, which needs to be enabled
|
||||
* @param sync_sig set the synchronization input signal
|
||||
* @param phase_val phase value in 1/1000 (for 86.7%, phase_val = 867) which timer moves to on sync signal
|
||||
*
|
||||
* @note Count direction is undefined within this API
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
__attribute__((deprecated("please use mcpwm_sync_configure() instead")))
|
||||
esp_err_t mcpwm_sync_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_sync_signal_t sync_sig,
|
||||
uint32_t phase_val);
|
||||
|
||||
/**
|
||||
* @brief Initialize sync submodule and sets the signal that will cause the timer be loaded with pre-defined value
|
||||
*
|
||||
* @param mcpwm_num set MCPWM unit(0-1)
|
||||
* @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers
|
||||
* @param sync_conf sync configuration on this timer
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t mcpwm_sync_configure(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, const mcpwm_sync_config_t *sync_conf);
|
||||
|
||||
/**
|
||||
* @brief Disable sync submodule on given timer
|
||||
*
|
||||
@ -819,6 +863,49 @@ esp_err_t mcpwm_sync_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcp
|
||||
*/
|
||||
esp_err_t mcpwm_sync_disable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num);
|
||||
|
||||
/**
|
||||
* @brief Set sync output on given timer
|
||||
* Configures what event triggers MCPWM timer to output a sync signal.
|
||||
*
|
||||
* @param mcpwm_num set MCPWM unit(0-1)
|
||||
* @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers
|
||||
* @param trigger set the trigger that will cause the timer to generate a software sync signal.
|
||||
* Specifically, `MCPWM_SWSYNC_SOURCE_DISABLED` will disable the timer from generating sync signal.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t mcpwm_set_timer_sync_output(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_timer_sync_trigger_t trigger);
|
||||
|
||||
/**
|
||||
* @brief Trigger a software sync event and sends it to a specific timer.
|
||||
*
|
||||
* @param mcpwm_num set MCPWM unit(0-1)
|
||||
* @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers
|
||||
*
|
||||
* @note This software sync event will have the same effect as hw one, except that:
|
||||
* - On esp32s3 the soft sync event can be routed to its output if `MCPWM_SWSYNC_SOURCE_SYNCIN` is selected via `mcpwm_set_timer_sync_output()`
|
||||
* - On esp32 there is no such behavior and soft sync event will only take effect on this timer and can not be propagated to others.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Function pointer error.
|
||||
*/
|
||||
esp_err_t mcpwm_timer_trigger_soft_sync(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num);
|
||||
|
||||
/**
|
||||
* @brief Set external GPIO sync input inverter
|
||||
*
|
||||
* @param mcpwm_num set MCPWM unit(0-1)
|
||||
* @param sync_sig set sync signal of MCPWM, only supports GPIO sync signal
|
||||
* @param invert whether GPIO sync source input is inverted (to get negative edge trigger)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Function pointer error.
|
||||
*/
|
||||
esp_err_t mcpwm_sync_invert_gpio_synchro(mcpwm_unit_t mcpwm_num, mcpwm_sync_signal_t sync_sig, bool invert);
|
||||
|
||||
/**
|
||||
* @brief Register MCPWM interrupt handler, the handler is an ISR.
|
||||
* the handler will be attached to the same CPU core that this function is running on.
|
||||
|
@ -901,15 +901,44 @@ esp_err_t mcpwm_sync_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcp
|
||||
uint32_t phase_val)
|
||||
{
|
||||
MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
|
||||
ESP_RETURN_ON_FALSE(sync_sig <= MCPWM_SELECT_GPIO_SYNC2, ESP_ERR_INVALID_ARG, TAG, "invalid sync_sig");
|
||||
ESP_RETURN_ON_FALSE(phase_val < 1000, ESP_ERR_INVALID_ARG, TAG, "phase_val must within 0~999");
|
||||
mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
|
||||
|
||||
mcpwm_critical_enter(mcpwm_num);
|
||||
uint32_t set_phase = mcpwm_ll_timer_get_peak(hal->dev, timer_num, false) * phase_val / 1000;
|
||||
mcpwm_ll_timer_set_sync_phase_value(hal->dev, timer_num, set_phase);
|
||||
if (sync_sig >= MCPWM_SELECT_SYNC0) {
|
||||
mcpwm_ll_timer_set_timer_synchro(hal->dev, timer_num, sync_sig - MCPWM_SELECT_SYNC0);
|
||||
if (sync_sig == MCPWM_SELECT_NO_INPUT) {
|
||||
mcpwm_ll_timer_set_soft_synchro(hal->dev, timer_num);
|
||||
} else if (sync_sig <= MCPWM_SELECT_TIMER2_SYNC) {
|
||||
mcpwm_ll_timer_set_timer_synchro(hal->dev, timer_num, sync_sig - MCPWM_SELECT_TIMER0_SYNC);
|
||||
} else {
|
||||
mcpwm_ll_timer_set_gpio_synchro(hal->dev, timer_num, sync_sig - MCPWM_SELECT_GPIO_SYNC0);
|
||||
}
|
||||
mcpwm_ll_timer_enable_sync_input(hal->dev, timer_num, true);
|
||||
mcpwm_critical_exit(mcpwm_num);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_sync_configure(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, const mcpwm_sync_config_t *sync_conf)
|
||||
{
|
||||
MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
|
||||
ESP_RETURN_ON_FALSE(sync_conf->sync_sig <= MCPWM_SELECT_GPIO_SYNC2, ESP_ERR_INVALID_ARG, TAG, "invalid sync_sig");
|
||||
mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
|
||||
|
||||
mcpwm_critical_enter(mcpwm_num);
|
||||
mcpwm_ll_timer_set_sync_phase_direction(hal->dev, timer_num, sync_conf->count_direction);
|
||||
// sync TEP with current setting
|
||||
uint32_t set_phase = 0;
|
||||
set_phase = mcpwm_ll_timer_get_peak(hal->dev, timer_num, false) * sync_conf->timer_val / 1000;
|
||||
mcpwm_ll_timer_set_sync_phase_value(hal->dev, timer_num, set_phase);
|
||||
if (sync_conf->sync_sig == MCPWM_SELECT_NO_INPUT){
|
||||
mcpwm_ll_timer_set_soft_synchro(hal->dev, timer_num);
|
||||
} else if (sync_conf->sync_sig <= MCPWM_SELECT_TIMER2_SYNC) {
|
||||
mcpwm_ll_timer_set_timer_synchro(hal->dev, timer_num, sync_conf->sync_sig - MCPWM_SELECT_TIMER0_SYNC);
|
||||
} else {
|
||||
mcpwm_ll_timer_set_gpio_synchro(hal->dev, timer_num, sync_conf->sync_sig - MCPWM_SELECT_GPIO_SYNC0);
|
||||
}
|
||||
mcpwm_ll_timer_sync_out_penetrate(hal->dev, timer_num);
|
||||
mcpwm_ll_timer_enable_sync_input(hal->dev, timer_num, true);
|
||||
mcpwm_critical_exit(mcpwm_num);
|
||||
return ESP_OK;
|
||||
@ -926,6 +955,55 @@ esp_err_t mcpwm_sync_disable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_timer_trigger_soft_sync(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
|
||||
{
|
||||
MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
|
||||
|
||||
mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
|
||||
mcpwm_critical_enter(mcpwm_num);
|
||||
mcpwm_ll_timer_trigger_soft_sync(hal->dev, timer_num);
|
||||
mcpwm_critical_exit(mcpwm_num);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_sync_invert_gpio_synchro(mcpwm_unit_t mcpwm_num, mcpwm_sync_signal_t sync_sig, bool invert){
|
||||
ESP_RETURN_ON_FALSE(sync_sig >= MCPWM_SELECT_GPIO_SYNC0 && sync_sig <= MCPWM_SELECT_GPIO_SYNC2,
|
||||
ESP_ERR_INVALID_ARG, TAG, "invalid sync sig");
|
||||
|
||||
mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
|
||||
mcpwm_critical_enter(mcpwm_num);
|
||||
mcpwm_ll_invert_gpio_synchro(hal->dev, sync_sig - MCPWM_SELECT_GPIO_SYNC0, invert);
|
||||
mcpwm_critical_exit(mcpwm_num);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_set_timer_sync_output(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_timer_sync_trigger_t trigger)
|
||||
{
|
||||
MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
|
||||
|
||||
mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
|
||||
mcpwm_critical_enter(mcpwm_num);
|
||||
switch (trigger) {
|
||||
case MCPWM_SWSYNC_SOURCE_SYNCIN:
|
||||
mcpwm_ll_timer_sync_out_penetrate(hal->dev, timer_num);
|
||||
break;
|
||||
case MCPWM_SWSYNC_SOURCE_TEZ:
|
||||
mcpwm_ll_timer_sync_out_on_timer_event(hal->dev, timer_num, MCPWM_TIMER_EVENT_ZERO);
|
||||
break;
|
||||
case MCPWM_SWSYNC_SOURCE_TEP:
|
||||
mcpwm_ll_timer_sync_out_on_timer_event(hal->dev, timer_num, MCPWM_TIMER_EVENT_PEAK);
|
||||
break;
|
||||
case MCPWM_SWSYNC_SOURCE_DISABLED:
|
||||
default:
|
||||
mcpwm_ll_timer_disable_sync_out(hal->dev, timer_num);
|
||||
break;
|
||||
}
|
||||
mcpwm_critical_exit(mcpwm_num);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mcpwm_isr_register(mcpwm_unit_t mcpwm_num, void (*fn)(void *), void *arg, int intr_alloc_flags, intr_handle_t *handle)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "soc/rtc.h"
|
||||
#if SOC_MCPWM_SUPPORTED
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "driver/pcnt.h"
|
||||
@ -23,7 +24,9 @@
|
||||
#define TEST_PWMA_GPIO (2)
|
||||
#define TEST_PWMB_GPIO (4)
|
||||
#define TEST_FAULT_GPIO (21)
|
||||
#define TEST_SYNC_GPIO (21)
|
||||
#define TEST_SYNC_GPIO_0 (21)
|
||||
#define TEST_SYNC_GPIO_1 (18)
|
||||
#define TEST_SYNC_GPIO_2 (19)
|
||||
#define TEST_CAP_GPIO (21)
|
||||
|
||||
#define MCPWM_TEST_GROUP_CLK_HZ (SOC_MCPWM_BASE_CLK_HZ / 16)
|
||||
@ -33,7 +36,7 @@ const static mcpwm_io_signals_t pwma[] = {MCPWM0A, MCPWM1A, MCPWM2A};
|
||||
const static mcpwm_io_signals_t pwmb[] = {MCPWM0B, MCPWM1B, MCPWM2B};
|
||||
const static mcpwm_fault_signal_t fault_sig_array[] = {MCPWM_SELECT_F0, MCPWM_SELECT_F1, MCPWM_SELECT_F2};
|
||||
const static mcpwm_io_signals_t fault_io_sig_array[] = {MCPWM_FAULT_0, MCPWM_FAULT_1, MCPWM_FAULT_2};
|
||||
const static mcpwm_sync_signal_t sync_sig_array[] = {MCPWM_SELECT_SYNC0, MCPWM_SELECT_SYNC1, MCPWM_SELECT_SYNC2};
|
||||
const static mcpwm_sync_signal_t sync_sig_array[] = {MCPWM_SELECT_GPIO_SYNC0, MCPWM_SELECT_GPIO_SYNC1, MCPWM_SELECT_GPIO_SYNC2};
|
||||
const static mcpwm_io_signals_t sync_io_sig_array[] = {MCPWM_SYNC_0, MCPWM_SYNC_1, MCPWM_SYNC_2};
|
||||
const static mcpwm_capture_signal_t cap_sig_array[] = {MCPWM_SELECT_CAP0, MCPWM_SELECT_CAP1, MCPWM_SELECT_CAP2};
|
||||
const static mcpwm_io_signals_t cap_io_sig_array[] = {MCPWM_CAP_0, MCPWM_CAP_1, MCPWM_CAP_2};
|
||||
@ -352,18 +355,25 @@ static void mcpwm_sync_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
|
||||
mcpwm_io_signals_t sync_io_sig = sync_io_sig_array[timer];
|
||||
|
||||
mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ);
|
||||
TEST_ESP_OK(test_mcpwm_gpio_init(unit, sync_io_sig, TEST_SYNC_GPIO));
|
||||
gpio_set_level(TEST_SYNC_GPIO, 0);
|
||||
TEST_ESP_OK(test_mcpwm_gpio_init(unit, sync_io_sig, TEST_SYNC_GPIO_0));
|
||||
gpio_set_level(TEST_SYNC_GPIO_0, 0);
|
||||
|
||||
TEST_ESP_OK(mcpwm_sync_enable(unit, timer, sync_sig, 200));
|
||||
mcpwm_sync_config_t sync_conf = {
|
||||
.sync_sig = sync_sig,
|
||||
.timer_val = 200,
|
||||
.count_direction = MCPWM_TIMER_DIRECTION_UP,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_sync_configure(unit, timer, &sync_conf));
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
gpio_set_level(TEST_SYNC_GPIO, 1); // trigger an external sync event
|
||||
gpio_set_level(TEST_SYNC_GPIO_0, 1); // trigger an external sync event
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
mcpwm_timer_trigger_soft_sync(unit, timer); // trigger a software sync event
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
TEST_ESP_OK(mcpwm_sync_disable(unit, timer));
|
||||
TEST_ESP_OK(mcpwm_stop(unit, timer));
|
||||
}
|
||||
|
||||
TEST_CASE("MCPWM timer sync test", "[mcpwm]")
|
||||
TEST_CASE("MCPWM timer GPIO sync test", "[mcpwm]")
|
||||
{
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) {
|
||||
@ -372,6 +382,95 @@ TEST_CASE("MCPWM timer sync test", "[mcpwm]")
|
||||
}
|
||||
}
|
||||
|
||||
static void mcpwm_swsync_test(mcpwm_unit_t unit) {
|
||||
const uint32_t test_sync_phase = 20;
|
||||
// used only in this area but need to be reset every time. mutex is not needed
|
||||
// store timestamps captured from ISR callback
|
||||
static uint64_t cap_timestamp[3];
|
||||
cap_timestamp[0] = 0;
|
||||
cap_timestamp[1] = 0;
|
||||
cap_timestamp[2] = 0;
|
||||
// control the start of capture to avoid unstable data
|
||||
static volatile bool log_cap;
|
||||
log_cap = false;
|
||||
|
||||
// cb function, to update capture value
|
||||
// only log when channel1 comes at first, then channel2, and do not log further more.
|
||||
bool capture_callback(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_channel, const cap_event_data_t *edata,
|
||||
void *user_data) {
|
||||
if (log_cap && (cap_timestamp[1] == 0 || cap_timestamp[2] == 0)) {
|
||||
if (cap_channel == MCPWM_SELECT_CAP1 && cap_timestamp[1] == 0) {
|
||||
cap_timestamp[1] = edata->cap_value;
|
||||
}
|
||||
if (cap_channel == MCPWM_SELECT_CAP2 && cap_timestamp[1] != 0) {
|
||||
cap_timestamp[2] = edata->cap_value;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// configure all timer output 10% PWM
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
mcpwm_setup_testbench(unit, i, 1000, 10.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ);
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
// configure capture for verification
|
||||
mcpwm_capture_config_t conf = {
|
||||
.cap_edge = MCPWM_POS_EDGE,
|
||||
.cap_prescale = 1,
|
||||
.capture_cb = capture_callback,
|
||||
.user_data = NULL,
|
||||
};
|
||||
TEST_ESP_OK(test_mcpwm_gpio_init(unit, MCPWM_CAP_0, TEST_SYNC_GPIO_0));
|
||||
TEST_ESP_OK(test_mcpwm_gpio_init(unit, MCPWM_CAP_1, TEST_SYNC_GPIO_1));
|
||||
TEST_ESP_OK(test_mcpwm_gpio_init(unit, MCPWM_CAP_2, TEST_SYNC_GPIO_2));
|
||||
TEST_ESP_OK(mcpwm_capture_enable_channel(unit, MCPWM_SELECT_CAP0, &conf));
|
||||
TEST_ESP_OK(mcpwm_capture_enable_channel(unit, MCPWM_SELECT_CAP1, &conf));
|
||||
TEST_ESP_OK(mcpwm_capture_enable_channel(unit, MCPWM_SELECT_CAP2, &conf));
|
||||
// timer0 produce sync sig at TEZ, timer1 and timer2 consume, to make sure last two can be synced precisely
|
||||
// timer1 and timer2 will be synced with TEZ of timer0 at a known phase.
|
||||
mcpwm_sync_config_t sync_conf = {
|
||||
.sync_sig = MCPWM_SELECT_TIMER0_SYNC,
|
||||
.timer_val = 0,
|
||||
.count_direction = MCPWM_TIMER_DIRECTION_UP,
|
||||
};
|
||||
TEST_ESP_OK(mcpwm_sync_configure(unit, MCPWM_TIMER_1, &sync_conf));
|
||||
sync_conf.timer_val = 1000 - test_sync_phase;
|
||||
TEST_ESP_OK(mcpwm_sync_configure(unit, MCPWM_TIMER_2, &sync_conf));
|
||||
TEST_ESP_OK(mcpwm_set_timer_sync_output(unit, MCPWM_TIMER_0, MCPWM_SWSYNC_SOURCE_TEZ));
|
||||
// init gpio at the end
|
||||
TEST_ESP_OK(test_mcpwm_gpio_init(unit, MCPWM0A, TEST_SYNC_GPIO_0));
|
||||
TEST_ESP_OK(test_mcpwm_gpio_init(unit, MCPWM1A, TEST_SYNC_GPIO_1));
|
||||
TEST_ESP_OK(test_mcpwm_gpio_init(unit, MCPWM2A, TEST_SYNC_GPIO_2));
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
log_cap = true;
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
uint32_t delta_timestamp_us = (cap_timestamp[2] - cap_timestamp[1]) * 1000000 / rtc_clk_apb_freq_get();
|
||||
uint32_t expected_phase_us = 1000000 / mcpwm_get_frequency(unit, MCPWM_TIMER_0) * test_sync_phase / 1000;
|
||||
// accept +-2 error
|
||||
TEST_ASSERT_UINT32_WITHIN(2, expected_phase_us, delta_timestamp_us);
|
||||
|
||||
// tear down
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
TEST_ESP_OK(mcpwm_capture_disable_channel(unit, i));
|
||||
TEST_ESP_OK(mcpwm_sync_disable(unit, i));
|
||||
TEST_ESP_OK(mcpwm_stop(unit, i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MCPWM timer swsync test", "[mcpwm]")
|
||||
{
|
||||
for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
|
||||
mcpwm_swsync_test(i);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
static void mcpwm_capture_test(mcpwm_unit_t unit, mcpwm_capture_signal_t cap_chan)
|
||||
|
@ -100,6 +100,7 @@
|
||||
#define SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP (1) ///< The number of capture timers that each group has
|
||||
#define SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER (3) ///< The number of capture channels that each capture timer has
|
||||
#define SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP (3) ///< The number of GPIO synchros that each group has
|
||||
#define SOC_MCPWM_SWSYNC_CAN_PROPAGATE (1) ///< Software sync event can be routed to its output
|
||||
#define SOC_MCPWM_BASE_CLK_HZ (160000000ULL) ///< Base Clock frequency of 160MHz
|
||||
|
||||
/*-------------------------- MPU CAPS ----------------------------------------*/
|
||||
|
@ -31,6 +31,7 @@ Contents
|
||||
* `Configure`_ a basic functionality of the outputs
|
||||
* `Operate`_ the outputs to drive a motor
|
||||
* `Adjust`_ how the motor is driven
|
||||
* `Synchronize`_ sync timers to work together
|
||||
* `Capture`_ external signals to provide additional control over the outputs
|
||||
* Use `Fault Handler`_ to detect and manage faults
|
||||
* Add a higher frequency `Carrier`_, if output signals are passed through an isolation transformer
|
||||
@ -84,15 +85,59 @@ There are couple of ways to adjust a signal on the outputs and changing how the
|
||||
|
||||
* Set specific PWM frequency by calling :cpp:func:`mcpwm_set_frequency`. This may be required to adjust to electrical or mechanical characteristics of particular motor and driver. To check what frequency is set, use function :cpp:func:`mcpwm_get_frequency`.
|
||||
* Introduce a dead time between outputs A and B when they are changing the state to reverse direction of the motor rotation. This is to make up for on/off switching delay of the motor driver FETs. The dead time options are defined in :cpp:type:`mcpwm_deadtime_type_t` and enabled by calling :cpp:func:`mcpwm_deadtime_enable`. To disable this functionality call :cpp:func:`mcpwm_deadtime_disable`.
|
||||
* Synchronize outputs of operator submodules, e.g. to get raising edge of PWM0A/B and PWM1A/B to start exactly at the same time, or shift them between each other by a given phase. Synchronization is triggered by ``SYNC SIGNALS`` shown on the :ref:`block diagram <mcpwm_block_diagram>` of the MCPWM above, and defined in :cpp:type:`mcpwm_sync_signal_t`. To attach the signal to a GPIO call :cpp:func:`mcpwm_gpio_init`. You can then enable synchronization with function :cpp:func:`mcpwm_sync_enable`. As input parameters provide MCPWM unit, timer to synchronize, the synchronization signal and a phase to delay the timer.
|
||||
* Synchronize outputs of operator submodules, e.g. to get raising edge of PWM0A/B and PWM1A/B to start exactly at the same time, or shift them between each other by a given phase. Synchronization is triggered by ``SYNC SIGNALS`` shown on the :ref:`block diagram <mcpwm_block_diagram>` of the MCPWM above, and defined in :cpp:type:`mcpwm_sync_signal_t`. To attach the signal to a GPIO call :cpp:func:`mcpwm_gpio_init`. You can then enable synchronization with function :cpp:func:`mcpwm_sync_configure`. As input parameters provide MCPWM unit, timer to synchronize, the synchronization signal and a phase to delay the timer.
|
||||
|
||||
.. note::
|
||||
|
||||
Synchronization signals are referred to using two different enumerations. First one :cpp:type:`mcpwm_io_signals_t` is used together with function :cpp:func:`mcpwm_gpio_init` when selecting a GPIO as the signal input source. The second one :cpp:type:`mcpwm_sync_signal_t` is used when enabling or disabling synchronization with :cpp:func:`mcpwm_sync_enable` or :cpp:func:`mcpwm_sync_disable`.
|
||||
Synchronization signals are referred to using two different enumerations. First one :cpp:type:`mcpwm_io_signals_t` is used together with function :cpp:func:`mcpwm_gpio_init` when selecting a GPIO as the signal input source. The second one :cpp:type:`mcpwm_sync_signal_t` is used when enabling or disabling synchronization with :cpp:func:`mcpwm_sync_configure` or :cpp:func:`mcpwm_sync_disable`.
|
||||
|
||||
|
||||
* Vary the pattern of the A/B output signals by getting MCPWM counters to count up, down and up/down (automatically changing the count direction). Respective configuration is done when calling :cpp:func:`mcpwm_init`, as discussed in section `Configure`_, and selecting one of counter types from :cpp:type:`mcpwm_counter_type_t`. For explanation of how A/B PWM output signals are generated, see *{IDF_TARGET_NAME} Technical Reference Manual* > *Motor Control PWM (MCPWM)* [`PDF <{IDF_TARGET_TRM_EN_URL}#mcpwm>`__].
|
||||
|
||||
|
||||
Synchronize
|
||||
-----------
|
||||
|
||||
Each PWM timer has a synchronization input and a synchronization output. The synchronization input can be selected from other timers' synchronization outputs or GPIO signals via the GPIO matrix. Timer's synchronization signal can be generated from either the input sync signal or when the count value reaches peak/zero. Thus, the PWM timers can be chained together with their phase-locked. During synchronization, the PWM timer clock prescaler will reset its counter in order to synchronize the PWM timer clock.
|
||||
|
||||
The functionality is enabled in following steps:
|
||||
|
||||
1. Make sure the PWM timer and operator are already configured so that sync will inherit its config (count mode, freq and duty).
|
||||
2. Enabling sync input of the timer by invoking :cpp:func:`mcpwm_sync_configure`, selecting desired signal input from :cpp:type:`mcpwm_sync_signal_t`, and setting the desired phase range from 0 to 999 which is mapped to 0%~99.9%. 0 means zero phase is applied and output is fired at the same time. And selecting desired counting direction.
|
||||
3. Enabling one of sync event source from another timer or from external GPIO input.
|
||||
|
||||
To sync with another timer:
|
||||
|
||||
Enabling sync output of another timer by invoking :cpp:func:`mcpwm_set_timer_sync_output` and selecting desired event to generate sync output from :cpp:type:`mcpwm_timer_sync_trigger_t`.
|
||||
|
||||
To sync with GPIO positive edge input (negative edge requires :cpp:func:`mcpwm_sync_invert_gpio_synchro`):
|
||||
|
||||
Configuring GPIOs to act as the sync signal inputs by calling functions :cpp:func:`mcpwm_gpio_init` or :cpp:func:`mcpwm_set_pin`, which were described in section `Configure`_.
|
||||
|
||||
It's normal condition that chained sync signal may have tens or even hundreds of nanoseconds of delay between each timer output due to hardware limitation. To sync two timers accurately it is required to have the third timer occupied to produce sync event that can be consumed parallel by other two timer, so that those two timer will have no delay between each other but have the same delay between the timer which provides events. Another solution is introducing an external GPIO event source so that all three timers can be synced together with no delay.
|
||||
|
||||
.. only:: SOC_MCPWM_SWSYNC_CAN_PROPAGATE
|
||||
|
||||
Software sync event which triggered on one timer can be propagated to other timers on {IDF_TARGET_NAME}, which can be used as a tricky way to get all three timers synced without any extra requirement.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// configure timer0 as trigger source
|
||||
mcpwm_set_timer_sync_output(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SWSYNC_SOURCE_SYNCIN);
|
||||
mcpwm_sync_config_t sync_conf = {
|
||||
.sync_sig = MCPWM_SELECT_TIMER0_SYNC,
|
||||
.timer_val = 0,
|
||||
.count_direction = MCPWM_TIMER_DIRECTION_UP,
|
||||
};
|
||||
mcpwm_sync_configure(TARGET_MCPWM_UNIT, MCPWM_TIMER_0, &sync_conf);
|
||||
mcpwm_sync_configure(TARGET_MCPWM_UNIT, MCPWM_TIMER_1, &sync_conf);
|
||||
mcpwm_sync_configure(TARGET_MCPWM_UNIT, MCPWM_TIMER_2, &sync_conf);
|
||||
// then send soft sync event to timer0
|
||||
mcpwm_timer_trigger_soft_sync(MCPWM_UNIT_0, MCPWM_TIMER_0);
|
||||
|
||||
If not required anymore, the capture functionality may be disabled with :cpp:func:`mcpwm_sync_disable`.
|
||||
|
||||
|
||||
Capture
|
||||
-------
|
||||
|
||||
@ -178,7 +223,6 @@ Application Example
|
||||
|
||||
MCPWM example are located under: :example:`peripherals/mcpwm`:
|
||||
|
||||
* Demonstration how to use each submodule of the MCPWM - :example:`peripherals/mcpwm/mcpwm_basic_config`
|
||||
* Control of BLDC (brushless DC) motor with hall sensor feedback - :example:`peripherals/mcpwm/mcpwm_bldc_hall_control`
|
||||
* Brushed DC motor control - :example:`peripherals/mcpwm/mcpwm_brushed_dc_control`
|
||||
* Servo motor control - :example:`peripherals/mcpwm/mcpwm_servo_control`
|
||||
|
@ -1,35 +0,0 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# MCPWM basic config Example
|
||||
|
||||
This example will show you how to use each submodule of MCPWM unit
|
||||
|
||||
The example can't be used without modifying the code first
|
||||
|
||||
Edit the macros at the top of mcpwm_example_basic_config.c to enable/disable the submodules which are used in the example
|
||||
|
||||
|
||||
## Step 1: Pin assignment
|
||||
* The gpio init function initializes:
|
||||
* six MCPWM output pins
|
||||
* three MCPWM fault input pins
|
||||
* three MCPWM sync input pins
|
||||
* three MCPWM capture input pins
|
||||
|
||||
|
||||
## Step 2: Connection
|
||||
* Six MCPWM output pins to motor driver input signals
|
||||
* Fault, sync, capture signals can be connected to respective signals
|
||||
|
||||
|
||||
## Step 3: Initialize MCPWM
|
||||
* You need to set the frequency and duty cycle of each three MCPWM timer along with other parameters mentioned
|
||||
* You need to set the MCPWM channel you want to use, with these timers
|
||||
|
||||
|
||||
## Step 4: Testing
|
||||
* The deadtime module, set deadtime type and with value as time*100ns
|
||||
* The sync module, synchonizes all the timer pulses
|
||||
* The fault module when enabled takes action on MCPWM signals when fault occurs
|
||||
* The capture module captures input signal(digital i.e. hall sensor value, etc), timing between two rising/falling edge
|
@ -1,2 +0,0 @@
|
||||
idf_component_register(SRCS "mcpwm_basic_config_example.c"
|
||||
INCLUDE_DIRS ".")
|
@ -1,168 +0,0 @@
|
||||
/* MCPWM basic config example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This example will show you how to use each submodule of MCPWM unit.
|
||||
* The example can't be used without modifying the code first.
|
||||
* Edit the macros at the top of mcpwm_example_basic_config.c to enable/disable the submodules which are used in the example.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "string.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/mcpwm.h"
|
||||
|
||||
#define MCPWM_EN_CARRIER 0 //Make this 1 to test carrier submodule of mcpwm, set high frequency carrier parameters
|
||||
#define MCPWM_EN_DEADTIME 0 //Make this 1 to test deadtime submodule of mcpwm, set deadtime value and deadtime mode
|
||||
#define MCPWM_EN_FAULT 0 //Make this 1 to test fault submodule of mcpwm, set action on MCPWM signal on fault occurence like overcurrent, overvoltage, etc
|
||||
#define MCPWM_EN_SYNC 0 //Make this 1 to test sync submodule of mcpwm, sync timer signals
|
||||
#define MCPWM_GPIO_INIT 0 //select which function to use to initialize gpio signals
|
||||
|
||||
#define GPIO_PWM0A_OUT 19 //Set GPIO 19 as PWM0A
|
||||
#define GPIO_PWM0B_OUT 18 //Set GPIO 18 as PWM0B
|
||||
#define GPIO_PWM1A_OUT 17 //Set GPIO 17 as PWM1A
|
||||
#define GPIO_PWM1B_OUT 16 //Set GPIO 16 as PWM1B
|
||||
#define GPIO_PWM2A_OUT 15 //Set GPIO 15 as PWM2A
|
||||
#define GPIO_PWM2B_OUT 14 //Set GPIO 14 as PWM2B
|
||||
#define GPIO_SYNC0_IN 2 //Set GPIO 02 as SYNC0
|
||||
#define GPIO_SYNC1_IN 4 //Set GPIO 04 as SYNC1
|
||||
#define GPIO_SYNC2_IN 5 //Set GPIO 05 as SYNC2
|
||||
#define GPIO_FAULT0_IN 32 //Set GPIO 32 as FAULT0
|
||||
#define GPIO_FAULT1_IN 33 //Set GPIO 33 as FAULT1
|
||||
#define GPIO_FAULT2_IN 34 //Set GPIO 34 as FAULT2
|
||||
|
||||
static void mcpwm_example_gpio_initialize(void)
|
||||
{
|
||||
printf("initializing mcpwm gpio...\n");
|
||||
#if MCPWM_GPIO_INIT
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_PWM0A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_PWM0B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1A, GPIO_PWM1A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1B, GPIO_PWM1B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2A, GPIO_PWM2A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2B, GPIO_PWM2B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, GPIO_CAP0_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_1, GPIO_CAP1_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_2, GPIO_CAP2_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_SYNC_0, GPIO_SYNC0_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_SYNC_1, GPIO_SYNC1_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_SYNC_2, GPIO_SYNC2_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_FAULT_0, GPIO_FAULT0_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_FAULT_1, GPIO_FAULT1_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_FAULT_2, GPIO_FAULT2_IN);
|
||||
#else
|
||||
mcpwm_pin_config_t pin_config = {
|
||||
.mcpwm0a_out_num = GPIO_PWM0A_OUT,
|
||||
.mcpwm0b_out_num = GPIO_PWM0B_OUT,
|
||||
.mcpwm1a_out_num = GPIO_PWM1A_OUT,
|
||||
.mcpwm1b_out_num = GPIO_PWM1B_OUT,
|
||||
.mcpwm2a_out_num = GPIO_PWM2A_OUT,
|
||||
.mcpwm2b_out_num = GPIO_PWM2B_OUT,
|
||||
.mcpwm_sync0_in_num = GPIO_SYNC0_IN,
|
||||
.mcpwm_sync1_in_num = GPIO_SYNC1_IN,
|
||||
.mcpwm_sync2_in_num = GPIO_SYNC2_IN,
|
||||
.mcpwm_fault0_in_num = GPIO_FAULT0_IN,
|
||||
.mcpwm_fault1_in_num = GPIO_FAULT1_IN,
|
||||
.mcpwm_fault2_in_num = GPIO_FAULT2_IN
|
||||
};
|
||||
mcpwm_set_pin(MCPWM_UNIT_0, &pin_config);
|
||||
#endif
|
||||
gpio_pulldown_en(GPIO_SYNC0_IN); //Enable pull down on SYNC0 signal
|
||||
gpio_pulldown_en(GPIO_SYNC1_IN); //Enable pull down on SYNC1 signal
|
||||
gpio_pulldown_en(GPIO_SYNC2_IN); //Enable pull down on SYNC2 signal
|
||||
gpio_pulldown_en(GPIO_FAULT0_IN); //Enable pull down on FAULT0 signal
|
||||
gpio_pulldown_en(GPIO_FAULT1_IN); //Enable pull down on FAULT1 signal
|
||||
gpio_pulldown_en(GPIO_FAULT2_IN); //Enable pull down on FAULT2 signal
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure whole MCPWM module
|
||||
*/
|
||||
static void mcpwm_example_config(void *arg)
|
||||
{
|
||||
//1. mcpwm gpio initialization
|
||||
mcpwm_example_gpio_initialize();
|
||||
|
||||
//2. initialize mcpwm configuration
|
||||
printf("Configuring Initial Parameters of mcpwm...\n");
|
||||
mcpwm_config_t pwm_config;
|
||||
pwm_config.frequency = 1000; //frequency = 1000Hz
|
||||
pwm_config.cmpr_a = 60.0; //duty cycle of PWMxA = 60.0%
|
||||
pwm_config.cmpr_b = 50.0; //duty cycle of PWMxb = 50.0%
|
||||
pwm_config.counter_mode = MCPWM_UP_COUNTER;
|
||||
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
|
||||
pwm_config.frequency = 500; //frequency = 500Hz
|
||||
pwm_config.cmpr_a = 45.9; //duty cycle of PWMxA = 45.9%
|
||||
pwm_config.cmpr_b = 7.0; //duty cycle of PWMxb = 07.0%
|
||||
pwm_config.counter_mode = MCPWM_UP_COUNTER;
|
||||
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_1, &pwm_config); //Configure PWM1A & PWM1B with above settings
|
||||
pwm_config.frequency = 400; //frequency = 400Hz
|
||||
pwm_config.cmpr_a = 23.2; //duty cycle of PWMxA = 23.2%
|
||||
pwm_config.cmpr_b = 97.0; //duty cycle of PWMxb = 97.0%
|
||||
pwm_config.counter_mode = MCPWM_UP_DOWN_COUNTER; //frequency is half when up down count mode is set i.e. SYMMETRIC PWM
|
||||
pwm_config.duty_mode = MCPWM_DUTY_MODE_1;
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_2, &pwm_config); //Configure PWM2A & PWM2B with above settings
|
||||
|
||||
#if MCPWM_EN_CARRIER
|
||||
//3. carrier configuration
|
||||
//comment if you don't want to use carrier mode
|
||||
//in carrier mode very high frequency carrier signal is generated at mcpwm high level signal
|
||||
mcpwm_carrier_config_t chop_config;
|
||||
chop_config.carrier_period = 6; //carrier period = (6 + 1)*800ns
|
||||
chop_config.carrier_duty = 3; //carrier duty = (3)*12.5%
|
||||
chop_config.carrier_os_mode = MCPWM_ONESHOT_MODE_EN; //If one shot mode is enabled then set pulse width, if disabled no need to set pulse width
|
||||
chop_config.pulse_width_in_os = 3; //first pulse width = (3 + 1)*carrier_period
|
||||
chop_config.carrier_ivt_mode = MCPWM_CARRIER_OUT_IVT_EN; //output signal inversion enable
|
||||
mcpwm_carrier_init(MCPWM_UNIT_0, MCPWM_TIMER_2, &chop_config); //Enable carrier on PWM2A and PWM2B with above settings
|
||||
//use mcpwm_carrier_disable function to disable carrier on mcpwm timer on which it was enabled
|
||||
#endif
|
||||
|
||||
#if MCPWM_EN_DEADTIME
|
||||
//4. deadtime configuration
|
||||
//comment if you don't want to use deadtime submodule
|
||||
//add rising edge delay or falling edge delay. There are 8 different types, each explained in mcpwm_deadtime_type_t in mcpwm.h
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_BYPASS_FED, 1000, 1000); //Enable deadtime on PWM2A and PWM2B with red = (1000)*100ns on PWM2A
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_BYPASS_RED, 300, 2000); //Enable deadtime on PWM1A and PWM1B with fed = (2000)*100ns on PWM1B
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_ACTIVE_RED_FED_FROM_PWMXA, 656, 67); //Enable deadtime on PWM0A and PWM0B with red = (656)*100ns & fed = (67)*100ns on PWM0A and PWM0B generated from PWM0A
|
||||
//use mcpwm_deadtime_disable function to disable deadtime on mcpwm timer on which it was enabled
|
||||
#endif
|
||||
|
||||
#if MCPWM_EN_FAULT
|
||||
//5. enable fault condition
|
||||
//comment if you don't want to use fault submodule, also u can comment the fault gpio signals
|
||||
//whenever fault occurs you can configure mcpwm signal to either force low, force high or toggle.
|
||||
//in cycmode, as soon as fault condition is over, the mcpwm signal is resumed, whereas in oneshot mode you need to reset.
|
||||
mcpwm_fault_init(MCPWM_UNIT_0, MCPWM_HIGH_LEVEL_TGR, MCPWM_SELECT_F0); //Enable FAULT, when high level occurs on FAULT0 signal
|
||||
mcpwm_fault_init(MCPWM_UNIT_0, MCPWM_HIGH_LEVEL_TGR, MCPWM_SELECT_F1); //Enable FAULT, when high level occurs on FAULT1 signal
|
||||
mcpwm_fault_init(MCPWM_UNIT_0, MCPWM_HIGH_LEVEL_TGR, MCPWM_SELECT_F2); //Enable FAULT, when high level occurs on FAULT2 signal
|
||||
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_SELECT_F0, MCPWM_FORCE_MCPWMXA_HIGH, MCPWM_FORCE_MCPWMXB_LOW); //Action taken on PWM1A and PWM1B, when FAULT0 occurs
|
||||
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_SELECT_F1, MCPWM_FORCE_MCPWMXA_LOW, MCPWM_FORCE_MCPWMXB_HIGH); //Action taken on PWM1A and PWM1B, when FAULT1 occurs
|
||||
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SELECT_F2, MCPWM_FORCE_MCPWMXA_HIGH, MCPWM_FORCE_MCPWMXB_LOW); //Action taken on PWM0A and PWM0B, when FAULT2 occurs
|
||||
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SELECT_F1, MCPWM_FORCE_MCPWMXA_LOW, MCPWM_FORCE_MCPWMXB_HIGH); //Action taken on PWM0A and PWM0B, when FAULT1 occurs
|
||||
#endif
|
||||
|
||||
#if MCPWM_EN_SYNC
|
||||
//6. Syncronization configuration
|
||||
//comment if you don't want to use sync submodule, also u can comment the sync gpio signals
|
||||
//here synchronization occurs on PWM1A and PWM1B
|
||||
mcpwm_sync_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_SELECT_SYNC0, 200); //Load counter value with 20% of period counter of mcpwm timer 1 when sync 0 occurs
|
||||
#endif
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("Testing MCPWM...\n");
|
||||
xTaskCreate(mcpwm_example_config, "mcpwm_example_config", 4096, NULL, 5, NULL);
|
||||
}
|
@ -3,4 +3,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(mcpwm_basic_config)
|
||||
project(mcpwm_sync_example)
|
@ -3,6 +3,6 @@
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := mcpwm_basic_config
|
||||
PROJECT_NAME := mcpwm_sync_example
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
74
examples/peripherals/mcpwm/mcpwm_sync_example/README.md
Normal file
74
examples/peripherals/mcpwm/mcpwm_sync_example/README.md
Normal file
@ -0,0 +1,74 @@
|
||||
| Supported Targets | ESP32 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- |
|
||||
|
||||
# MCPWM sync Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example aims to show howto sync those timers within the same MCPWM unit to produce fully synchronized output.
|
||||
|
||||
The example will:
|
||||
|
||||
- init MCPWM
|
||||
- sync all three timers with the help of one extra GPIO
|
||||
- mess the synchronized timers by stopping and restarting
|
||||
- sync all three timers with software sync event, no GPIO required. (Only targets with `SOC_MCPWM_SWSYNC_CAN_PROPAGATE` ability can support).
|
||||
- sync all three timers, but adding 10% delay between pulses from different channels
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* An ESP32/ESP32S3 development board
|
||||
|
||||
It is recommended to have an oscilloscope or logic analyzer to verify the output pulse
|
||||
|
||||
Connection :
|
||||
|
||||
| Pin | Func | Mode |
|
||||
| :---------: | :----------------: | :----: |
|
||||
| GPIO_NUM_16 | MCPWM0.Timer0.GenA | Output |
|
||||
| GPIO_NUM_17 | MCPWM0.Timer1.GenA | Output |
|
||||
| GPIO_NUM_18 | MCPWM0.Timer2.GenA | Output |
|
||||
| GPIO_NUM_21 | GPIO_SYNC0 | INPUT |
|
||||
| GPIO_NUM_19 | simulate input | OUTPUT |
|
||||
|
||||
GPIO_NUM_21 and GPIO_NUM_19 **SHOULD** be wired together to provide simulated input.
|
||||
|
||||
Above pin selection can be changed within file `mcpwm_sync_example.c`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (306) sync_example: MCPWM sync example
|
||||
I (306) sync_example: PWM started, not synchronized
|
||||
I (3316) sync_example: Sync timers with GPIO approach
|
||||
I (3326) sync_example: Output should already be synchronized
|
||||
I (6326) sync_example: force synchronous lost
|
||||
I (9326) sync_example: Output should already be synchronized on esp32s3
|
||||
I (12336) sync_example: Each output pulse should be placed with 10 percents of period
|
||||
```
|
||||
|
||||
Overall pulse graph:
|
||||
|
||||
![](readme_res/overall.png)
|
||||
|
||||
Sync:
|
||||
|
||||
![](readme_res/synced.png)
|
||||
|
||||
Sync with phase:
|
||||
|
||||
![](readme_res/sync_phase.png)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mcpwm_sync_example.c"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,158 @@
|
||||
/* MCPWM sync example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This example will show you how to use capture function to read HC-SR04 sonar sensor.
|
||||
*/
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "driver/mcpwm.h"
|
||||
|
||||
const static char *TAG = "sync_example";
|
||||
|
||||
#define TARGET_MCPWM_UNIT MCPWM_UNIT_0
|
||||
#define TIMER0_OUTPUT_GPIO GPIO_NUM_16
|
||||
#define TIMER1_OUTPUT_GPIO GPIO_NUM_17
|
||||
#define TIMER2_OUTPUT_GPIO GPIO_NUM_18
|
||||
#define SIMU_GPIO_SYNC_SOURCE_GPIO GPIO_NUM_21
|
||||
#define SIMU_GPIO_SYNC_SIMULATE_GPIO GPIO_NUM_19
|
||||
|
||||
void app_main(void) {
|
||||
ESP_LOGI(TAG, "MCPWM sync example");
|
||||
|
||||
// init MCPWM: 10% duty cycle on all three timers in MCPWM unit 0 (currently not synchronous)
|
||||
mcpwm_config_t pwm_config = {
|
||||
.frequency = 1000,
|
||||
.cmpr_a = 10,
|
||||
.cmpr_b = 0,
|
||||
.counter_mode = MCPWM_UP_COUNTER,
|
||||
.duty_mode = MCPWM_DUTY_MODE_0,
|
||||
};
|
||||
ESP_ERROR_CHECK(mcpwm_init(TARGET_MCPWM_UNIT, MCPWM_TIMER_0, &pwm_config));
|
||||
ESP_ERROR_CHECK(mcpwm_init(TARGET_MCPWM_UNIT, MCPWM_TIMER_1, &pwm_config));
|
||||
ESP_ERROR_CHECK(mcpwm_init(TARGET_MCPWM_UNIT, MCPWM_TIMER_2, &pwm_config));
|
||||
|
||||
// bind output to GPIO
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0A, TIMER0_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1A, TIMER1_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2A, TIMER2_OUTPUT_GPIO));
|
||||
ESP_LOGI(TAG, "PWM started, not synchronized");
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
// temporarily disable GPIO output, by binding to GenBs which have 0 output
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0B, TIMER0_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1B, TIMER1_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2B, TIMER2_OUTPUT_GPIO));
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
|
||||
ESP_LOGI(TAG, "Sync timers with GPIO approach");
|
||||
// first configure sync source
|
||||
mcpwm_sync_config_t sync_conf = {
|
||||
.sync_sig = MCPWM_SELECT_GPIO_SYNC0,
|
||||
.timer_val = 0,
|
||||
.count_direction = MCPWM_TIMER_DIRECTION_UP,
|
||||
};
|
||||
ESP_ERROR_CHECK(mcpwm_sync_configure(TARGET_MCPWM_UNIT, MCPWM_TIMER_0, &sync_conf));
|
||||
ESP_ERROR_CHECK(mcpwm_sync_configure(TARGET_MCPWM_UNIT, MCPWM_TIMER_1, &sync_conf));
|
||||
ESP_ERROR_CHECK(mcpwm_sync_configure(TARGET_MCPWM_UNIT, MCPWM_TIMER_2, &sync_conf));
|
||||
// then configure GPIO
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM_SYNC_0, SIMU_GPIO_SYNC_SOURCE_GPIO));
|
||||
gpio_config_t io_conf = {};
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.pin_bit_mask = BIT64(SIMU_GPIO_SYNC_SIMULATE_GPIO);
|
||||
io_conf.pull_down_en = 0;
|
||||
io_conf.pull_up_en = 0;
|
||||
ESP_ERROR_CHECK(gpio_config(&io_conf));
|
||||
ESP_ERROR_CHECK(gpio_set_level(SIMU_GPIO_SYNC_SIMULATE_GPIO, 0));
|
||||
ESP_ERROR_CHECK(gpio_set_level(SIMU_GPIO_SYNC_SIMULATE_GPIO, 1));
|
||||
// wait for at least one TEP
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// re-enable GPIO output, to see the result
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0A, TIMER0_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1A, TIMER1_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2A, TIMER2_OUTPUT_GPIO));
|
||||
ESP_LOGI(TAG, "Output should already be synchronized");
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
// stop and restart timers to mess them
|
||||
ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_2));
|
||||
ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_1));
|
||||
ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_0));
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
ESP_ERROR_CHECK(mcpwm_start(TARGET_MCPWM_UNIT, MCPWM_TIMER_0));
|
||||
ESP_ERROR_CHECK(mcpwm_start(TARGET_MCPWM_UNIT, MCPWM_TIMER_1));
|
||||
ESP_ERROR_CHECK(mcpwm_start(TARGET_MCPWM_UNIT, MCPWM_TIMER_2));
|
||||
ESP_LOGI(TAG, "force synchronous lost");
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
// temporarily disable GPIO output, by binding to GenBs which have 0 output
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0B, TIMER0_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1B, TIMER1_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2B, TIMER2_OUTPUT_GPIO));
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
|
||||
#ifdef SOC_MCPWM_SWSYNC_CAN_PROPAGATE
|
||||
// use the trick that only available on esp32s3
|
||||
mcpwm_set_timer_sync_output(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SWSYNC_SOURCE_SYNCIN);
|
||||
sync_conf.sync_sig = MCPWM_SELECT_TIMER0_SYNC;
|
||||
mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_0, &sync_conf);
|
||||
mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_1, &sync_conf);
|
||||
mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_2, &sync_conf);
|
||||
// then send soft sync event to timer0
|
||||
mcpwm_timer_trigger_soft_sync(MCPWM_UNIT_0, MCPWM_TIMER_0);
|
||||
// re-enable GPIO output
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0A, TIMER0_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1A, TIMER1_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2A, TIMER2_OUTPUT_GPIO));
|
||||
ESP_LOGI(TAG, "Output should already be synchronized on esp32s3");
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
#endif
|
||||
|
||||
// temporarily disable GPIO output, by binding to GenBs which have 0 output
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0B, TIMER0_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1B, TIMER1_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2B, TIMER2_OUTPUT_GPIO));
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
// create phase between each timer.
|
||||
// for this case all timers has 10% of period phase between each other
|
||||
sync_conf.sync_sig = MCPWM_SELECT_GPIO_SYNC0;
|
||||
sync_conf.timer_val = 0; // no phase applied
|
||||
mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_0, &sync_conf);
|
||||
sync_conf.timer_val = 900; // fill the counter with 90.0% of period will cause next pulse being delayed 10% period
|
||||
mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_1, &sync_conf);
|
||||
sync_conf.timer_val = 800; // fill the counter with 80.0% of period will cause next pulse being delayed 20% period
|
||||
mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_2, &sync_conf);
|
||||
// trigger positive edge
|
||||
ESP_ERROR_CHECK(gpio_set_level(SIMU_GPIO_SYNC_SIMULATE_GPIO, 0));
|
||||
ESP_ERROR_CHECK(gpio_set_level(SIMU_GPIO_SYNC_SIMULATE_GPIO, 1));
|
||||
// wait for at least one TEP
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// re-enable GPIO output, to see the result
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0A, TIMER0_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1A, TIMER1_OUTPUT_GPIO));
|
||||
ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2A, TIMER2_OUTPUT_GPIO));
|
||||
ESP_LOGI(TAG, "Each output pulse should be placed with 10 percents of period");
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_2));
|
||||
ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_1));
|
||||
ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_0));
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Loading…
x
Reference in New Issue
Block a user