Глобальная переделка. тест сделан по длине импульса и заданной частоте шим, а не меандру
This commit is contained in:
@@ -12,20 +12,20 @@ namespace {
|
||||
constexpr ledc_mode_t PWM_SPEED_MODE = LEDC_LOW_SPEED_MODE;
|
||||
constexpr ledc_timer_t PWM_TIMER = LEDC_TIMER_0;
|
||||
|
||||
void setIntegerDivider(uint16_t divider) {
|
||||
void setDivider(uint32_t dividerRaw) {
|
||||
ledc_dev_t *hardware = LEDC_LL_GET_HW();
|
||||
ledc_ll_timer_pause(hardware, PWM_SPEED_MODE, PWM_TIMER);
|
||||
ledc_ll_set_clock_divider(hardware, PWM_SPEED_MODE, PWM_TIMER,
|
||||
static_cast<uint32_t>(divider) << LEDC_LL_FRACTIONAL_BITS);
|
||||
dividerRaw);
|
||||
ledc_ll_timer_rst(hardware, PWM_SPEED_MODE, PWM_TIMER);
|
||||
ledc_ll_ls_timer_update(hardware, PWM_SPEED_MODE, PWM_TIMER);
|
||||
ledc_ll_timer_resume(hardware, PWM_SPEED_MODE, PWM_TIMER);
|
||||
}
|
||||
|
||||
bool integerDividerIsSet(uint16_t expected) {
|
||||
bool dividerIsSet(uint32_t expectedRaw) {
|
||||
uint32_t rawDivider = 0;
|
||||
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);
|
||||
return rawDivider == expectedRaw;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
mcpwm_timer_handle_t mcpwmTimer = nullptr;
|
||||
@@ -34,6 +34,11 @@ mcpwm_cmpr_handle_t mcpwmComparator = nullptr;
|
||||
mcpwm_gen_handle_t mcpwmGenerator = nullptr;
|
||||
uint32_t mcpwmFrequencyHz = 0;
|
||||
|
||||
constexpr mcpwm_generator_action_t PWM_ACTIVE_ACTION =
|
||||
PWM_ACTIVE_LEVEL == HIGH ? MCPWM_GEN_ACTION_HIGH : MCPWM_GEN_ACTION_LOW;
|
||||
constexpr mcpwm_generator_action_t PWM_INACTIVE_ACTION =
|
||||
PWM_ACTIVE_LEVEL == HIGH ? MCPWM_GEN_ACTION_LOW : MCPWM_GEN_ACTION_HIGH;
|
||||
|
||||
void releaseMcpwm() {
|
||||
if (mcpwmGenerator) {
|
||||
mcpwm_del_generator(mcpwmGenerator);
|
||||
@@ -94,10 +99,10 @@ void PwmGenerator::begin() {
|
||||
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;
|
||||
MCPWM_TIMER_EVENT_EMPTY, PWM_ACTIVE_ACTION)) == 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;
|
||||
mcpwmComparator, PWM_INACTIVE_ACTION)) == ESP_OK;
|
||||
ok = ok && mcpwm_timer_enable(mcpwmTimer) == ESP_OK;
|
||||
if (!ok) {
|
||||
releaseMcpwm();
|
||||
@@ -109,23 +114,29 @@ void PwmGenerator::begin() {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PwmGenerator::start(uint32_t hz, uint8_t dutyPct, ActualPwm &a) {
|
||||
bool PwmGenerator::start(uint32_t hz, uint32_t pulseNs, ActualPwm &a) {
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
IntegerPwmConfig config = {};
|
||||
if (!chooseIntegerPwmConfig(hz, LEDC_SOURCE_CLOCK_HZ, LEDC_MAX_BITS, dutyPct, config)) return false;
|
||||
if (!choosePwmConfig(hz, pulseNs, LEDC_SOURCE_CLOCK_HZ, LEDC_MAX_BITS, config)) return false;
|
||||
const uint8_t bits = config.bits;
|
||||
const uint32_t levels = 1UL << bits;
|
||||
const uint32_t duty = (static_cast<uint64_t>(levels) * dutyPct + 50U) / 100U;
|
||||
const uint32_t duty = config.dutyCount;
|
||||
for (uint8_t attempt = 0; attempt < 2; ++attempt) {
|
||||
stop();
|
||||
const bool attached = ledcAttachChannel(GPIO_PWM, config.actualHz, bits, LEDC_CHANNEL);
|
||||
if (attached) {
|
||||
// Arduino's LEDC API normally chooses an 8-bit fractional divider.
|
||||
// Force the fractional byte to zero so every PWM period contains the
|
||||
// same integer number of 40 MHz source-clock ticks.
|
||||
setIntegerDivider(config.divider);
|
||||
// Native LEDC produces a HIGH pulse. Invert the GPIO matrix output when
|
||||
// the configured active pulse level is LOW.
|
||||
if (!ledcOutputInvert(GPIO_PWM, PWM_ACTIVE_LEVEL == LOW)) {
|
||||
ledcDetach(GPIO_PWM);
|
||||
delay(2);
|
||||
continue;
|
||||
}
|
||||
// Apply the selected Q10.8 divider explicitly. This lets pulse width,
|
||||
// rather than duty percentage, drive the hardware quantization.
|
||||
setDivider(config.dividerRaw);
|
||||
}
|
||||
if (attached && integerDividerIsSet(config.divider) && ledcWriteChannel(LEDC_CHANNEL, duty)) {
|
||||
if (attached && dividerIsSet(config.dividerRaw) && ledcWriteChannel(LEDC_CHANNEL, duty)) {
|
||||
// On the first configuration after power-up the duty update is latched
|
||||
// on a timer edge. Reading immediately can therefore return zero.
|
||||
uint32_t settleUs = static_cast<uint32_t>((2000000ULL + hz - 1U) / hz);
|
||||
@@ -133,7 +144,8 @@ bool PwmGenerator::start(uint32_t hz, uint8_t dutyPct, ActualPwm &a) {
|
||||
delayMicroseconds(settleUs);
|
||||
const uint32_t actualHz = ledcReadFreq(GPIO_PWM);
|
||||
if (actualHz == config.actualHz) {
|
||||
a = {hz, actualHz, 100.0f * duty / levels, bits};
|
||||
a = {hz, actualHz, pulseNs, config.actualPulseNs,
|
||||
100.0f * duty / levels, bits};
|
||||
running_ = true;
|
||||
return true;
|
||||
}
|
||||
@@ -144,11 +156,13 @@ 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 ||
|
||||
if (!mcpwmTimer || !mcpwmComparator || !mcpwmGenerator || !hz || !pulseNs ||
|
||||
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;
|
||||
uint32_t activeTicks = static_cast<uint32_t>(
|
||||
(static_cast<uint64_t>(pulseNs) * MCPWM_RESOLUTION_HZ + 500000000ULL) /
|
||||
1000000000ULL);
|
||||
if (activeTicks == 0U) activeTicks = 1U;
|
||||
if (activeTicks >= periodTicks) activeTicks = periodTicks - 1U;
|
||||
|
||||
@@ -165,7 +179,11 @@ bool PwmGenerator::start(uint32_t hz, uint8_t dutyPct, ActualPwm &a) {
|
||||
return false;
|
||||
}
|
||||
|
||||
a = {hz, hz, 100.0f * activeTicks / periodTicks, periodResolutionBits(periodTicks)};
|
||||
const uint32_t actualPulseNs = static_cast<uint32_t>(
|
||||
(static_cast<uint64_t>(activeTicks) * 1000000000ULL + MCPWM_RESOLUTION_HZ / 2U) /
|
||||
MCPWM_RESOLUTION_HZ);
|
||||
a = {hz, hz, pulseNs, actualPulseNs, 100.0f * activeTicks / periodTicks,
|
||||
periodResolutionBits(periodTicks)};
|
||||
mcpwmFrequencyHz = hz;
|
||||
running_ = true;
|
||||
return true;
|
||||
@@ -189,8 +207,8 @@ void PwmGenerator::stop() {
|
||||
}
|
||||
|
||||
void PwmGenerator::active() {
|
||||
// First detach/stop the PWM peripheral, then select the independently
|
||||
// configured active level. The active and safe levels may be equal.
|
||||
// First detach/stop the PWM peripheral, then apply the same active level
|
||||
// that denotes the pulse during a running test.
|
||||
stop();
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
digitalWrite(GPIO_PWM, PWM_ACTIVE_LEVEL);
|
||||
|
||||
Reference in New Issue
Block a user