работает с перемычкой на 1МГц 2%
This commit is contained in:
@@ -1,6 +1,28 @@
|
||||
#include "Pwm.h"
|
||||
#include "Config.h"
|
||||
#include "Core.h"
|
||||
#include <hal/ledc_ll.h>
|
||||
|
||||
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) {
|
||||
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);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void PwmGenerator::begin() {
|
||||
// Match LEDC_SOURCE_CLOCK_HZ and make the timer calculation deterministic.
|
||||
@@ -10,18 +32,38 @@ void PwmGenerator::begin() {
|
||||
}
|
||||
|
||||
bool PwmGenerator::start(uint32_t hz, uint8_t dutyPct, ActualPwm &a) {
|
||||
stop();
|
||||
const uint8_t bits = choosePwmResolution(hz, LEDC_SOURCE_CLOCK_HZ, LEDC_MAX_BITS);
|
||||
if (!bits) return false;
|
||||
if (!ledcAttachChannel(GPIO_PWM, hz, bits, LEDC_CHANNEL)) return false;
|
||||
IntegerPwmConfig config = {};
|
||||
if (!chooseIntegerPwmConfig(hz, LEDC_SOURCE_CLOCK_HZ, LEDC_MAX_BITS, dutyPct, 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;
|
||||
if (!ledcWriteChannel(LEDC_CHANNEL, duty)) { ledcDetach(GPIO_PWM); return false; }
|
||||
const uint32_t actualHz = ledcReadFreq(GPIO_PWM);
|
||||
if (!actualHz) { ledcDetach(GPIO_PWM); return false; }
|
||||
a = {hz, actualHz, 100.0f * duty / levels, bits};
|
||||
running_ = true;
|
||||
return true;
|
||||
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);
|
||||
}
|
||||
if (attached && integerDividerIsSet(config.divider) && 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);
|
||||
if (settleUs > 2000U) settleUs = 2000U;
|
||||
delayMicroseconds(settleUs);
|
||||
const uint32_t actualHz = ledcReadFreq(GPIO_PWM);
|
||||
if (actualHz) {
|
||||
a = {hz, actualHz, 100.0f * duty / levels, bits};
|
||||
running_ = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (attached) ledcDetach(GPIO_PWM);
|
||||
delay(2);
|
||||
}
|
||||
pinMode(GPIO_PWM, OUTPUT); digitalWrite(GPIO_PWM, PWM_SAFE_LEVEL);
|
||||
return false;
|
||||
}
|
||||
|
||||
void PwmGenerator::stop() {
|
||||
|
||||
Reference in New Issue
Block a user