Пины и ШИМ для S3 (не проверено)
This commit is contained in:
@@ -3,23 +3,43 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
// ------------------------- Hardware configuration -------------------------
|
||||
// Uncomment for the hand-wired prototype. The production PCB assignments
|
||||
// below follow the physical header positions shown in the schematic.
|
||||
// #define MAKETKA
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
constexpr bool TARGET_IS_C3 = true;
|
||||
constexpr uint8_t GPIO_PWM = 3;
|
||||
constexpr uint8_t GPIO_RX = 4;
|
||||
#ifdef MAKETKA
|
||||
constexpr uint8_t GPIO_BUTTON_MODE = 0;
|
||||
constexpr uint8_t GPIO_BUTTON_START = 1;
|
||||
#else
|
||||
constexpr uint8_t GPIO_BUTTON_MODE = 1;
|
||||
constexpr uint8_t GPIO_BUTTON_START = 0;
|
||||
#endif
|
||||
constexpr uint8_t GPIO_SDA = 6;
|
||||
constexpr uint8_t GPIO_SCL = 7;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
constexpr bool TARGET_IS_C3 = false;
|
||||
#ifdef MAKETKA
|
||||
constexpr uint8_t GPIO_PWM = 4;
|
||||
constexpr uint8_t GPIO_RX = 5;
|
||||
constexpr uint8_t GPIO_BUTTON_MODE = 6;
|
||||
constexpr uint8_t GPIO_BUTTON_START = 7;
|
||||
constexpr uint8_t GPIO_BUTTON_MODE = 0;
|
||||
constexpr uint8_t GPIO_BUTTON_START = 1;
|
||||
constexpr uint8_t GPIO_SDA = 8;
|
||||
constexpr uint8_t GPIO_SCL = 9;
|
||||
#else
|
||||
// The S3 SuperMini is fitted so its 5V and GND pins occupy the same PCB
|
||||
// contacts as on the C3 SuperMini. Signals therefore follow header position.
|
||||
constexpr uint8_t GPIO_PWM = 12;
|
||||
constexpr uint8_t GPIO_RX = 13;
|
||||
constexpr uint8_t GPIO_BUTTON_MODE = 10;
|
||||
constexpr uint8_t GPIO_BUTTON_START = 9;
|
||||
constexpr uint8_t GPIO_SDA = 44;
|
||||
constexpr uint8_t GPIO_SCL = 1;
|
||||
#endif
|
||||
#else
|
||||
#error "Only ESP32-C3 and ESP32-S3 are supported"
|
||||
#endif
|
||||
|
||||
@@ -75,18 +95,23 @@ constexpr uint32_t S3_STRICT_MAX_HZ = 1000000;
|
||||
// still fits both levels of the current PWM signal: 20, 40 or 80 MHz.
|
||||
constexpr uint32_t CAPTURE_RESOLUTION_OPTIONS_HZ[] = {20000000, 40000000, 80000000};
|
||||
constexpr uint32_t RMT_MAX_LEVEL_TICKS = 32766;
|
||||
// Arduino-ESP32 uses the 40 MHz crystal as the default LEDC clock on C3/S3.
|
||||
// C3 uses the 40 MHz crystal as the LEDC clock.
|
||||
// Keep this explicit so the resolution calculation never asks LEDC for an
|
||||
// impossible frequency/resolution combination.
|
||||
constexpr uint32_t LEDC_SOURCE_CLOCK_HZ = 40000000;
|
||||
constexpr uint8_t LEDC_CHANNEL = 0;
|
||||
constexpr uint8_t LEDC_MAX_BITS = 14;
|
||||
// S3 uses the dedicated MCPWM peripheral. A 40 MHz timer clock keeps the
|
||||
// longest 1 kHz period within the S3's 16-bit MCPWM counter and makes every
|
||||
// frequency in TEST_FREQUENCIES_HZ exact.
|
||||
constexpr uint32_t MCPWM_RESOLUTION_HZ = 40000000;
|
||||
constexpr uint32_t MCPWM_MAX_PERIOD_TICKS = 65535;
|
||||
|
||||
// -------------------------- Menu value arrays -----------------------------
|
||||
// START and END deliberately have separate, independently cycling menu lists.
|
||||
// Every value is exactly achievable from the 40 MHz XTAL with an integer LEDC
|
||||
// divider. The test itself walks TEST_FREQUENCIES_HZ between the selected
|
||||
// endpoints, so there is no separately configurable step.
|
||||
// Every value is exactly achievable from a 40 MHz timer clock. The test walks
|
||||
// TEST_FREQUENCIES_HZ between the selected endpoints, so there is no
|
||||
// separately configurable step.
|
||||
constexpr uint32_t START_FREQ_OPTIONS_HZ[] = {1000, 10000, 100000};
|
||||
constexpr uint32_t END_FREQ_OPTIONS_HZ[] = {100000, 500000, 1000000};
|
||||
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#include "Pwm.h"
|
||||
#include "Config.h"
|
||||
#include "Core.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
#include <hal/ledc_ll.h>
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include <driver/mcpwm_prelude.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
constexpr ledc_mode_t PWM_SPEED_MODE = LEDC_LOW_SPEED_MODE;
|
||||
constexpr ledc_timer_t PWM_TIMER = LEDC_TIMER_0;
|
||||
|
||||
@@ -22,16 +27,90 @@ bool integerDividerIsSet(uint16_t expected) {
|
||||
ledc_ll_get_clock_divider(LEDC_LL_GET_HW(), PWM_SPEED_MODE, PWM_TIMER, &rawDivider);
|
||||
return rawDivider == (static_cast<uint32_t>(expected) << LEDC_LL_FRACTIONAL_BITS);
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
mcpwm_timer_handle_t mcpwmTimer = nullptr;
|
||||
mcpwm_oper_handle_t mcpwmOperator = nullptr;
|
||||
mcpwm_cmpr_handle_t mcpwmComparator = nullptr;
|
||||
mcpwm_gen_handle_t mcpwmGenerator = nullptr;
|
||||
uint32_t mcpwmFrequencyHz = 0;
|
||||
|
||||
void releaseMcpwm() {
|
||||
if (mcpwmGenerator) {
|
||||
mcpwm_del_generator(mcpwmGenerator);
|
||||
mcpwmGenerator = nullptr;
|
||||
}
|
||||
if (mcpwmComparator) {
|
||||
mcpwm_del_comparator(mcpwmComparator);
|
||||
mcpwmComparator = nullptr;
|
||||
}
|
||||
if (mcpwmOperator) {
|
||||
mcpwm_del_operator(mcpwmOperator);
|
||||
mcpwmOperator = nullptr;
|
||||
}
|
||||
if (mcpwmTimer) {
|
||||
mcpwm_timer_disable(mcpwmTimer);
|
||||
mcpwm_del_timer(mcpwmTimer);
|
||||
mcpwmTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t periodResolutionBits(uint32_t periodTicks) {
|
||||
uint8_t bits = 0;
|
||||
while (periodTicks > 1U) {
|
||||
periodTicks >>= 1U;
|
||||
++bits;
|
||||
}
|
||||
return bits ? bits : 1U;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PwmGenerator::begin() {
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
// Match LEDC_SOURCE_CLOCK_HZ and make the timer calculation deterministic.
|
||||
ledcSetClockSource(LEDC_USE_XTAL_CLK);
|
||||
pinMode(GPIO_PWM, OUTPUT);
|
||||
stop();
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
mcpwm_timer_config_t timerConfig = {};
|
||||
timerConfig.group_id = 0;
|
||||
timerConfig.clk_src = MCPWM_TIMER_CLK_SRC_PLL160M;
|
||||
timerConfig.resolution_hz = MCPWM_RESOLUTION_HZ;
|
||||
timerConfig.count_mode = MCPWM_TIMER_COUNT_MODE_UP;
|
||||
timerConfig.period_ticks = MCPWM_RESOLUTION_HZ / 1000U;
|
||||
|
||||
mcpwm_operator_config_t operatorConfig = {};
|
||||
operatorConfig.group_id = 0;
|
||||
mcpwm_comparator_config_t comparatorConfig = {};
|
||||
mcpwm_generator_config_t generatorConfig = {};
|
||||
generatorConfig.gen_gpio_num = GPIO_PWM;
|
||||
|
||||
bool ok = mcpwm_new_timer(&timerConfig, &mcpwmTimer) == ESP_OK;
|
||||
ok = ok && mcpwm_new_operator(&operatorConfig, &mcpwmOperator) == ESP_OK;
|
||||
ok = ok && mcpwm_operator_connect_timer(mcpwmOperator, mcpwmTimer) == ESP_OK;
|
||||
ok = ok && mcpwm_new_comparator(mcpwmOperator, &comparatorConfig, &mcpwmComparator) == ESP_OK;
|
||||
ok = ok && mcpwm_new_generator(mcpwmOperator, &generatorConfig, &mcpwmGenerator) == ESP_OK;
|
||||
ok = ok && mcpwm_comparator_set_compare_value(mcpwmComparator,
|
||||
timerConfig.period_ticks / 2U) == ESP_OK;
|
||||
ok = ok && mcpwm_generator_set_action_on_timer_event(mcpwmGenerator,
|
||||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP,
|
||||
MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH)) == ESP_OK;
|
||||
ok = ok && mcpwm_generator_set_action_on_compare_event(mcpwmGenerator,
|
||||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP,
|
||||
mcpwmComparator, MCPWM_GEN_ACTION_LOW)) == ESP_OK;
|
||||
ok = ok && mcpwm_timer_enable(mcpwmTimer) == ESP_OK;
|
||||
if (!ok) {
|
||||
releaseMcpwm();
|
||||
pinMode(GPIO_PWM, OUTPUT);
|
||||
digitalWrite(GPIO_PWM, PWM_SAFE_LEVEL);
|
||||
return;
|
||||
}
|
||||
mcpwm_generator_set_force_level(mcpwmGenerator, PWM_SAFE_LEVEL, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PwmGenerator::start(uint32_t hz, uint8_t dutyPct, ActualPwm &a) {
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
IntegerPwmConfig config = {};
|
||||
if (!chooseIntegerPwmConfig(hz, LEDC_SOURCE_CLOCK_HZ, LEDC_MAX_BITS, dutyPct, config)) return false;
|
||||
const uint8_t bits = config.bits;
|
||||
@@ -64,10 +143,44 @@ bool PwmGenerator::start(uint32_t hz, uint8_t dutyPct, ActualPwm &a) {
|
||||
}
|
||||
pinMode(GPIO_PWM, OUTPUT); digitalWrite(GPIO_PWM, PWM_SAFE_LEVEL);
|
||||
return false;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
if (!mcpwmTimer || !mcpwmComparator || !mcpwmGenerator || !hz || dutyPct > 100U ||
|
||||
MCPWM_RESOLUTION_HZ % hz) return false;
|
||||
const uint32_t periodTicks = MCPWM_RESOLUTION_HZ / hz;
|
||||
if (periodTicks < 2U || periodTicks > MCPWM_MAX_PERIOD_TICKS) return false;
|
||||
uint32_t activeTicks = (static_cast<uint64_t>(periodTicks) * dutyPct + 50U) / 100U;
|
||||
if (activeTicks == 0U) activeTicks = 1U;
|
||||
if (activeTicks >= periodTicks) activeTicks = periodTicks - 1U;
|
||||
|
||||
stop();
|
||||
bool ok = mcpwm_timer_set_period(mcpwmTimer, periodTicks) == ESP_OK;
|
||||
ok = ok && mcpwm_comparator_set_compare_value(mcpwmComparator, activeTicks) == ESP_OK;
|
||||
ok = ok && mcpwm_generator_set_force_level(mcpwmGenerator, -1, false) == ESP_OK;
|
||||
ok = ok && mcpwm_timer_start_stop(mcpwmTimer, MCPWM_TIMER_START_NO_STOP) == ESP_OK;
|
||||
if (!ok) {
|
||||
mcpwm_generator_set_force_level(mcpwmGenerator, PWM_SAFE_LEVEL, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
a = {hz, hz, 100.0f * activeTicks / periodTicks, periodResolutionBits(periodTicks)};
|
||||
mcpwmFrequencyHz = hz;
|
||||
running_ = true;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PwmGenerator::stop() {
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
if (running_) ledcDetach(GPIO_PWM);
|
||||
pinMode(GPIO_PWM, OUTPUT); digitalWrite(GPIO_PWM, PWM_SAFE_LEVEL);
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
if (mcpwmGenerator) mcpwm_generator_set_force_level(mcpwmGenerator, PWM_SAFE_LEVEL, true);
|
||||
if (running_ && mcpwmTimer) {
|
||||
mcpwm_timer_start_stop(mcpwmTimer, MCPWM_TIMER_STOP_EMPTY);
|
||||
const uint32_t waitUs = mcpwmFrequencyHz ? (1000000U / mcpwmFrequencyHz + 2U) : 2U;
|
||||
delayMicroseconds(waitUs);
|
||||
}
|
||||
mcpwmFrequencyHz = 0;
|
||||
#endif
|
||||
running_ = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user