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
- 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)
- Increase PWM channels to 16 (Esp32 only)
### Changed
- 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};
#ifdef ESP32
#include "esp8266toEsp32.h"
#define ST7789_DIMMER
#endif
@ -258,11 +259,9 @@ void Arduino_ST7789::commonInit(const uint8_t *cmdList) {
}
if (_bp>=0) {
#define ESP32_PWM_CHANNEL 1
// #define ESP32_PWM_CHANNEL 1
#ifdef ST7789_DIMMER
ledcSetup(ESP32_PWM_CHANNEL,4000,8);
ledcAttachPin(_bp,ESP32_PWM_CHANNEL);
ledcWrite(ESP32_PWM_CHANNEL,128);
analogWrite(_bp, 128);
#else
pinMode(_bp, OUTPUT);
#endif
@ -575,7 +574,8 @@ void Arduino_ST7789::DisplayOnff(int8_t on) {
writecommand(ST7789_DISPON); //Display on
if (_bp>=0) {
#ifdef ST7789_DIMMER
ledcWrite(ESP32_PWM_CHANNEL,dimmer);
analogWrite(_bp, dimmer);
// ledcWrite(ESP32_PWM_CHANNEL,dimmer);
#else
digitalWrite(_bp,HIGH);
#endif
@ -584,7 +584,8 @@ void Arduino_ST7789::DisplayOnff(int8_t on) {
writecommand(ST7789_DISPOFF);
if (_bp>=0) {
#ifdef ST7789_DIMMER
ledcWrite(ESP32_PWM_CHANNEL,0);
analogWrite(_bp, 0);
// ledcWrite(ESP32_PWM_CHANNEL,0);
#else
digitalWrite(_bp,LOW);
#endif
@ -598,7 +599,8 @@ void Arduino_ST7789::dim(uint8_t dim) {
if (dimmer>15) dimmer=15;
dimmer=((float)dimmer/15.0)*255.0;
#ifdef ESP32
ledcWrite(ESP32_PWM_CHANNEL,dimmer);
analogWrite(_bp, dimmer);
// ledcWrite(ESP32_PWM_CHANNEL,dimmer);
#endif
}

View File

@ -59,10 +59,10 @@ uint16_t Renderer::GetColorFromIndex(uint8_t index) {
void Renderer::dim(uint8_t contrast) {
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 Updateframe();
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 setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
virtual void invertDisplay(boolean i);

View File

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

View File

@ -20,6 +20,10 @@
#include <Arduino.h>
#include "uDisplay.h"
#ifdef ESP32
#include "esp8266toEsp32.h"
#endif
// #define UDSP_DEBUG
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) {
#ifdef ESP32
ledcSetup(ESP32_PWM_CHANNEL, 977, 8); // use 10 bits resolution like in Light
ledcAttachPin(bpanel, ESP32_PWM_CHANNEL);
ledcWrite(ESP32_PWM_CHANNEL, 8); // 38/255 correspond roughly to 50% visual brighness (with Gamma)
analogWrite(bpanel, 32);
#else
pinMode(bpanel, OUTPUT);
digitalWrite(bpanel, HIGH);
@ -1353,7 +1355,8 @@ void uDisplay::DisplayOnff(int8_t on) {
if (dsp_on != 0xff) spi_command_one(dsp_on);
if (bpanel >= 0) {
#ifdef ESP32
ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma);
analogWrite(bpanel, dimmer10_gamma);
// ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma);
#else
digitalWrite(bpanel, HIGH);
#endif
@ -1363,7 +1366,8 @@ void uDisplay::DisplayOnff(int8_t on) {
if (dsp_off != 0xff) spi_command_one(dsp_off);
if (bpanel >= 0) {
#ifdef ESP32
ledcWrite(ESP32_PWM_CHANNEL, 0);
analogWrite(bpanel, 0);
// ledcWrite(ESP32_PWM_CHANNEL, 0);
#else
digitalWrite(bpanel, LOW);
#endif
@ -1402,16 +1406,17 @@ void udisp_dimm(uint8_t dim);
// }
// 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_gamma = dim_gamma;
dimmer10_gamma = dim_gamma;
if (ep_mode) {
return;
}
#ifdef ESP32 // TODO should we also add a ESP8266 version for bpanel?
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) {
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_HIGH if (spi_dc >= 0) GPIO_SET_SLOW(spi_dc);
#define ESP32_PWM_CHANNEL 1
#define LUTMAXSIZE 64
class uDisplay : public Renderer {
@ -93,7 +90,7 @@ class uDisplay : public Renderer {
uint16_t bgcol(void);
int8_t color_type(void);
// 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);
void setRotation(uint8_t m);
void fillScreen(uint16_t color);
@ -186,7 +183,7 @@ class uDisplay : public Renderer {
int8_t bpanel; // backbanel GPIO, -1 if none
int8_t spi_miso;
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;
uint8_t sspi;
SPISettings spiSettings;

View File

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

View File

@ -43,8 +43,8 @@
#include "lwip/netif.h"
#ifdef ESP8266
#include <include/ClientContext.h>
#include "c_types.h"
#endif
#include "c_types.h"
#include <core_version.h>
#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/>.
*/
//
#ifdef ESP32
#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 "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
// 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
\*********************************************************************************************/
#if CONFIG_IDF_TARGET_ESP32C3
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99 };
uint32_t _pwm_frequency = 977; // Default 977Hz
uint8_t _pwm_bit_num = 10; // Default 1023
#else // other ESP32
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99, 99, 99 };
uint32_t _pwm_frequency = 977; // Default 977Hz
uint8_t _pwm_bit_num = 10; // Default 1023
#endif // CONFIG_IDF_TARGET_ESP32C3 vs ESP32
void _analogInit(void) {
if (pwm_impl_inited) return;
// set all channels to unaffected (255)
for (uint32_t i = 0; i < MAX_PWMS; i++) {
pwm_channel[i] = 255;
}
pwm_impl_inited = true;
}
uint32_t _analog_pin2chan(uint32_t pin) {
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if ((_pwm_channel[channel] < 99) && (_pwm_channel[channel] == pin)) {
int32_t _analog_pin2chan(uint32_t pin) { // returns -1 if uallocated
_analogInit(); // make sure the mapping array is initialized
for (uint32_t channel = 0; channel < MAX_PWMS; channel++) {
if ((pwm_channel[channel] < 255) && (pwm_channel[channel] == pin)) {
return channel;
}
}
return 0;
return -1;
}
void _analogWriteFreqRange(void) {
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (_pwm_channel[channel] < 99) {
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num);
_analogInit(); // make sure the mapping array is initialized
for (uint32_t channel = 0; channel < MAX_PWMS; channel++) {
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) {
_pwm_bit_num = _analogGetResolution(range);
pwm_bit_num = _analogGetResolution(range);
_analogWriteFreqRange();
}
void analogWriteFreq(uint32_t freq) {
_pwm_frequency = freq;
pwm_frequency = freq;
_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
uint32_t channel;
for (channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (_pwm_channel[channel] == pin) {
// Already attached
// Serial.printf("PWM: Already attached pin %d to channel %d\n", pin, channel);
return true;
}
}
int32_t channel = _analog_pin2chan(pin);
if (channel >= 0) { return channel; }
// Find an empty channel
for (channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (99 == _pwm_channel[channel]) {
_pwm_channel[channel] = pin;
ledcAttachPin(pin, channel + PWM_CHANNEL_OFFSET);
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num);
for (channel = 0; channel < MAX_PWMS; channel++) {
if (255 == pwm_channel[channel]) {
pwm_channel[channel] = pin;
ledcAttachPin(pin, channel);
ledcSetup(channel, pwm_frequency, pwm_bit_num);
// Serial.printf("PWM: New attach pin %d to channel %d\n", pin, channel);
return true;
return channel;
}
}
// 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);
extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val)
{
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);
extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val) {
analogWritePhase(pin, val, 0); // if unspecified, use phase = 0
}
/*
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.
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.
*/
#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
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)
{
uint32_t chan = _analog_pin2chan(pin) + PWM_CHANNEL_OFFSET;
if (duty >> (_pwm_bit_num-1) ) ++duty;
if(chan >= LEDC_CHANNELS){
return;
int32_t chan = _analog_pin2chan(pin);
if (chan < 0) { // not yet allocated, try to allocate
chan = analogAttach(pin);
if (chan < 0) { return; } // failed
}
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);
//Fixing if all bits in resolution is set = LEDC FULL ON
uint32_t max_duty = (1 << channels_resolution[chan]) - 1;
phase = phase % max_duty;
if(duty == max_duty){ // no sure whether this is needed anymore TODO
duty = max_duty + 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);
}
#endif // ESP32

View File

@ -13,39 +13,21 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __ESP8266TOESP32_H__
#define __ESP8266TOESP32_H__
#ifdef ESP32
// my debug Stuff
#define Serial_Debug1(p) Serial.printf p
#define Serial_DebugX(p)
//
// basics
//
// dummy defines
//#define SPIFFS_END (SPI_FLASH_SEC_SIZE * 200)
//#define SETTINGS_LOCATION SPIFFS_END
#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
uint32_t _analogGetResolution(uint32_t x);
void analogWriteRange(uint32_t range);
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);
// 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
typedef double real64_t;
//
// Time and Timer
//
@ -71,7 +51,6 @@ typedef double real64_t;
// Serial minimal type to hold the config
typedef int SerConfu8;
typedef int SerialConfig;
//#define analogWrite(a, b)
//
// 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_write(log_data, n) PortUdp.write((const uint8_t *)(log_data), n)
//
#define wifi_forceSleepBegin()
#undef LWIP_IPV6
#define REASON_DEFAULT_RST 0 // "Power on" normal startup by power on
@ -106,4 +82,5 @@ typedef int SerialConfig;
#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
uint16_t pwm_frequency; // 2E6
power_t power; // 2E8
uint16_t pwm_value[MAX_PWMS]; // 2EC
uint16_t pwm_value[MAX_PWMS_LEGACY];// 2EC
int16_t altitude; // 2F6
uint16_t tele_period; // 2F8
uint8_t display_rotate; // 2FA
@ -579,7 +579,7 @@ typedef struct {
uint8_t ex_my_adc0; // 495 Free since 9.0.0.1
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_dimmer; // 49E
uint8_t rule_enabled; // 49F
@ -614,7 +614,8 @@ typedef struct {
uint32_t ipv4_rgx_address; // 558
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
uint32_t pulse_counter[MAX_COUNTERS]; // 5C0

View File

@ -184,10 +184,11 @@ bool RtcRebootValid(void) {
extern "C" {
#include "spi_flash.h"
}
#include "eboot_command.h"
#ifdef ESP8266
#include "eboot_command.h"
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);
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_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->pwm_value[i] = 0;
}

View File

@ -1523,56 +1523,6 @@ void CmndTemplate(void)
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)
{
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
#endif //USE_LIGHT
#ifdef ESP32
if (analogAttach(Pin(GPIO_LED1, led)))
if (analogAttach(Pin(GPIO_LED1, led)) >= 0)
#endif
analogWrite(Pin(GPIO_LED1, led), bitRead(TasmotaGlobal.led_inverted, led) ? Settings->pwm_range - pwm : pwm);
} 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)
{
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));
}
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));
}
else if (XdrvCall(FUNC_PIN_STATE)) {
@ -2002,23 +1978,7 @@ void GpioInit(void)
#endif // USE_SONOFF_SC
#endif // ESP8266
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
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]);
}
}
}
GpioInitPwm();
for (uint32_t i = 0; i < MAX_RELAYS; 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_SWITCHES_SET = 28; // Max number of switches
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_TIMERS = 16; // Max number of 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
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 heartbeat_inverted; // Heartbeat pulse inverted flag
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_power; // LED power state
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 light_driver; // Light module configured
uint8_t light_type; // Light types

View File

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

View File

@ -227,7 +227,7 @@ struct LIGHT {
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 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;
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
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
}
}
@ -1164,9 +1164,7 @@ void LightInit(void)
#ifdef ESP8266
pinMode(Pin(GPIO_PWM1, i), OUTPUT);
#endif // ESP8266
#ifdef ESP32
analogAttach(Pin(GPIO_PWM1, i));
#endif // ESP32
// For ESP32, the PWM are already attached by GpioInit() - GpioInitPwm()
}
}
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) {
// 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...
#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
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
}
if (!Settings->flag4.zerocross_dimmer) {
uint32_t pwm_val = bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - cur_col : cur_col;
#ifdef ESP32
uint32_t pwm_phase_invert = bitRead(TasmotaGlobal.pwm_inverted, i) ? cur_col : 0; // move phase if inverted
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;
TasmotaGlobal.pwm_value[i] = cur_col; // mark the new expected value
#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
}
}
}
#ifdef ESP32
PwmApplyGPIO();
#endif // ESP32
#ifdef USE_PWM_DIMMER
// Animate brightness LEDs to follow PWM dimmer brightness
if (PWM_DIMMER == TasmotaGlobal.module_type) PWMDimmerSetBrightnessLeds(change10to8(max_col));

