Merge pull request #14628 from s-hadinger/pwm_multi

Increase PWM channels to 16 (Esp32 only)
This commit is contained in:
Theo Arends 2022-01-28 09:13:25 +01:00 committed by GitHub
commit 1033bdf7df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 450 additions and 288 deletions

View File

@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file.
- Command ``SspmScan`` to rescan Sonoff SPM modbus - Command ``SspmScan`` to rescan Sonoff SPM modbus
- Support for MQ analog sensor for air quality by Francesco Adriani (#14581) - Support for MQ analog sensor for air quality by Francesco Adriani (#14581)
- Command ``SetOption134 1`` to disable PWM auto-phasing for lights by default (new behavior) (#14590) - Command ``SetOption134 1`` to disable PWM auto-phasing for lights by default (new behavior) (#14590)
- Increase PWM channels to 16 (Esp32 only)
### Changed ### Changed
- BME68x-Sensor-API library from v3.5.9 to v4.4.7 - BME68x-Sensor-API library from v3.5.9 to v4.4.7

View File

@ -18,6 +18,7 @@ const uint16_t ST7789_colors[]={ST7789_BLACK,ST7789_WHITE,ST7789_RED,ST7789_GREE
ST7789_LIGHTGREY,ST7789_DARKGREY,ST7789_ORANGE,ST7789_GREENYELLOW,ST7789_PINK}; ST7789_LIGHTGREY,ST7789_DARKGREY,ST7789_ORANGE,ST7789_GREENYELLOW,ST7789_PINK};
#ifdef ESP32 #ifdef ESP32
#include "esp8266toEsp32.h"
#define ST7789_DIMMER #define ST7789_DIMMER
#endif #endif
@ -258,11 +259,9 @@ void Arduino_ST7789::commonInit(const uint8_t *cmdList) {
} }
if (_bp>=0) { if (_bp>=0) {
#define ESP32_PWM_CHANNEL 1 // #define ESP32_PWM_CHANNEL 1
#ifdef ST7789_DIMMER #ifdef ST7789_DIMMER
ledcSetup(ESP32_PWM_CHANNEL,4000,8); analogWrite(_bp, 128);
ledcAttachPin(_bp,ESP32_PWM_CHANNEL);
ledcWrite(ESP32_PWM_CHANNEL,128);
#else #else
pinMode(_bp, OUTPUT); pinMode(_bp, OUTPUT);
#endif #endif
@ -575,7 +574,8 @@ void Arduino_ST7789::DisplayOnff(int8_t on) {
writecommand(ST7789_DISPON); //Display on writecommand(ST7789_DISPON); //Display on
if (_bp>=0) { if (_bp>=0) {
#ifdef ST7789_DIMMER #ifdef ST7789_DIMMER
ledcWrite(ESP32_PWM_CHANNEL,dimmer); analogWrite(_bp, dimmer);
// ledcWrite(ESP32_PWM_CHANNEL,dimmer);
#else #else
digitalWrite(_bp,HIGH); digitalWrite(_bp,HIGH);
#endif #endif
@ -584,7 +584,8 @@ void Arduino_ST7789::DisplayOnff(int8_t on) {
writecommand(ST7789_DISPOFF); writecommand(ST7789_DISPOFF);
if (_bp>=0) { if (_bp>=0) {
#ifdef ST7789_DIMMER #ifdef ST7789_DIMMER
ledcWrite(ESP32_PWM_CHANNEL,0); analogWrite(_bp, 0);
// ledcWrite(ESP32_PWM_CHANNEL,0);
#else #else
digitalWrite(_bp,LOW); digitalWrite(_bp,LOW);
#endif #endif
@ -598,7 +599,8 @@ void Arduino_ST7789::dim(uint8_t dim) {
if (dimmer>15) dimmer=15; if (dimmer>15) dimmer=15;
dimmer=((float)dimmer/15.0)*255.0; dimmer=((float)dimmer/15.0)*255.0;
#ifdef ESP32 #ifdef ESP32
ledcWrite(ESP32_PWM_CHANNEL,dimmer); analogWrite(_bp, dimmer);
// ledcWrite(ESP32_PWM_CHANNEL,dimmer);
#endif #endif
} }

View File

@ -59,10 +59,10 @@ uint16_t Renderer::GetColorFromIndex(uint8_t index) {
void Renderer::dim(uint8_t contrast) { void Renderer::dim(uint8_t contrast) {
uint8_t contrast8 = ((uint32_t)contrast * 255) / 15; uint8_t contrast8 = ((uint32_t)contrast * 255) / 15;
dim8(contrast8, contrast8); dim10(contrast8, contrast8 * 4);
} }
void Renderer::dim8(uint8_t contrast, uint8_t contrast_gamma) { void Renderer::dim10(uint8_t contrast, uint16_t contrast_gamma) {
} }

View File

@ -72,7 +72,7 @@ public:
virtual void Begin(int16_t p1,int16_t p2,int16_t p3); virtual void Begin(int16_t p1,int16_t p2,int16_t p3);
virtual void Updateframe(); virtual void Updateframe();
virtual void dim(uint8_t contrast); // input has range 0..15 virtual void dim(uint8_t contrast); // input has range 0..15
virtual void dim8(uint8_t contrast, uint8_t contrast_gamma); // input has range 0..255, second arg has gamma correction for PWM virtual void dim10(uint8_t contrast, uint16_t contrast_gamma); // input has range 0..255, second arg has gamma correction for PWM with 10 bits resolution
virtual void pushColors(uint16_t *data, uint16_t len, boolean first); virtual void pushColors(uint16_t *data, uint16_t len, boolean first);
virtual void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); virtual void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
virtual void invertDisplay(boolean i); virtual void invertDisplay(boolean i);

View File

@ -53,6 +53,7 @@
// ESP32 uses 2. SPI BUS, ESP8266 uses software spi // ESP32 uses 2. SPI BUS, ESP8266 uses software spi
#ifdef ESP32 #ifdef ESP32
#include "esp8266toEsp32.h"
#undef ILI9341_2_DIMMER #undef ILI9341_2_DIMMER
#define ILI9341_2_DIMMER #define ILI9341_2_DIMMER
#undef ESP32_PWM_CHANNEL #undef ESP32_PWM_CHANNEL
@ -248,9 +249,7 @@ void ILI9341_2::init(uint16_t width, uint16_t height) {
if (_bp >= 0) { if (_bp >= 0) {
#ifdef ILI9341_2_DIMMER #ifdef ILI9341_2_DIMMER
ledcSetup(ESP32_PWM_CHANNEL, 4000, 8); analogWrite(_bp, 511);
ledcAttachPin(_bp, ESP32_PWM_CHANNEL);
ledcWrite(ESP32_PWM_CHANNEL, 128);
#else #else
pinMode(_bp, OUTPUT); pinMode(_bp, OUTPUT);
#endif #endif
@ -544,7 +543,8 @@ void ILI9341_2::DisplayOnff(int8_t on) {
SPI_END_TRANSACTION(); SPI_END_TRANSACTION();
if (_bp >= 0) { if (_bp >= 0) {
#ifdef ILI9341_2_DIMMER #ifdef ILI9341_2_DIMMER
ledcWrite(ESP32_PWM_CHANNEL, dimmer); analogWrite(_bp, dimmer * 4);
// ledcWrite(ESP32_PWM_CHANNEL, dimmer);
#else #else
digitalWrite(_bp, HIGH); digitalWrite(_bp, HIGH);
#endif #endif
@ -557,7 +557,8 @@ void ILI9341_2::DisplayOnff(int8_t on) {
SPI_END_TRANSACTION(); SPI_END_TRANSACTION();
if (_bp >= 0) { if (_bp >= 0) {
#ifdef ILI9341_2_DIMMER #ifdef ILI9341_2_DIMMER
ledcWrite(ESP32_PWM_CHANNEL, 0); analogWrite(_bp, 0);
// ledcWrite(ESP32_PWM_CHANNEL, 0);
#else #else
digitalWrite(_bp, LOW); digitalWrite(_bp, LOW);
#endif #endif
@ -604,7 +605,8 @@ void ILI9341_2::dim(uint8_t dim) {
dimmer=((float)dimmer/15.0)*255.0; dimmer=((float)dimmer/15.0)*255.0;
#ifdef ESP32 #ifdef ESP32
if (_bp>=0) { if (_bp>=0) {
ledcWrite(ESP32_PWM_CHANNEL,dimmer); analogWrite(_bp, dimmer * 4);
// ledcWrite(ESP32_PWM_CHANNEL,dimmer);
} else { } else {
if (_hwspi>=2) { if (_hwspi>=2) {
//ili9342_dimm(dim); //ili9342_dimm(dim);

View File

@ -20,6 +20,10 @@
#include <Arduino.h> #include <Arduino.h>
#include "uDisplay.h" #include "uDisplay.h"
#ifdef ESP32
#include "esp8266toEsp32.h"
#endif
// #define UDSP_DEBUG // #define UDSP_DEBUG
const uint16_t udisp_colors[]={UDISP_BLACK,UDISP_WHITE,UDISP_RED,UDISP_GREEN,UDISP_BLUE,UDISP_CYAN,UDISP_MAGENTA,\ const uint16_t udisp_colors[]={UDISP_BLACK,UDISP_WHITE,UDISP_RED,UDISP_GREEN,UDISP_BLUE,UDISP_CYAN,UDISP_MAGENTA,\
@ -428,9 +432,7 @@ Renderer *uDisplay::Init(void) {
if (bpanel >= 0) { if (bpanel >= 0) {
#ifdef ESP32 #ifdef ESP32
ledcSetup(ESP32_PWM_CHANNEL, 977, 8); // use 10 bits resolution like in Light analogWrite(bpanel, 32);
ledcAttachPin(bpanel, ESP32_PWM_CHANNEL);
ledcWrite(ESP32_PWM_CHANNEL, 8); // 38/255 correspond roughly to 50% visual brighness (with Gamma)
#else #else
pinMode(bpanel, OUTPUT); pinMode(bpanel, OUTPUT);
digitalWrite(bpanel, HIGH); digitalWrite(bpanel, HIGH);
@ -1353,7 +1355,8 @@ void uDisplay::DisplayOnff(int8_t on) {
if (dsp_on != 0xff) spi_command_one(dsp_on); if (dsp_on != 0xff) spi_command_one(dsp_on);
if (bpanel >= 0) { if (bpanel >= 0) {
#ifdef ESP32 #ifdef ESP32
ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma); analogWrite(bpanel, dimmer10_gamma);
// ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma);
#else #else
digitalWrite(bpanel, HIGH); digitalWrite(bpanel, HIGH);
#endif #endif
@ -1363,7 +1366,8 @@ void uDisplay::DisplayOnff(int8_t on) {
if (dsp_off != 0xff) spi_command_one(dsp_off); if (dsp_off != 0xff) spi_command_one(dsp_off);
if (bpanel >= 0) { if (bpanel >= 0) {
#ifdef ESP32 #ifdef ESP32
ledcWrite(ESP32_PWM_CHANNEL, 0); analogWrite(bpanel, 0);
// ledcWrite(ESP32_PWM_CHANNEL, 0);
#else #else
digitalWrite(bpanel, LOW); digitalWrite(bpanel, LOW);
#endif #endif
@ -1402,16 +1406,17 @@ void udisp_dimm(uint8_t dim);
// } // }
// dim is 0..255 // dim is 0..255
void uDisplay::dim8(uint8_t dim, uint8_t dim_gamma) { // dimmer with 8 bits resolution, 0..255. Gamma correction must be done by caller void uDisplay::dim10(uint8_t dim, uint16_t dim_gamma) { // dimmer with 8 bits resolution, 0..255. Gamma correction must be done by caller
dimmer8 = dim; dimmer8 = dim;
dimmer8_gamma = dim_gamma; dimmer10_gamma = dim_gamma;
if (ep_mode) { if (ep_mode) {
return; return;
} }
#ifdef ESP32 // TODO should we also add a ESP8266 version for bpanel? #ifdef ESP32 // TODO should we also add a ESP8266 version for bpanel?
if (bpanel >= 0) { // is the BaclPanel GPIO configured if (bpanel >= 0) { // is the BaclPanel GPIO configured
ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma); analogWrite(bpanel, dimmer10_gamma);
// ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma);
} else if (dim_cbp) { } else if (dim_cbp) {
dim_cbp(dim); dim_cbp(dim);
} }

View File

@ -74,9 +74,6 @@ enum uColorType { uCOLOR_BW, uCOLOR_COLOR };
#define SPI_DC_LOW if (spi_dc >= 0) GPIO_CLR_SLOW(spi_dc); #define SPI_DC_LOW if (spi_dc >= 0) GPIO_CLR_SLOW(spi_dc);
#define SPI_DC_HIGH if (spi_dc >= 0) GPIO_SET_SLOW(spi_dc); #define SPI_DC_HIGH if (spi_dc >= 0) GPIO_SET_SLOW(spi_dc);
#define ESP32_PWM_CHANNEL 1
#define LUTMAXSIZE 64 #define LUTMAXSIZE 64
class uDisplay : public Renderer { class uDisplay : public Renderer {
@ -93,7 +90,7 @@ class uDisplay : public Renderer {
uint16_t bgcol(void); uint16_t bgcol(void);
int8_t color_type(void); int8_t color_type(void);
// void dim(uint8_t dim); // original version with 4 bits resolution 0..15 // void dim(uint8_t dim); // original version with 4 bits resolution 0..15
virtual void dim8(uint8_t dim, uint8_t dim_gamma); // dimmer with 8 bits resolution, 0..255. Gamma correction must be done by caller virtual void dim10(uint8_t dim, uint16_t dim_gamma); // dimmer with 8 bits resolution, 0..255. Gamma correction must be done by caller with 10 bits resolution
uint16_t GetColorFromIndex(uint8_t index); uint16_t GetColorFromIndex(uint8_t index);
void setRotation(uint8_t m); void setRotation(uint8_t m);
void fillScreen(uint16_t color); void fillScreen(uint16_t color);
@ -186,7 +183,7 @@ class uDisplay : public Renderer {
int8_t bpanel; // backbanel GPIO, -1 if none int8_t bpanel; // backbanel GPIO, -1 if none
int8_t spi_miso; int8_t spi_miso;
uint8_t dimmer8; // 8 bits resolution, 0..255 uint8_t dimmer8; // 8 bits resolution, 0..255
uint8_t dimmer8_gamma; // 8 bits resolution, 0..255, gamma corrected uint16_t dimmer10_gamma; // 10 bits resolution, 0..1023, gamma corrected
SPIClass *uspi; SPIClass *uspi;
uint8_t sspi; uint8_t sspi;
SPISettings spiSettings; SPISettings spiSettings;

View File

@ -50,7 +50,9 @@ extern "C"
#include <lwip/inet.h> #include <lwip/inet.h>
#include <lwip/netif.h> #include <lwip/netif.h>
#include <include/ClientContext.h> #include <include/ClientContext.h>
#ifdef ESP8266
#include <c_types.h> #include <c_types.h>
#endif
#include <coredecls.h> #include <coredecls.h>
#if !CORE_MOCK #if !CORE_MOCK

View File

@ -43,8 +43,8 @@
#include "lwip/netif.h" #include "lwip/netif.h"
#ifdef ESP8266 #ifdef ESP8266
#include <include/ClientContext.h> #include <include/ClientContext.h>
#endif
#include "c_types.h" #include "c_types.h"
#endif
#include <core_version.h> #include <core_version.h>
#undef DEBUG_TLS #undef DEBUG_TLS

View File

@ -1,6 +0,0 @@
#pragma once
/**/
#include <stdint.h>
#ifndef ICACHE_FLASH_ATTR
#define ICACHE_FLASH_ATTR
#endif

View File

@ -1,3 +0,0 @@
//
// Compat with ESP32
//

View File

@ -13,58 +13,64 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//
#ifdef ESP32
#include "Arduino.h" #include "Arduino.h"
//#include "lwip/apps/sntp.h"
#include <nvs.h>
// See libraries\ESP32\examples\ResetReason.ino
#if ESP_IDF_VERSION_MAJOR > 3 // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/rtc.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/rtc.h"
#endif
#include <ESP8266WiFi.h>
#include "esp8266toEsp32.h" #include "esp8266toEsp32.h"
#include "driver/ledc.h"
// Tasmota Logging
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
// ESP Stuff // ESP Stuff
// replicated from `tasmota.h`
#if defined(CONFIG_IDF_TARGET_ESP32)
const uint8_t MAX_PWMS = 16; // ESP32: 16 ledc PWM channels in total - TODO for now
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
const uint8_t MAX_PWMS = 8; // ESP32S2: 8 ledc PWM channels in total
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
const uint8_t MAX_PWMS = 6; // ESP32C3: 6 ledc PWM channels in total
#else
const uint8_t MAX_PWMS = 5; // Unknown - revert to 5 PWM max
#endif
// channel mapping
static uint8_t pwm_channel[MAX_PWMS];
static uint32_t pwm_frequency = 977; // Default 977Hz
static uint8_t pwm_bit_num = 10; // Default 1023
static bool pwm_impl_inited = false; // trigger initialization
/*********************************************************************************************\ /*********************************************************************************************\
* ESP32 analogWrite emulation support * ESP32 analogWrite emulation support
\*********************************************************************************************/ \*********************************************************************************************/
#if CONFIG_IDF_TARGET_ESP32C3 void _analogInit(void) {
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99 }; if (pwm_impl_inited) return;
uint32_t _pwm_frequency = 977; // Default 977Hz // set all channels to unaffected (255)
uint8_t _pwm_bit_num = 10; // Default 1023 for (uint32_t i = 0; i < MAX_PWMS; i++) {
#else // other ESP32 pwm_channel[i] = 255;
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99, 99, 99 }; }
uint32_t _pwm_frequency = 977; // Default 977Hz pwm_impl_inited = true;
uint8_t _pwm_bit_num = 10; // Default 1023 }
#endif // CONFIG_IDF_TARGET_ESP32C3 vs ESP32
uint32_t _analog_pin2chan(uint32_t pin) { int32_t _analog_pin2chan(uint32_t pin) { // returns -1 if uallocated
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) { _analogInit(); // make sure the mapping array is initialized
if ((_pwm_channel[channel] < 99) && (_pwm_channel[channel] == pin)) { for (uint32_t channel = 0; channel < MAX_PWMS; channel++) {
if ((pwm_channel[channel] < 255) && (pwm_channel[channel] == pin)) {
return channel; return channel;
} }
} }
return 0; return -1;
} }
void _analogWriteFreqRange(void) { void _analogWriteFreqRange(void) {
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) { _analogInit(); // make sure the mapping array is initialized
if (_pwm_channel[channel] < 99) { for (uint32_t channel = 0; channel < MAX_PWMS; channel++) {
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num); if (pwm_channel[channel] < 255) {
ledcSetup(channel, pwm_frequency, pwm_bit_num);
} }
} }
} }
@ -80,50 +86,43 @@ uint32_t _analogGetResolution(uint32_t x) {
} }
void analogWriteRange(uint32_t range) { void analogWriteRange(uint32_t range) {
_pwm_bit_num = _analogGetResolution(range); pwm_bit_num = _analogGetResolution(range);
_analogWriteFreqRange(); _analogWriteFreqRange();
} }
void analogWriteFreq(uint32_t freq) { void analogWriteFreq(uint32_t freq) {
_pwm_frequency = freq; pwm_frequency = freq;
_analogWriteFreqRange(); _analogWriteFreqRange();
} }
bool analogAttach(uint32_t pin) { int32_t analogAttach(uint32_t pin) { // returns ledc channel used, or -1 if failed
_analogInit(); // make sure the mapping array is initialized
// Find if pin is already attached // Find if pin is already attached
uint32_t channel; int32_t channel = _analog_pin2chan(pin);
for (channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) { if (channel >= 0) { return channel; }
if (_pwm_channel[channel] == pin) {
// Already attached
// Serial.printf("PWM: Already attached pin %d to channel %d\n", pin, channel);
return true;
}
}
// Find an empty channel // Find an empty channel
for (channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) { for (channel = 0; channel < MAX_PWMS; channel++) {
if (99 == _pwm_channel[channel]) { if (255 == pwm_channel[channel]) {
_pwm_channel[channel] = pin; pwm_channel[channel] = pin;
ledcAttachPin(pin, channel + PWM_CHANNEL_OFFSET); ledcAttachPin(pin, channel);
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num); ledcSetup(channel, pwm_frequency, pwm_bit_num);
// Serial.printf("PWM: New attach pin %d to channel %d\n", pin, channel); // Serial.printf("PWM: New attach pin %d to channel %d\n", pin, channel);
return true; return channel;
} }
} }
// No more channels available // No more channels available
return false; AddLog(LOG_LEVEL_INFO, "PWM: no more PWM (ledc) channel for GPIO %i", pin);
return -1;
} }
// void analogWrite(uint8_t pin, int val); // void analogWrite(uint8_t pin, int val);
extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val) extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val) {
{ analogWritePhase(pin, val, 0); // if unspecified, use phase = 0
uint32_t channel = _analog_pin2chan(pin);
if ( val >> (_pwm_bit_num-1) ) ++val;
ledcWrite(channel + PWM_CHANNEL_OFFSET, val);
// Serial.printf("write %d - %d\n",channel,val);
} }
/* /*
The primary goal of this library is to add phase control to PWM ledc The primary goal of this function is to add phase control to PWM ledc
functions. functions.
Phase control allows to stress less the power supply of LED lights. Phase control allows to stress less the power supply of LED lights.
@ -142,35 +141,28 @@ extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val)
implementation changes. implementation changes.
*/ */
#include "driver/ledc.h"
#ifdef SOC_LEDC_SUPPORT_HS_MODE
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1)
#else
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
#endif
// exported from Arduno Core // exported from Arduno Core
extern uint8_t channels_resolution[LEDC_CHANNELS]; extern uint8_t channels_resolution[MAX_PWMS];
void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase) void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase)
{ {
uint32_t chan = _analog_pin2chan(pin) + PWM_CHANNEL_OFFSET; int32_t chan = _analog_pin2chan(pin);
if (duty >> (_pwm_bit_num-1) ) ++duty; if (chan < 0) { // not yet allocated, try to allocate
chan = analogAttach(pin);
if(chan >= LEDC_CHANNELS){ if (chan < 0) { return; } // failed
return;
} }
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;
uint8_t group=(chan/8), channel=(chan%8); uint8_t group=(chan/8), channel=(chan%8);
//Fixing if all bits in resolution is set = LEDC FULL ON //Fixing if all bits in resolution is set = LEDC FULL ON
uint32_t max_duty = (1 << channels_resolution[chan]) - 1; uint32_t max_duty = (1 << channels_resolution[chan]) - 1;
phase = phase % max_duty; phase = phase & max_duty;
if(duty == max_duty){ // no sure whether this is needed anymore TODO
duty = max_duty + 1;
}
ledc_set_duty_with_hpoint((ledc_mode_t)group, (ledc_channel_t)channel, duty, phase); 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); ledc_update_duty((ledc_mode_t)group, (ledc_channel_t)channel);
} }
#endif // ESP32

View File

@ -13,39 +13,21 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef __ESP8266TOESP32_H__
#define __ESP8266TOESP32_H__
#ifdef ESP32 #ifdef ESP32
// my debug Stuff
#define Serial_Debug1(p) Serial.printf p
#define Serial_DebugX(p)
// //
// basics // basics
// //
// dummy defines
//#define SPIFFS_END (SPI_FLASH_SEC_SIZE * 200)
//#define SETTINGS_LOCATION SPIFFS_END
#include <Esp.h> #include <Esp.h>
#if CONFIG_IDF_TARGET_ESP32C3
#define PWM_SUPPORTED_CHANNELS 6
#define PWM_CHANNEL_OFFSET 1 // Webcam uses channel 0, so we offset standard PWM
#else // other ESP32
#define PWM_SUPPORTED_CHANNELS 8
#define PWM_CHANNEL_OFFSET 2 // Webcam uses channel 0, so we offset standard PWM
#endif // CONFIG_IDF_TARGET_ESP32C3 vs ESP32
extern uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS];
extern uint32_t _pwm_frequency;
extern uint8_t _pwm_bit_num;
void _analogWriteFreqRange(void);
// input range is in full range, ledc needs bits // input range is in full range, ledc needs bits
uint32_t _analogGetResolution(uint32_t x);
void analogWriteRange(uint32_t range); void analogWriteRange(uint32_t range);
void analogWriteFreq(uint32_t freq); void analogWriteFreq(uint32_t freq);
bool analogAttach(uint32_t pin); 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
void analogWrite(uint8_t pin, int val); void analogWrite(uint8_t pin, int val);
// Extended version that also allows to change phase // Extended version that also allows to change phase
@ -56,8 +38,6 @@ extern void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase = 0);
#define INPUT_PULLDOWN_16 INPUT_PULLUP #define INPUT_PULLDOWN_16 INPUT_PULLUP
typedef double real64_t;
// //
// Time and Timer // Time and Timer
// //
@ -71,7 +51,6 @@ typedef double real64_t;
// Serial minimal type to hold the config // Serial minimal type to hold the config
typedef int SerConfu8; typedef int SerConfu8;
typedef int SerialConfig; typedef int SerialConfig;
//#define analogWrite(a, b)
// //
// UDP // UDP
@ -79,9 +58,6 @@ typedef int SerialConfig;
//#define PortUdp_writestr(log_data) PortUdp.write((const uint8_t *)(log_data), strlen(log_data)) //#define PortUdp_writestr(log_data) PortUdp.write((const uint8_t *)(log_data), strlen(log_data))
#define PortUdp_write(log_data, n) PortUdp.write((const uint8_t *)(log_data), n) #define PortUdp_write(log_data, n) PortUdp.write((const uint8_t *)(log_data), n)
//
#define wifi_forceSleepBegin()
#undef LWIP_IPV6 #undef LWIP_IPV6
#define REASON_DEFAULT_RST 0 // "Power on" normal startup by power on #define REASON_DEFAULT_RST 0 // "Power on" normal startup by power on
@ -106,4 +82,5 @@ typedef int SerialConfig;
#define STATION_IF 0 #define STATION_IF 0
#endif #endif // ESP32
#endif // __ESP8266TOESP32_H__

View File

@ -1,8 +0,0 @@
#pragma once
/**/
#include <lwip/ip_addr.h>
/*
#ifndef ICACHE_FLASH_ATTR
#define ICACHE_FLASH_ATTR
#endif
*/

View File

@ -474,7 +474,7 @@ typedef struct {
TimeRule tflag[2]; // 2E2 TimeRule tflag[2]; // 2E2
uint16_t pwm_frequency; // 2E6 uint16_t pwm_frequency; // 2E6
power_t power; // 2E8 power_t power; // 2E8
uint16_t pwm_value[MAX_PWMS]; // 2EC uint16_t pwm_value[MAX_PWMS_LEGACY];// 2EC
int16_t altitude; // 2F6 int16_t altitude; // 2F6
uint16_t tele_period; // 2F8 uint16_t tele_period; // 2F8
uint8_t display_rotate; // 2FA uint8_t display_rotate; // 2FA
@ -579,7 +579,7 @@ typedef struct {
uint8_t ex_my_adc0; // 495 Free since 9.0.0.1 uint8_t ex_my_adc0; // 495 Free since 9.0.0.1
uint16_t light_pixels; // 496 uint16_t light_pixels; // 496
uint8_t light_color[5]; // 498 uint8_t light_color[LST_MAX]; // 498 LST_MAX = 5
uint8_t light_correction; // 49D uint8_t light_correction; // 49D
uint8_t light_dimmer; // 49E uint8_t light_dimmer; // 49E
uint8_t rule_enabled; // 49F uint8_t rule_enabled; // 49F
@ -614,7 +614,8 @@ typedef struct {
uint32_t ipv4_rgx_address; // 558 uint32_t ipv4_rgx_address; // 558
uint32_t ipv4_rgx_subnetmask; // 55C uint32_t ipv4_rgx_subnetmask; // 55C
uint8_t free_560[92]; // 560 uint16_t pwm_value_ext[16-5]; // 560 Extension to pwm_value to store up to 16 PWM for ESP32. This array stores values 5..15
uint8_t free_560[70]; // 576
SysMBitfield1 flag2; // 5BC SysMBitfield1 flag2; // 5BC
uint32_t pulse_counter[MAX_COUNTERS]; // 5C0 uint32_t pulse_counter[MAX_COUNTERS]; // 5C0

View File

@ -184,10 +184,11 @@ bool RtcRebootValid(void) {
extern "C" { extern "C" {
#include "spi_flash.h" #include "spi_flash.h"
} }
#include "eboot_command.h"
#ifdef ESP8266 #ifdef ESP8266
#include "eboot_command.h"
extern "C" uint32_t _FS_start; // 1M = 0x402fb000, 2M = 0x40300000, 4M = 0x40300000 extern "C" uint32_t _FS_start; // 1M = 0x402fb000, 2M = 0x40300000, 4M = 0x40300000
const uint32_t FLASH_FS_START = (((uint32_t)&_FS_start - 0x40200000) / SPI_FLASH_SEC_SIZE); const uint32_t FLASH_FS_START = (((uint32_t)&_FS_start - 0x40200000) / SPI_FLASH_SEC_SIZE);
uint32_t SETTINGS_LOCATION = FLASH_FS_START -1; // 0xFA, 0x0FF or 0x0FF uint32_t SETTINGS_LOCATION = FLASH_FS_START -1; // 0xFA, 0x0FF or 0x0FF
@ -1094,7 +1095,7 @@ void SettingsDefaultSet2(void) {
Settings->pwm_frequency = PWM_FREQ; Settings->pwm_frequency = PWM_FREQ;
Settings->pwm_range = PWM_RANGE; Settings->pwm_range = PWM_RANGE;
for (uint32_t i = 0; i < MAX_PWMS; i++) { for (uint32_t i = 0; i < LST_MAX; i++) {
Settings->light_color[i] = DEFAULT_LIGHT_COMPONENT; Settings->light_color[i] = DEFAULT_LIGHT_COMPONENT;
// Settings->pwm_value[i] = 0; // Settings->pwm_value[i] = 0;
} }

View File

@ -1523,56 +1523,6 @@ void CmndTemplate(void)
if (!error) { TemplateJson(); } if (!error) { TemplateJson(); }
} }
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)) {
Settings->pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload;
analogWrite(Pin(GPIO_PWM1, XdrvMailbox.index -1), bitRead(TasmotaGlobal.pwm_inverted, XdrvMailbox.index -1) ? Settings->pwm_range - XdrvMailbox.payload : XdrvMailbox.payload);
}
Response_P(PSTR("{"));
MqttShowPWMState(); // Render the PWM status to MQTT
ResponseJsonEnd();
}
}
void CmndPwmfrequency(void)
{
if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) {
Settings->pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload;
analogWriteFreq(Settings->pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c)
#ifdef USE_LIGHT
LightReapplyColor();
LightAnimate();
#endif // USE_LIGHT
}
ResponseCmndNumber(Settings->pwm_frequency);
}
void CmndPwmrange(void) {
// Support only 8 (=255), 9 (=511) and 10 (=1023) bits resolution
if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) {
uint32_t pwm_range = XdrvMailbox.payload;
uint32_t pwm_resolution = 0;
while (pwm_range) {
pwm_resolution++;
pwm_range >>= 1;
}
pwm_range = (1 << pwm_resolution) - 1;
uint32_t old_pwm_range = Settings->pwm_range;
Settings->pwm_range = (1 == XdrvMailbox.payload) ? PWM_RANGE : pwm_range;
for (uint32_t i = 0; i < MAX_PWMS; i++) {
if (Settings->pwm_value[i] > Settings->pwm_range) {
Settings->pwm_value[i] = Settings->pwm_range;
}
}
if (Settings->pwm_range != old_pwm_range) { // On ESP32 this prevents loss of duty state
analogWriteRange(Settings->pwm_range); // Default is 1023 (Arduino.h)
}
}
ResponseCmndNumber(Settings->pwm_range);
}
void CmndButtonDebounce(void) void CmndButtonDebounce(void)
{ {
if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) { if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) {

273
tasmota/support_pwm.ino Normal file
View File

@ -0,0 +1,273 @@
/*
support_pwm.ino - command support for Tasmota
Copyright (C) 2021 Theo Arends & Stephan Hadinger
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************\
* PWM Control for ESP32
\***********************************************************************/
#ifdef ESP32
// All changes in PWM have been applied, rearm all change indicators
void PwmRearmChanges(void) {
for (uint32_t i = 0; i < MAX_PWMS; i++) {
// Init expected changes
TasmotaGlobal.pwm_value[i] = -1; // no change wanted
TasmotaGlobal.pwm_phase[i] = -1; // no change wanted
}
}
// Load PWM values from settings and intiliaze values
// void PwmLoadFromSettings(void) {
// for (uint32_t i = 0; i < MAX_PWMS; i++) {
// if (i < MAX_PWMS_LEGACY) {
// TasmotaGlobal.pwm_cur_value[i] = Settings->pwm_value[i]; // retrieve in Legacy pool for 0..4
// } else {
// TasmotaGlobal.pwm_cur_value[i] = Settings->pwm_value_ext[i - MAX_PWMS_LEGACY]; // retrieve in Legacy pool for 5..15
// }
// TasmotaGlobal.pwm_cur_phase[i] = 0; // no phase shift for now, will be recomputed at first push to GPIOs
// }
// PwmRearmChanges(); // reset expected changes
// }
// Copy current values to Settings
void PwmSaveToSettings(void) {
for (uint32_t i = 0; i < MAX_PWMS; i++) {
if (i < MAX_PWMS_LEGACY) {
Settings->pwm_value[i] = TasmotaGlobal.pwm_cur_value[i]; // store in Legacy pool for 0..4
} else {
Settings->pwm_value_ext[i - MAX_PWMS_LEGACY] = TasmotaGlobal.pwm_cur_value[i]; // retrieve in Legacy pool for 5..15
}
}
}
/***********************************************************************\
* PWM Control for ESP32
\***********************************************************************/
// Apply PWM expected values to actual GPIO PWM
// As input, `TasmotaGlobal.pwm_value[]` and `TasmotaGlobal.pwm_phase[]` contain the new expected values
// 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) {
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++) {
// compute `pwm_val`, the virtual value of PWM (not taking into account inverted)
uint32_t pwm_val = TasmotaGlobal.pwm_cur_value[i]; // logical value of PWM, 0..1023
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
if (TasmotaGlobal.pwm_phase[i] >= 0) {
pwm_phase = TasmotaGlobal.pwm_phase[i]; // if explicit set explicitly,
} else if (Settings->flag5.pwm_force_same_phase) {
pwm_phase = 0; // if auto-phase is off
} 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
if (PinUsed(GPIO_PWM1, i)) {
if ((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);
}
}
// set new current values
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
// );
PwmSaveToSettings(); // copy to Settings
PwmRearmChanges(); // reset expected changes
}
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();
}
Response_P(PSTR("{"));
MqttShowPWMState(); // Render the PWM status to MQTT
ResponseJsonEnd();
}
}
void GpioInitPwm(void) {
PwmRearmChanges();
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
analogAttach(Pin(GPIO_PWM1, i));
if (i < TasmotaGlobal.light_type) {
// force PWM GPIOs to black
TasmotaGlobal.pwm_value[i] = 0;
} else {
TasmotaGlobal.pwm_present = true;
if (i < MAX_PWMS_LEGACY) {
TasmotaGlobal.pwm_value[i] = Settings->pwm_value[i];
} else {
TasmotaGlobal.pwm_value[i] = Settings->pwm_value_ext[i - MAX_PWMS_LEGACY];
}
}
}
}
PwmApplyGPIO(); // apply all changes
}
/********************************************************************************************/
void ResetPwm(void)
{
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
TasmotaGlobal.pwm_value[i] = 0;
}
PwmApplyGPIO();
}
#else // now for ESP8266
void PwmRearmChanges(void) {}
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)) {
uint32_t pwm_index = XdrvMailbox.index - 1;
if (pwm_index < MAX_PWMS_LEGACY) { // write in the appropriate settings pool
Settings->pwm_value[pwm_index] = XdrvMailbox.payload;
} else {
Settings->pwm_value_ext[pwm_index - MAX_PWMS_LEGACY] = XdrvMailbox.payload;
}
analogWrite(Pin(GPIO_PWM1, pwm_index), bitRead(TasmotaGlobal.pwm_inverted, pwm_index) ? Settings->pwm_range - XdrvMailbox.payload : XdrvMailbox.payload);
}
Response_P(PSTR("{"));
MqttShowPWMState(); // Render the PWM status to MQTT
ResponseJsonEnd();
}
}
void GpioInitPwm(void) {
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
pinMode(Pin(GPIO_PWM1, i), OUTPUT);
if (i < TasmotaGlobal.light_type) {
// force PWM GPIOs to low or high mode if belongs to the light (always <5), see #7165
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
} else {
TasmotaGlobal.pwm_present = true;
if (i < MAX_PWMS_LEGACY) {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
} else {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value_ext[i] : Settings->pwm_value_ext[i]);
}
}
}
}
}
/********************************************************************************************/
void ResetPwm(void)
{
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
// analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
}
}
}
#endif // ESP8266
void CmndPwmfrequency(void)
{
if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) {
Settings->pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload;
analogWriteFreq(Settings->pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c)
#ifdef USE_LIGHT
LightReapplyColor();
LightAnimate();
#endif // USE_LIGHT
}
ResponseCmndNumber(Settings->pwm_frequency);
}
void CmndPwmrange(void) {
// Support only 8 (=255), 9 (=511) and 10 (=1023) bits resolution
if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) {
uint32_t pwm_range = XdrvMailbox.payload;
uint32_t pwm_resolution = 0;
while (pwm_range) {
pwm_resolution++;
pwm_range >>= 1;
}
pwm_range = (1 << pwm_resolution) - 1;
uint32_t old_pwm_range = Settings->pwm_range;
Settings->pwm_range = (1 == XdrvMailbox.payload) ? PWM_RANGE : pwm_range;
for (uint32_t i = 0; i < MAX_PWMS; i++) {
if (i < MAX_PWMS_LEGACY) {
if (Settings->pwm_value[i] > Settings->pwm_range) {
Settings->pwm_value[i] = Settings->pwm_range;
}
} else {
if (Settings->pwm_value_ext[i - MAX_PWMS_LEGACY] > Settings->pwm_range) {
Settings->pwm_value_ext[i - MAX_PWMS_LEGACY] = Settings->pwm_range;
}
}
}
if (Settings->pwm_range != old_pwm_range) { // On ESP32 this prevents loss of duty state
analogWriteRange(Settings->pwm_range); // Default is 1023 (Arduino.h)
}
}
ResponseCmndNumber(Settings->pwm_range);
}
void MqttShowPWMState(void)
{
ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{"));
bool first = true;
for (uint32_t i = 0; i < MAX_PWMS; i++) { // TODO
if (PinUsed(GPIO_PWM1, i)) {
uint32_t pwm_val = (i < MAX_PWMS_LEGACY) ? Settings->pwm_value[i] : Settings->pwm_value_ext[i - MAX_PWMS_LEGACY];
ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, pwm_val);
first = false;
}
}
ResponseJsonEnd();
}

