mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
3f68b61f3e
mcpwm: support non-vararg version of generator functions (v5.0) See merge request espressif/esp-idf!21951
372 lines
15 KiB
C
372 lines
15 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2022-2023 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 oper = NULL;
|
|
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &oper));
|
|
TEST_ESP_OK(mcpwm_operator_connect_timer(oper, timer));
|
|
|
|
mcpwm_generator_config_t generator_config = {
|
|
.gen_gpio_num = 0,
|
|
};
|
|
mcpwm_gen_handle_t generator = NULL;
|
|
TEST_ESP_OK(mcpwm_new_generator(oper, &generator_config, &generator));
|
|
|
|
TEST_ESP_OK(mcpwm_generator_set_action_on_timer_event(generator,
|
|
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_TOGGLE)));
|
|
|
|
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(oper, &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(oper, &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(oper));
|
|
TEST_ESP_OK(mcpwm_del_timer(timer));
|
|
}
|
|
|
|
static bool IRAM_ATTR test_cbc_brake_on_gpio_fault_callback(mcpwm_oper_handle_t oper, const mcpwm_brake_event_data_t *edata, void *user_data)
|
|
{
|
|
esp_rom_printf("cbc brake\r\n");
|
|
return false;
|
|
}
|
|
|
|
static bool IRAM_ATTR test_ost_brake_on_gpio_fault_callback(mcpwm_oper_handle_t oper, 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 oper = NULL;
|
|
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &oper));
|
|
TEST_ESP_OK(mcpwm_operator_connect_timer(oper, 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(oper, &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(oper, &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(oper, &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(oper, &generator_config, &gen_a));
|
|
generator_config.gen_gpio_num = gen_b_gpio;
|
|
TEST_ESP_OK(mcpwm_new_generator(oper, &generator_config, &gen_b));
|
|
|
|
printf("set generator actions on timer event\r\n");
|
|
TEST_ESP_OK(mcpwm_generator_set_action_on_timer_event(gen_a,
|
|
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW)));
|
|
TEST_ESP_OK(mcpwm_generator_set_action_on_timer_event(gen_b,
|
|
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW)));
|
|
|
|
printf("set generator actions on brake event\r\n");
|
|
TEST_ESP_OK(mcpwm_generator_set_action_on_brake_event(gen_a,
|
|
MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_CBC, MCPWM_GEN_ACTION_HIGH)));
|
|
TEST_ESP_OK(mcpwm_generator_set_action_on_brake_event(gen_b,
|
|
MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_OST, MCPWM_GEN_ACTION_HIGH)));
|
|
|
|
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(oper, 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(oper, 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(oper, 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(oper));
|
|
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 oper = NULL;
|
|
TEST_ESP_OK(mcpwm_new_operator(&operator_config, &oper));
|
|
TEST_ESP_OK(mcpwm_operator_connect_timer(oper, 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(oper, &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(oper, &generator_config, &gen_a));
|
|
generator_config.gen_gpio_num = gen_b_gpio;
|
|
TEST_ESP_OK(mcpwm_new_generator(oper, &generator_config, &gen_b));
|
|
|
|
printf("set generator actions on timer event\r\n");
|
|
TEST_ESP_OK(mcpwm_generator_set_action_on_timer_event(gen_a,
|
|
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW)));
|
|
TEST_ESP_OK(mcpwm_generator_set_action_on_timer_event(gen_b,
|
|
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW)));
|
|
|
|
printf("set generator actions on brake event\r\n");
|
|
TEST_ESP_OK(mcpwm_generator_set_action_on_brake_event(gen_a,
|
|
MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_CBC, MCPWM_GEN_ACTION_HIGH)));
|
|
TEST_ESP_OK(mcpwm_generator_set_action_on_brake_event(gen_b,
|
|
MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_OST, MCPWM_GEN_ACTION_HIGH)));
|
|
|
|
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(oper, 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(oper, &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(oper, 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(oper));
|
|
TEST_ESP_OK(mcpwm_del_timer(timer));
|
|
}
|