Esp32 improve PWM inverted

This commit is contained in:
Stephan Hadinger 2022-03-08 22:49:12 +01:00
parent 3c1f30d3be
commit a7577cfefc
4 changed files with 48 additions and 30 deletions

View File

@ -97,16 +97,33 @@ void analogWriteFreq(uint32_t freq) {
_analogWriteFreqRange();
}
int32_t analogAttach(uint32_t pin) { // returns ledc channel used, or -1 if failed
int32_t analogAttach(uint32_t pin, bool output_invert) { // returns ledc channel used, or -1 if failed
_analogInit(); // make sure the mapping array is initialized
// Find if pin is already attached
int32_t channel = _analog_pin2chan(pin);
if (channel >= 0) { return channel; }
int32_t chan = _analog_pin2chan(pin);
if (chan >= 0) { return chan; }
// Find an empty channel
for (channel = 0; channel < MAX_PWMS; channel++) {
if (255 == pwm_channel[channel]) {
pwm_channel[channel] = pin;
ledcAttachPin(pin, channel);
for (chan = 0; chan < MAX_PWMS; chan++) {
if (255 == pwm_channel[chan]) {
pwm_channel[chan] = pin;
// ledcAttachPin(pin, channel); -- replicating here because we want the default duty
uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4);
// AddLog(LOG_LEVEL_INFO, "PWM: ledc_channel pin=%i out_invert=%i", pin, output_invert);
ledc_channel_config_t ledc_channel = {
(int)pin, // gpio
(ledc_mode_t)group, // speed-mode
(ledc_channel_t)channel, // channel
(ledc_intr_type_t)LEDC_INTR_DISABLE, // intr_type
(ledc_timer_t)timer, // timer_sel
0, // duty
0, // hpoint
{ output_invert ? 1u : 0u },// output_invert
};
ledc_channel_config(&ledc_channel);
ledcSetup(channel, pwm_frequency, pwm_bit_num);
// Serial.printf("PWM: New attach pin %d to channel %d\n", pin, channel);
return channel;
@ -153,6 +170,7 @@ void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase)
chan = analogAttach(pin);
if (chan < 0) { return; } // failed
}
// AddLog(LOG_LEVEL_INFO, "PWM: analogWritePhase pin=%i chan=%i duty=%03X phase=%03X", pin, chan, duty, phase);
if (duty >> (pwm_bit_num-1) ) ++duty; // input is 0..1023 but PWM takes 0..1024 - so we skip at mid-range. It creates a small non-linearity
if (phase >> (pwm_bit_num-1) ) ++phase;
@ -163,8 +181,10 @@ void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase)
uint32_t max_duty = (1 << channels_resolution[chan]) - 1;
phase = phase & max_duty;
ledc_set_duty_with_hpoint((ledc_mode_t)group, (ledc_channel_t)channel, duty, phase);
ledc_update_duty((ledc_mode_t)group, (ledc_channel_t)channel);
esp_err_t err1, err2;
err1 = ledc_set_duty_with_hpoint((ledc_mode_t)group, (ledc_channel_t)channel, duty, phase);
err2 = ledc_update_duty((ledc_mode_t)group, (ledc_channel_t)channel);
// AddLog(LOG_LEVEL_INFO, "PWM: err1=%i err2=%i", err1, err2);
}
#endif // ESP32

View File

@ -27,7 +27,7 @@
// input range is in full range, ledc needs bits
void analogWriteRange(uint32_t range);
void analogWriteFreq(uint32_t freq);
int32_t analogAttach(uint32_t pin); // returns the ledc channel, or -1 if failed. This is implicitly called by analogWrite if the channel was not already allocated
int32_t analogAttach(uint32_t pin, bool output_invert = false); // returns the ledc channel, or -1 if failed. This is implicitly called by analogWrite if the channel was not already allocated
void analogWrite(uint8_t pin, int val);
// Extended version that also allows to change phase

View File

@ -64,7 +64,8 @@ void PwmSaveToSettings(void) {
// or `-1` if no change.
// Auto-phasing is recomputed, and changes are applied to GPIO if there is a physical GPIO configured and an actual change needed
//
void PwmApplyGPIO(void) {
// force_update_all: force applying the PWM values even if the value didn't change (necessary at initialization)
void PwmApplyGPIO(bool force_update_all) {
uint32_t pwm_phase_accumulator = 0; // dephase each PWM channel with the value of the previous
for (uint32_t i = 0; i < MAX_PWMS; i++) {
@ -73,9 +74,6 @@ void PwmApplyGPIO(void) {
if (TasmotaGlobal.pwm_value[i] >= 0) { pwm_val = TasmotaGlobal.pwm_value[i]; } // new value explicitly specified
if (pwm_val > Settings->pwm_range) { pwm_val = Settings->pwm_range; } // prevent overflow
// gpio_val : actual value of GPIO, taking into account inversion
uint32_t gpio_val = bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - pwm_val : pwm_val;
// compute phase
uint32_t pwm_phase = TasmotaGlobal.pwm_cur_phase[i]; // pwm_phase is the logical phase of the active pulse, ignoring inverted
uint32_t gpio_phase = pwm_phase; // gpio is the physical phase taking into account inverted
@ -86,17 +84,17 @@ void PwmApplyGPIO(void) {
} else {
// compute auto-phase
pwm_phase = pwm_phase_accumulator;
uint32_t pwm_phase_invert = bitRead(TasmotaGlobal.pwm_inverted, i) ? pwm_val : 0; // move phase if inverted
gpio_phase = (pwm_phase + pwm_phase_invert) & Settings->pwm_range;
// accumulate phase for next GPIO
pwm_phase_accumulator = (pwm_phase + pwm_val) & Settings->pwm_range;
}
// apply new values to GPIO if GPIO is set
// AddLog(LOG_LEVEL_INFO, "PWM: i=%i used=%i pwm_val=%03X vs %03X pwm_phase=%03X vs %03X", i, PinUsed(GPIO_PWM1, i), pwm_val, TasmotaGlobal.pwm_cur_value[i], pwm_phase, TasmotaGlobal.pwm_cur_phase[i]);
if (PinUsed(GPIO_PWM1, i)) {
if ((pwm_val != TasmotaGlobal.pwm_cur_value[i]) || (pwm_phase != TasmotaGlobal.pwm_cur_phase[i])) {
if (force_update_all || (pwm_val != TasmotaGlobal.pwm_cur_value[i]) || (pwm_phase != TasmotaGlobal.pwm_cur_phase[i])) {
// GPIO has PWM and there is a chnage to apply, apply it
analogWritePhase(Pin(GPIO_PWM1, i), gpio_val, gpio_phase);
analogWritePhase(Pin(GPIO_PWM1, i), pwm_val, pwm_phase);
// AddLog(LOG_LEVEL_INFO, "PWM: analogWritePhase i=%i val=%03X phase=%03X", i, pwm_val, pwm_phase);
}
}
@ -104,13 +102,13 @@ void PwmApplyGPIO(void) {
TasmotaGlobal.pwm_cur_value[i] = pwm_val;
TasmotaGlobal.pwm_cur_phase[i] = pwm_phase;
}
// AddLog(LOG_LEVEL_INFO, "PWM: Val=%03X-%03X-%03X-%03X-%03X Phase=%03X-%03X-%03X-%03X-%03X Range=%03X",
// TasmotaGlobal.pwm_cur_value[0], TasmotaGlobal.pwm_cur_value[1], TasmotaGlobal.pwm_cur_value[2], TasmotaGlobal.pwm_cur_value[3],
// TasmotaGlobal.pwm_cur_value[4],
// TasmotaGlobal.pwm_cur_phase[0], TasmotaGlobal.pwm_cur_phase[1], TasmotaGlobal.pwm_cur_phase[2], TasmotaGlobal.pwm_cur_phase[3],
// TasmotaGlobal.pwm_cur_phase[4],
// Settings->pwm_range
// );
AddLog(LOG_LEVEL_INFO, "PWM: Val=%03X-%03X-%03X-%03X-%03X Phase=%03X-%03X-%03X-%03X-%03X Range=%03X",
TasmotaGlobal.pwm_cur_value[0], TasmotaGlobal.pwm_cur_value[1], TasmotaGlobal.pwm_cur_value[2], TasmotaGlobal.pwm_cur_value[3],
TasmotaGlobal.pwm_cur_value[4],
TasmotaGlobal.pwm_cur_phase[0], TasmotaGlobal.pwm_cur_phase[1], TasmotaGlobal.pwm_cur_phase[2], TasmotaGlobal.pwm_cur_phase[3],
TasmotaGlobal.pwm_cur_phase[4],
Settings->pwm_range
);
PwmSaveToSettings(); // copy to Settings
PwmRearmChanges(); // reset expected changes
}
@ -120,7 +118,7 @@ void CmndPwm(void)
if (TasmotaGlobal.pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings->pwm_range) && PinUsed(GPIO_PWM1, XdrvMailbox.index -1)) {
TasmotaGlobal.pwm_value[XdrvMailbox.index - 1] = XdrvMailbox.payload;
PwmApplyGPIO();
PwmApplyGPIO(false);
}
Response_P(PSTR("{"));
MqttShowPWMState(); // Render the PWM status to MQTT
@ -133,7 +131,7 @@ void GpioInitPwm(void) {
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
analogAttach(Pin(GPIO_PWM1, i));
analogAttach(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i));
if (i < TasmotaGlobal.light_type) {
// force PWM GPIOs to black
TasmotaGlobal.pwm_value[i] = 0;
@ -147,7 +145,7 @@ void GpioInitPwm(void) {
}
}
}
PwmApplyGPIO(); // apply all changes
PwmApplyGPIO(true); // apply all changes
}
/********************************************************************************************/
@ -157,7 +155,7 @@ void ResetPwm(void)
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
TasmotaGlobal.pwm_value[i] = 0;
}
PwmApplyGPIO();
PwmApplyGPIO(true);
}
#else // now for ESP8266

View File

@ -2163,7 +2163,7 @@ void LightSetOutputs(const uint16_t *cur_col_10) {
}
}
#ifdef ESP32
PwmApplyGPIO();
PwmApplyGPIO(false);
#endif // ESP32
#ifdef USE_PWM_DIMMER