View File

@ -1989,7 +1989,7 @@ void CmndDisplayMode(void) {
// Apply the current display dimmer
void ApplyDisplayDimmer(void) {
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)) {
ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY);
}
@ -1997,7 +1997,7 @@ void ApplyDisplayDimmer(void) {
ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY);
}
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
// still call Berry virtual display in case it is not managed entirely by renderer
Xdsp18(FUNC_DISPLAY_DIM);

View File

@ -50,12 +50,9 @@ void BuzzerSet(uint32_t state) {
if (Settings->flag4.buzzer_freq_mode) { // SetOption111 - Enable frequency output mode for buzzer
static uint8_t last_state = 0;
if (last_state != state) {
#ifdef ESP32
if (analogAttach(Pin(GPIO_BUZZER)))
#endif // ESP32
// Set 50% duty cycle for frequency output
// 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
// Set 50% duty cycle for frequency output
// 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
last_state = state;
}
} else {

View File

@ -219,12 +219,6 @@ uint32_t WcSetup(int32_t fsiz) {
//esp_log_level_set("*", ESP_LOG_VERBOSE);
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()) {
config.pin_d0 = Pin(GPIO_WEBCAM_DATA); // Y2_GPIO_NUM;
@ -265,8 +259,17 @@ uint32_t WcSetup(int32_t fsiz) {
config.pin_reset = RESET_GPIO_NUM;
AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Default template"));
}
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.getPsramSize()
//esp_log_level_set("*", ESP_LOG_INFO);