View File

@ -444,7 +444,7 @@ void SetLedPowerIdx(uint32_t led, uint32_t state)
pwm = changeUIntScale((uint16_t)(state ? Settings->ledpwm_on : Settings->ledpwm_off), 0, 255, 0, Settings->pwm_range); // linear pwm = changeUIntScale((uint16_t)(state ? Settings->ledpwm_on : Settings->ledpwm_off), 0, 255, 0, Settings->pwm_range); // linear
#endif //USE_LIGHT #endif //USE_LIGHT
#ifdef ESP32 #ifdef ESP32
if (analogAttach(Pin(GPIO_LED1, led))) if (analogAttach(Pin(GPIO_LED1, led)) >= 0)
#endif #endif
analogWrite(Pin(GPIO_LED1, led), bitRead(TasmotaGlobal.led_inverted, led) ? Settings->pwm_range - pwm : pwm); analogWrite(Pin(GPIO_LED1, led), bitRead(TasmotaGlobal.led_inverted, led) ? Settings->pwm_range - pwm : pwm);
} else { } else {
@ -735,19 +735,6 @@ void StopAllPowerBlink(void)
} }
} }
void MqttShowPWMState(void)
{
ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{"));
bool first = true;
for (uint32_t i = 0; i < MAX_PWMS; i++) {
if (PinUsed(GPIO_PWM1, i)) {
ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings->pwm_value[i]);
first = false;
}
}
ResponseJsonEnd();
}
void MqttShowState(void) void MqttShowState(void)
{ {
char stemp1[TOPSZ]; char stemp1[TOPSZ];
@ -1670,17 +1657,6 @@ void SerialInput(void)
} }
} }
/********************************************************************************************/
void ResetPwm(void)
{
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
// analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
}
}
}
/********************************************************************************************/ /********************************************************************************************/
@ -1825,7 +1801,7 @@ void GpioInit(void)
mpin -= (AGPIO(GPIO_HEARTBEAT_INV) - AGPIO(GPIO_HEARTBEAT)); mpin -= (AGPIO(GPIO_HEARTBEAT_INV) - AGPIO(GPIO_HEARTBEAT));
} }
else if ((mpin >= AGPIO(GPIO_PWM1_INV)) && (mpin < (AGPIO(GPIO_PWM1_INV) + MAX_PWMS))) { else if ((mpin >= AGPIO(GPIO_PWM1_INV)) && (mpin < (AGPIO(GPIO_PWM1_INV) + MAX_PWMS))) {
bitSet(TasmotaGlobal.pwm_inverted, mpin - AGPIO(GPIO_PWM1_INV)); bitSet(TasmotaGlobal.pwm_inverted, mpin - AGPIO(GPIO_PWM1_INV)); // PWMi are later converted to PMW, but marked as inverted in TasmotaGlobal.pwm_inverted
mpin -= (AGPIO(GPIO_PWM1_INV) - AGPIO(GPIO_PWM1)); mpin -= (AGPIO(GPIO_PWM1_INV) - AGPIO(GPIO_PWM1));
} }
else if (XdrvCall(FUNC_PIN_STATE)) { else if (XdrvCall(FUNC_PIN_STATE)) {
@ -2002,23 +1978,7 @@ void GpioInit(void)
#endif // USE_SONOFF_SC #endif // USE_SONOFF_SC
#endif // ESP8266 #endif // ESP8266
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only GpioInitPwm();
if (PinUsed(GPIO_PWM1, i)) {
#ifdef ESP8266
pinMode(Pin(GPIO_PWM1, i), OUTPUT);
#endif // ESP8266
#ifdef ESP32
analogAttach(Pin(GPIO_PWM1, i));
#endif // ESP32
if (TasmotaGlobal.light_type) {
// force PWM GPIOs to low or high mode, see #7165
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
} else {
TasmotaGlobal.pwm_present = true;
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
}
}
}
for (uint32_t i = 0; i < MAX_RELAYS; i++) { for (uint32_t i = 0; i < MAX_RELAYS; i++) {
if (PinUsed(GPIO_REL1, i)) { if (PinUsed(GPIO_REL1, i)) {

View File

@ -60,7 +60,21 @@ const uint8_t MAX_KEYS = 8; // Max number of keys or buttons (up
const uint8_t MAX_INTERLOCKS_SET = 14; // Max number of interlock groups (MAX_RELAYS / 2) const uint8_t MAX_INTERLOCKS_SET = 14; // Max number of interlock groups (MAX_RELAYS / 2)
const uint8_t MAX_SWITCHES_SET = 28; // Max number of switches const uint8_t MAX_SWITCHES_SET = 28; // Max number of switches
const uint8_t MAX_LEDS = 4; // Max number of leds const uint8_t MAX_LEDS = 4; // Max number of leds
const uint8_t MAX_PWMS = 5; // Max number of PWM channels const uint8_t MAX_PWMS_LEGACY = 5; // Max number of PWM channels in first settings block - Legacy limit for ESP8266, but extended for ESP32 (see below)
#ifdef ESP32
// Max number of PWM channels (total including extended) - ESP32 only
#if defined(CONFIG_IDF_TARGET_ESP32)
const uint8_t MAX_PWMS = 16; // ESP32: 16 ledc PWM channels in total - TODO for now
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
const uint8_t MAX_PWMS = 8; // ESP32S2: 8 ledc PWM channels in total
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
const uint8_t MAX_PWMS = 6; // ESP32C3: 6 ledc PWM channels in total
#else
const uint8_t MAX_PWMS = 5; // Unknown - revert to 5 PWM max
#endif
#else
const uint8_t MAX_PWMS = 5; // (not used on ESP8266)
#endif
const uint8_t MAX_COUNTERS = 4; // Max number of counter sensors const uint8_t MAX_COUNTERS = 4; // Max number of counter sensors
const uint8_t MAX_TIMERS = 16; // Max number of Timers const uint8_t MAX_TIMERS = 16; // Max number of Timers
const uint8_t MAX_PULSETIMERS = 8; // Max number of supported pulse timers const uint8_t MAX_PULSETIMERS = 8; // Max number of supported pulse timers

View File

@ -154,6 +154,13 @@ struct TasmotaGlobal_t {
bool enable_logging; // Enable logging bool enable_logging; // Enable logging
StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits) StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits)
uint16_t pwm_inverted; // PWM inverted flag (1 = inverted) - extended to 16 bits for ESP32
#ifdef ESP32
int16_t pwm_cur_value[MAX_PWMS]; // Current effective values of PWMs as applied to GPIOs
int16_t pwm_cur_phase[MAX_PWMS]; // Current phase values of PWMs as applied to GPIOs
int16_t pwm_value[MAX_PWMS]; // Wanted values of PWMs after update - -1 means no change
int16_t pwm_phase[MAX_PWMS]; // Wanted phase of PWMs after update - -1 means no change
#endif // ESP32
uint8_t init_state; // Tasmota init state uint8_t init_state; // Tasmota init state
uint8_t heartbeat_inverted; // Heartbeat pulse inverted flag uint8_t heartbeat_inverted; // Heartbeat pulse inverted flag
uint8_t spi_enabled; // SPI configured uint8_t spi_enabled; // SPI configured
@ -172,7 +179,7 @@ struct TasmotaGlobal_t {
uint8_t led_inverted; // LED inverted flag (1 = (0 = On, 1 = Off)) uint8_t led_inverted; // LED inverted flag (1 = (0 = On, 1 = Off))
uint8_t led_power; // LED power state uint8_t led_power; // LED power state
uint8_t ledlnk_inverted; // Link LED inverted flag (1 = (0 = On, 1 = Off)) uint8_t ledlnk_inverted; // Link LED inverted flag (1 = (0 = On, 1 = Off))
uint8_t pwm_inverted; // PWM inverted flag (1 = inverted) // uint8_t pwm_inverted; // PWM inverted flag (1 = inverted) -- TODO
uint8_t energy_driver; // Energy monitor configured uint8_t energy_driver; // Energy monitor configured
uint8_t light_driver; // Light module configured uint8_t light_driver; // Light module configured
uint8_t light_type; // Light types uint8_t light_type; // Light types

View File

@ -449,7 +449,7 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_CNTR1_NP) + MAX_COUNTERS, AGPIO(GPIO_CNTR1_NP) + MAX_COUNTERS,
#endif #endif
AGPIO(GPIO_PWM1) + MAX_PWMS, // RGB Red or C Cold White AGPIO(GPIO_PWM1) + MAX_PWMS, // RGB Red or C Cold White
AGPIO(GPIO_PWM1_INV) + MAX_PWMS, AGPIO(GPIO_PWM1_INV) + MAX_PWMS, // or extended PWM for ESP32
#ifdef USE_BUZZER #ifdef USE_BUZZER
AGPIO(GPIO_BUZZER), // Buzzer AGPIO(GPIO_BUZZER), // Buzzer
AGPIO(GPIO_BUZZER_INV), // Inverted buzzer AGPIO(GPIO_BUZZER_INV), // Inverted buzzer

View File

@ -227,7 +227,7 @@ struct LIGHT {
uint8_t old_power = 1; uint8_t old_power = 1;
uint8_t wakeup_active = 0; // 0=inctive, 1=on-going, 2=about to start, 3=will be triggered next cycle uint8_t wakeup_active = 0; // 0=inctive, 1=on-going, 2=about to start, 3=will be triggered next cycle
uint8_t fixed_color_index = 1; uint8_t fixed_color_index = 1;
uint8_t pwm_offset = 0; // Offset in color buffer uint8_t pwm_offset = 0; // Offset in color buffer, used by sm16716 to drive itself RGB, and PWM for CCT (value is 0 or 3)
uint8_t max_scheme = LS_MAX -1; uint8_t max_scheme = LS_MAX -1;
uint32_t wakeup_start_time = 0; uint32_t wakeup_start_time = 0;
@ -1030,7 +1030,7 @@ bool LightModuleInit(void)
TasmotaGlobal.light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0 TasmotaGlobal.light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0
if (Settings->flag.pwm_control) { // SetOption15 - Switch between commands PWM or COLOR/DIMMER/CT/CHANNEL if (Settings->flag.pwm_control) { // SetOption15 - Switch between commands PWM or COLOR/DIMMER/CT/CHANNEL
for (uint32_t i = 0; i < MAX_PWMS; i++) { for (uint32_t i = 0; i < LST_MAX; i++) {
if (PinUsed(GPIO_PWM1, i)) { TasmotaGlobal.light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 if (PinUsed(GPIO_PWM1, i)) { TasmotaGlobal.light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1
} }
} }
@ -1164,9 +1164,7 @@ void LightInit(void)
#ifdef ESP8266 #ifdef ESP8266
pinMode(Pin(GPIO_PWM1, i), OUTPUT); pinMode(Pin(GPIO_PWM1, i), OUTPUT);
#endif // ESP8266 #endif // ESP8266
#ifdef ESP32 // For ESP32, the PWM are already attached by GpioInit() - GpioInitPwm()
analogAttach(Pin(GPIO_PWM1, i));
#endif // ESP32
} }
} }
if (PinUsed(GPIO_ARIRFRCV)) { if (PinUsed(GPIO_ARIRFRCV)) {
@ -2087,10 +2085,6 @@ void LightApplyPower(uint8_t new_color[LST_MAX], power_t power) {
void LightSetOutputs(const uint16_t *cur_col_10) { void LightSetOutputs(const uint16_t *cur_col_10) {
// now apply the actual PWM values, adjusted and remapped 10-bits range // now apply the actual PWM values, adjusted and remapped 10-bits range
if (TasmotaGlobal.light_type < LT_PWM6) { // only for direct PWM lights, not for Tuya, Armtronix... if (TasmotaGlobal.light_type < LT_PWM6) { // only for direct PWM lights, not for Tuya, Armtronix...
#ifdef ESP32
uint32_t pwm_phase = 0; // dephase each PWM channel with the value of the previous
uint32_t pwm_modulus = (1 << _pwm_bit_num) - 1; // 1023
#endif // ESP32
#ifdef USE_PWM_DIMMER #ifdef USE_PWM_DIMMER
uint16_t max_col = 0; uint16_t max_col = 0;
@ -2116,17 +2110,18 @@ void LightSetOutputs(const uint16_t *cur_col_10) {
cur_col = cur_col > 0 ? changeUIntScale(cur_col, 0, Settings->pwm_range, Light.pwm_min, Light.pwm_max) : 0; // shrink to the range of pwm_min..pwm_max cur_col = cur_col > 0 ? changeUIntScale(cur_col, 0, Settings->pwm_range, Light.pwm_min, Light.pwm_max) : 0; // shrink to the range of pwm_min..pwm_max
} }
if (!Settings->flag4.zerocross_dimmer) { if (!Settings->flag4.zerocross_dimmer) {
uint32_t pwm_val = bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - cur_col : cur_col;
#ifdef ESP32 #ifdef ESP32
uint32_t pwm_phase_invert = bitRead(TasmotaGlobal.pwm_inverted, i) ? cur_col : 0; // move phase if inverted TasmotaGlobal.pwm_value[i] = cur_col; // mark the new expected value
analogWritePhase(Pin(GPIO_PWM1, i), pwm_val, Settings->flag5.pwm_force_same_phase ? 0 : (pwm_phase + pwm_phase_invert) & pwm_modulus);
pwm_phase = (pwm_phase + cur_col) & pwm_modulus;
#else // ESP32 #else // ESP32
analogWrite(Pin(GPIO_PWM1, i), pwm_val); analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - cur_col : cur_col);
#endif // ESP32 #endif // ESP32
} }
} }
} }
#ifdef ESP32
PwmApplyGPIO();
#endif // ESP32
#ifdef USE_PWM_DIMMER #ifdef USE_PWM_DIMMER
// Animate brightness LEDs to follow PWM dimmer brightness // Animate brightness LEDs to follow PWM dimmer brightness
if (PWM_DIMMER == TasmotaGlobal.module_type) PWMDimmerSetBrightnessLeds(change10to8(max_col)); if (PWM_DIMMER == TasmotaGlobal.module_type) PWMDimmerSetBrightnessLeds(change10to8(max_col));

View File

@ -1989,7 +1989,7 @@ void CmndDisplayMode(void) {
// Apply the current display dimmer // Apply the current display dimmer
void ApplyDisplayDimmer(void) { void ApplyDisplayDimmer(void) {
uint8_t dimmer8 = changeUIntScale(GetDisplayDimmer(), 0, 100, 0, 255); uint8_t dimmer8 = changeUIntScale(GetDisplayDimmer(), 0, 100, 0, 255);
uint8_t dimmer8_gamma = ledGamma(dimmer8); uint16_t dimmer10_gamma = ledGamma10(dimmer8);
if (dimmer8 && !(disp_power)) { if (dimmer8 && !(disp_power)) {
ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY); ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY);
} }
@ -1997,7 +1997,7 @@ void ApplyDisplayDimmer(void) {
ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY); ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY);
} }
if (renderer) { if (renderer) {
renderer->dim8(dimmer8, dimmer8_gamma); // provide 8 bits and gamma corrected dimmer in 8 bits renderer->dim10(dimmer8, dimmer10_gamma); // provide 8 bits and gamma corrected dimmer in 8 bits
#ifdef USE_BERRY #ifdef USE_BERRY
// still call Berry virtual display in case it is not managed entirely by renderer // still call Berry virtual display in case it is not managed entirely by renderer
Xdsp18(FUNC_DISPLAY_DIM); Xdsp18(FUNC_DISPLAY_DIM);

View File

@ -50,9 +50,6 @@ void BuzzerSet(uint32_t state) {
if (Settings->flag4.buzzer_freq_mode) { // SetOption111 - Enable frequency output mode for buzzer if (Settings->flag4.buzzer_freq_mode) { // SetOption111 - Enable frequency output mode for buzzer
static uint8_t last_state = 0; static uint8_t last_state = 0;
if (last_state != state) { if (last_state != state) {
#ifdef ESP32
if (analogAttach(Pin(GPIO_BUZZER)))
#endif // ESP32
// Set 50% duty cycle for frequency output // Set 50% duty cycle for frequency output
// Set 0% (or 100% for inverted PWM) duty cycle which turns off frequency output either way // Set 0% (or 100% for inverted PWM) duty cycle which turns off frequency output either way
analogWrite(Pin(GPIO_BUZZER), (state) ? Settings->pwm_range / 2 : 0); // set duty cycle for frequency output analogWrite(Pin(GPIO_BUZZER), (state) ? Settings->pwm_range / 2 : 0); // set duty cycle for frequency output

View File

@ -219,12 +219,6 @@ uint32_t WcSetup(int32_t fsiz) {
//esp_log_level_set("*", ESP_LOG_VERBOSE); //esp_log_level_set("*", ESP_LOG_VERBOSE);
camera_config_t config; camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
// config.pixel_format = PIXFORMAT_GRAYSCALE;
// config.pixel_format = PIXFORMAT_RGB565;
if (WcPinUsed()) { if (WcPinUsed()) {
config.pin_d0 = Pin(GPIO_WEBCAM_DATA); // Y2_GPIO_NUM; config.pin_d0 = Pin(GPIO_WEBCAM_DATA); // Y2_GPIO_NUM;
@ -266,7 +260,16 @@ uint32_t WcSetup(int32_t fsiz) {
AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Default template")); AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Default template"));
} }
//ESP.getPsramSize() int32_t ledc_channel = analogAttach(config.pin_xclk);
if (ledc_channel < 0) {
AddLog(LOG_LEVEL_ERROR, "CAM: cannot allocated ledc cahnnel, remove a PWM GPIO");
}
config.ledc_channel = (ledc_channel_t) ledc_channel;
AddLog(LOG_LEVEL_DEBUG_MORE, "CAM: XCLK on GPIO %i using ledc channel %i", config.pin_xclk, config.ledc_channel);
config.ledc_timer = LEDC_TIMER_0;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
//esp_log_level_set("*", ESP_LOG_INFO); //esp_log_level_set("*", ESP_LOG_INFO);