diff --git a/.clang-format b/.clang-format index b877896..d8ae8f2 100644 --- a/.clang-format +++ b/.clang-format @@ -152,3 +152,4 @@ UseTab: Never WhitespaceSensitiveMacros: [] AlignConsecutiveMacros: AcrossEmptyLines AlignArrayOfStructures: Left +CommentPragmas: '^#define' \ No newline at end of file diff --git a/.github/workflows/LibraryBuild.yml b/.github/workflows/LibraryBuild.yml index a298aa3..3125892 100644 --- a/.github/workflows/LibraryBuild.yml +++ b/.github/workflows/LibraryBuild.yml @@ -41,7 +41,7 @@ jobs: - config-name: esp8266-v3 platform-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json - arduino-platform: esp8266:esp8266@3.0.2 + arduino-platform: esp8266:esp8266@3.1.2 arduino-boards-fqbn: esp8266:esp8266:d1_mini - config-name: esp32-v1 @@ -51,7 +51,7 @@ jobs: - config-name: esp32-v2 platform-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json - arduino-platform: esp32:esp32@2.0.4 + arduino-platform: esp32:esp32@2.0.14 arduino-boards-fqbn: esp32:esp32:esp32 - config-name: arduino-uno diff --git a/examples/6_8_lights_effects/effect.cpp b/examples/6_8_lights_effects/effect.cpp index d6f111e..524f7ac 100644 --- a/examples/6_8_lights_effects/effect.cpp +++ b/examples/6_8_lights_effects/effect.cpp @@ -15,7 +15,11 @@ extern DimmableLightLinearized #endif #if defined(ESP8266) - lights[N_LIGHTS] = { { 5 }, { 4 }, { 14 }, { 12 }, { 15 }, { 16 }, { 0 }, { 2 } }; + // Remember that GPIO0 (D3) and GPIO2 (D4) are "critical" since they control the boot phase. + // I have to disconnect them to make it boot when using Krida's dimmers. If you want to + // use those pins without disconnecting and connecting the wires, you need additional circuitry to + // "protect" them. + lights[N_LIGHTS] = { { 5 }, { 4 }, { 14 }, { 12 }, { 15 }, { 16 }, { 0 }, { 2 } }; #elif defined(ESP32) lights[N_LIGHTS] = { { 4 }, { 16 }, { 17 }, { 5 }, { 18 }, { 19 }, { 21 }, { 22 } }; #elif defined(AVR) // Arduino diff --git a/library.json b/library.json index b98363b..98f1a79 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Dimmable Light for Arduino", - "version": "1.5.0", + "version": "1.5.1", "authors": { "name": "Fabiano Riccardi", "email": "fabiano.riccardi@outlook.com" diff --git a/library.properties b/library.properties index f3529e9..021c682 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Dimmable Light for Arduino -version=1.5.0 +version=1.5.1 author=Fabiano Riccardi maintainer=Fabiano Riccardi sentence=This library allows to easily control dimmers (also known as thyristors). diff --git a/platformio.ini b/platformio.ini index e8680c7..4d426cf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,32 +18,34 @@ src_dir = examples/1_dimmable_light lib_dir = . [env:esp8266] -platform = espressif8266 +platform = espressif8266@4.2.1 board = d1_mini framework = arduino [env:esp32] -platform = espressif32 +platform = espressif32@6.4.0 board = lolin32 framework = arduino [env:uno] -platform = atmelavr +platform = atmelavr@4.2.0 board = uno framework = arduino lib_deps = ${env.lib_deps} mike-matera/ArduinoSTL@^1.3.3 +upload_speed = 115200 [env:mega2560] -platform = atmelavr +platform = atmelavr@4.2.0 board = megaatmega2560 framework = arduino lib_deps = ${env.lib_deps} mike-matera/ArduinoSTL@^1.3.3 +upload_speed = 115200 [env:nano_33_iot] -platform = atmelsam +platform = atmelsam@8.2.0 board = nano_33_iot framework = arduino diff --git a/readme.md b/readme.md index 31d79a0..40d15d9 100644 --- a/readme.md +++ b/readme.md @@ -7,22 +7,23 @@ A library to manage thyristors (aka dimmer or triac) and phase-fired control (ak ## Motivations At the very beginning, this library was born from the curiosity to experiment the performance and capabilities of hardware timer on ESP8266 and to control old-fashioned incandescence lights. -In the second instance, I needed to move thyristor controller to other microcontrollers, and the library had to adapt to the underlying hardware timers, which often vary for specifications and expose a very different interface from architecture to architecture. Moreover, at the time there weren't multi-platform libraries to control thyristor, so I decided to extend and maintain this library. +In the second instance, I wanted to port the original piece of software to ESP32, and so I started to conceive a flexible software architecture that better adapts to different hardware platforms. Moreover, at the time there weren't multi-platform libraries to control thyristors, so I decided to publish, extend, and maintain this library over time. ### About the timers -Actually, it was interesting (and sometime frustrating) to discover that a *simple* peripheral such as timer can really vary from architecture to architecture. ESP8266 is equipped with 2 timers, but one is dedicated to Wi-Fi management. This can complicate the development of applications that require multiple and simultaneous use of timer. Moreover, it hasn't "advanced" capabilities such as input compare, multiple output compare channels, and bidirectional counter. At least, these 2 timers are 32-bit. ESP32 is way better than its predecessor: it has 4 64-bit counters with up and down counters, but again no input capture and just 1 output compare channel per timer. Finally, I tried the ATmega328 of AVR family. The main difference and drawback is that they have only 8-bit or 16-bit timers, but this is reasonable if you consider that they were launched in the late 1996, when 8-bit microcontrollers were the standard. However, they provide more functionalities such as input compare and output compare with multiple channels and uniformed control registers over the different family's models. Moreover, they are well-supported by C header files containing complete registers' specifications. -For sure, among these MCUs, the most complete implementation is provided by the old but gold AVR family. -This brief overview gives a glimpse of the variety of timers embedded in microcontrollers, and it highlights the importance of building an abstraction layer that hides these differences and expose the essential functionalities needed to control thyristors: one-shot trigger and stop the timer. +Actually, it was interesting (and sometime frustrating) to discover that a *simple* peripheral such as timer can heavily vary among different platforms. +For example, the ESP8266 is equipped with 2 timers, but only one is usable by the user since the other is reserved for Wi-Fi management. This can lead immediately to a complicate development if the user application needs the timer for multi purposes. For this reason, [ESP8266TimerInterrupt](https://github.com/khoih-prog/ESP8266TimerInterrupt) was born. Moreover, that timer hasn't "advanced" capabilities such as input compare, multiple output compare channels, a bidirectional counter, and it is only 23-bit. Another example is the ESP32, that is way better than its predecessor: it has 4 64-bit timers with up and down counters, but still no input capture and just 1 output compare channel per timer. Finally, I cannot avoid mentioning the AVR ATmega's timers: they have multiple full-featured 8-bit or 16-bit timers running at lower clock frequency than modern MCUs, which may reduce the overall resolution of dimmer control or lead to more complicated ISRs to handle multiple rollovers. At least, AVR MCUs, compared to ESP8266 and ESP32, are well-supported by C header files containing complete registers' specifications. +This brief overview gives a glimpse of the variety of properties to consider while working with timers embedded in microcontrollers, and it highlights the importance of building an abstraction layer that hides all these differences and exposes the 2 primitives needed to control thyristors: one-shot timer activation and stop counting. ## Features -1. Control multiple thyristors at the same time +1. Control multiple thyristors using a single hardware timer 2. Compatible with multiple platforms (ESP8266/ESP32/AVR/SAMD) 3. Interrupt optimization (trigger interrupts only if necessary, no periodic interrupt) -4. Control the load via gate activation time or relative power +4. Control the load by 2 measurement unit: gate activation time or linearized relative power +5. Documented parameters to finely tune the library on your hardware and requirements -Here a comparison against 2 similar and popular libraries: +Here the comparison against 2 similar and popular libraries: | | Dimmable Light for Arduino | [RobotDynOfficial/RDBDimmer](https://github.com/RobotDynOfficial/RBDDimmer) | [circuitar/Dimmer](https://github.com/circuitar/Dimmer) | |------------------------------------------|---------------------------------------------|-----------------------------------------------------------------------------|---------------------------------------------------------| diff --git a/src/circular_queue.h b/src/circular_queue.h index 0d4b97d..8bfc5a8 100644 --- a/src/circular_queue.h +++ b/src/circular_queue.h @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #ifndef CIRCULAR_QUEUE_H #define CIRCULAR_QUEUE_H diff --git a/src/dimmable_light.cpp b/src/dimmable_light.cpp index 9caa842..e2d9d29 100644 --- a/src/dimmable_light.cpp +++ b/src/dimmable_light.cpp @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #include "dimmable_light.h" uint8_t DimmableLight::nLights = 0; \ No newline at end of file diff --git a/src/dimmable_light.h b/src/dimmable_light.h index dd8adb2..2612784 100644 --- a/src/dimmable_light.h +++ b/src/dimmable_light.h @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #ifndef DIMMABLE_LIGHT_H #define DIMMABLE_LIGHT_H diff --git a/src/dimmable_light_linearized.cpp b/src/dimmable_light_linearized.cpp index 139526b..0b964a6 100644 --- a/src/dimmable_light_linearized.cpp +++ b/src/dimmable_light_linearized.cpp @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #include "dimmable_light_linearized.h" uint8_t DimmableLightLinearized::nLights = 0; \ No newline at end of file diff --git a/src/dimmable_light_linearized.h b/src/dimmable_light_linearized.h index 05f993d..b580eef 100644 --- a/src/dimmable_light_linearized.h +++ b/src/dimmable_light_linearized.h @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #ifndef DIMMABLE_LIGHT_LINEARIZED_H #define DIMMABLE_LIGHT_LINEARIZED_H diff --git a/src/dimmable_light_manager.cpp b/src/dimmable_light_manager.cpp index 53de3e1..620aa47 100644 --- a/src/dimmable_light_manager.cpp +++ b/src/dimmable_light_manager.cpp @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #include "dimmable_light_manager.h" bool DimmableLightManager::add(String lightName, uint8_t pin) { diff --git a/src/dimmable_light_manager.h b/src/dimmable_light_manager.h index 7a05901..99c2497 100644 --- a/src/dimmable_light_manager.h +++ b/src/dimmable_light_manager.h @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #ifndef DIMMABLE_LIGHT_MANAGER_H #define DIMMABLE_LIGHT_MANAGER_H diff --git a/src/hw_timer_avr.cpp b/src/hw_timer_avr.cpp index 04cd6fa..0b5509f 100644 --- a/src/hw_timer_avr.cpp +++ b/src/hw_timer_avr.cpp @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #ifdef AVR #include "hw_timer_avr.h" @@ -24,13 +24,14 @@ #include /** - * This parameter controls the timer used by dimmers. Timer0 is used by - * Arduino core, so you shouldn't use it. Moreover, Timer0 and Timer2 are - * just 8bit: this affects the dimmer's resolution control. From my experience, - * 8 bits are not so much to have smooth control on incandescence bulbs, - * I would suggest you to use other timers (16bits). + * This parameter controls the timer used by this library. Timer0 is used by Arduino core, so you + * shouldn't use it. The remaining timers are 8-bits or 16-bits. From my experience with + * incandescence bulbs, I have observed sharp steps in brightness when using 8-bits timers, so I + * decided to set as default the first available 16-bit timer: the *1*. * - * TIMER_ID ranges [1;5] (on arduino mega) + * Free timers ID: + * - [1;2] on Arduino Uno (ATmega328P) + * - [1;5] on Arduino Mega (ATmega2560) */ #define TIMER_ID 1 diff --git a/src/hw_timer_avr.h b/src/hw_timer_avr.h index e6b011c..77048ee 100644 --- a/src/hw_timer_avr.h +++ b/src/hw_timer_avr.h @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ /*********************************************************************************** * Here there is specific AVR code. AVR is usually programmed at very low level diff --git a/src/hw_timer_esp32.cpp b/src/hw_timer_esp32.cpp index 809cee0..f16e24e 100644 --- a/src/hw_timer_esp32.cpp +++ b/src/hw_timer_esp32.cpp @@ -1,27 +1,27 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #ifdef ESP32 #include "hw_timer_esp32.h" -#define TIMER_ID 0 +const static int TIMER_ID = 0; static hw_timer_t* timer = nullptr; @@ -30,43 +30,29 @@ void timerInit(void (*callback)()) { // Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more // info), count up. The counter starts to increase its value. timer = timerBegin(TIMER_ID, 80, true); - // Attach onTimer function to our timer. - timerAttachInterrupt(timer, callback, true); -} + timerStop(timer); + timerWrite(timer, 0); -void IRAM_ATTR setCallback(void (*callback)()) { - // Third parameter stands for "edge" (true) and "level" (false) - timerAttachInterrupt(timer, callback, true); + timerAttachInterrupt(timer, callback, false); } -void IRAM_ATTR startTimerAndTrigger(uint32_t delay) { - // timeStop(timer); - timer->dev->config.enable = 0; - - // timerWrite(timer, 0); - timer->dev->load_high = 0; - timer->dev->load_low = 0; - timer->dev->reload = 1; - - // Set alarm to call onTimer function "delay" microsecond. - // Repeat the alarm (third parameter) - // timerAlarmWrite(timer, delay, true); - timer->dev->alarm_high = 0; - timer->dev->alarm_low = delay; - // Reload the counter, but do not stop the counting - timer->dev->config.autoreload = 0; +void ARDUINO_ISR_ATTR startTimerAndTrigger(uint32_t delay) { + timerWrite(timer, 0); + timerAlarmWrite(timer, delay, false); + timerAlarmEnable(timer); + timerStart(timer); +} - // Start an alarm - // timerAlarmEnable(timer); - timer->dev->config.alarm_en = 1; +void ARDUINO_ISR_ATTR setAlarm(uint32_t delay) { + timerAlarmWrite(timer, delay, false); - // Start timer - // timerStart(timer); - timer->dev->config.enable = 1; + // On core v2.0.0-2.0.1, the timer alarm is automatically disabled after triggering, + // so re-enable the alarm + timerAlarmEnable(timer); } -void IRAM_ATTR stopTimer() { - timer->dev->config.enable = 0; +void ARDUINO_ISR_ATTR stopTimer() { + timerStop(timer); } #endif // END ESP32 diff --git a/src/hw_timer_esp32.h b/src/hw_timer_esp32.h index c81d55a..5cc8c94 100644 --- a/src/hw_timer_esp32.h +++ b/src/hw_timer_esp32.h @@ -1,68 +1,38 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #ifndef HW_TIMER_ESP32_H #define HW_TIMER_ESP32_H #include -typedef struct { - union { - struct { - uint32_t reserved0 : 10; - uint32_t alarm_en : 1; /*When set alarm is enabled*/ - uint32_t level_int_en : 1; /*When set level type interrupt will be generated during alarm*/ - uint32_t edge_int_en : 1; /*When set edge type interrupt will be generated during alarm*/ - uint32_t divider : 16; /*Timer clock (T0/1_clk) pre-scale value.*/ - uint32_t autoreload : 1; /*When set timer 0/1 auto-reload at alarming is enabled*/ - uint32_t increase : 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 - time-base counter decrement.*/ - uint32_t enable : 1; /*When set timer 0/1 time-base counter is enabled*/ - }; - uint32_t val; - } config; - uint32_t cnt_low; /*Register to store timer 0/1 time-base counter current value lower 32 bits.*/ - uint32_t cnt_high; /*Register to store timer 0 time-base counter current value higher 32 bits.*/ - uint32_t update; /*Write any value will trigger a timer 0 time-base counter value update (timer 0 - current value will be stored in registers above)*/ - uint32_t alarm_low; /*Timer 0 time-base counter value lower 32 bits that will trigger the alarm*/ - uint32_t alarm_high; /*Timer 0 time-base counter value higher 32 bits that will trigger the - alarm*/ - uint32_t load_low; /*Lower 32 bits of the value that will load into timer 0 time-base counter*/ - uint32_t load_high; /*higher 32 bits of the value that will load into timer 0 time-base counter*/ - uint32_t reload; /*Write any value will trigger timer 0 time-base counter reload*/ -} hw_timer_reg_t; - -typedef struct hw_timer_s { - hw_timer_reg_t* dev; - uint8_t num; - uint8_t group; - uint8_t timer; - portMUX_TYPE lock; -} hw_timer_t; +// This workaround is necessary to support compilation on ESP32-Arduino v1.0.x +#ifndef ARDUINO_ISR_ATTR +#define ARDUINO_ISR_ATTR +#endif void timerInit(void (*callback)()); -void setCallback(void (*callback)()); - void startTimerAndTrigger(uint32_t delay); +void setAlarm(uint32_t delay); + void stopTimer(); #endif // END HW_TIMER_ESP32_H \ No newline at end of file diff --git a/src/hw_timer_samd.cpp b/src/hw_timer_samd.cpp index 57c8419..6b9e74b 100644 --- a/src/hw_timer_samd.cpp +++ b/src/hw_timer_samd.cpp @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ /*********************************************************************************** * Here there is specific SAMD code. SAMD21 is usually programmed at register level diff --git a/src/hw_timer_samd.h b/src/hw_timer_samd.h index 1a1f2be..bf1b7d9 100644 --- a/src/hw_timer_samd.h +++ b/src/hw_timer_samd.h @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ /*********************************************************************************** * Here there is specific SAMD code. SAMD21 is usually programmed at register level diff --git a/src/thyristor.cpp b/src/thyristor.cpp index 9003a25..83fd2ba 100644 --- a/src/thyristor.cpp +++ b/src/thyristor.cpp @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #include "thyristor.h" #include "circular_queue.h" #include @@ -33,24 +33,21 @@ #error "only ESP8266, ESP32, AVR, SAMD architectures are supported" #endif -// Check if zero cross interrupts are evenly time-spaced (i.e. no spurious interrupt) -// If the interrupt is "too" close to the previous one, ignore the current one. -// To define if two interrupts are too close, look at semiPeriodShrinkMargin and -// semiPeriodExpandMargin constant +// Ignore zero-cross interrupts when they occurs too early w.r.t semi-period ideal length. +// The constant *semiPeriodShrinkMargin* defines the "too early" margin. +// This filter affects the MONITOR_FREQUENCY measurement. //#define FILTER_INT_PERIOD -// FOR DEBUG PURPOSE ONLY: -// If the FILTER_INT_PERIOD is enabled, print on Serial the time passed -// from the previous interrupt. when the semi-period length is "wrong" -// according to semiPeriodShrinkMargin and semiPeriodExpandMargin thresholds. +// FOR DEBUG PURPOSE ONLY. This option requires FILTER_INT_PERIOD enabled. +// Print on serial port the time passed from the previous zero cross interrupt when the semi-period +// length is exceed the interval defined by *semiPeriodShrinkMargin* and *semiPeriodExpandMargin*. //#define PRINT_INT_PERIOD -// FOR DEBUG PURPOSE ONLY: -// check if all the lights were managed in the last semi-period. -// If not, a char is printed on serial. +// FOR DEBUG PURPOSE ONLY. +// Prints a char on the serial port if not all thyristors are managed in a semi-period. //#define CHECK_MANAGED_THYR -// Force the signal lenght of thyristor's gate. If not enabled, the signal to gate +// Force the signal length of thyristor's gate. If not enabled, the signal to gate // is turned off through an interrupt just before the end of the period. // Look at gateTurnOffTime constant for more info. //#define PREDEFINED_PULSE_LENGTH @@ -66,35 +63,38 @@ static const uint16_t semiPeriodLength = 8333; static uint16_t semiPeriodLength = 0; #endif -// The margins are precautions against noise, electrical spikes and frequency skew errors. -// Delay values before startMargin turn the thyristor always ON. -// Delay values after endMargin turn the thyristor always OFF. -// Tune this parameters accordingly to your setup (electrical network and MCU). -// Values are expressed in microseconds +// These margins are precautions against noise, electrical spikes and frequency skew errors. +// Activation delays lower than *startMargin* turn the thyristor fully ON. +// Activation delays higher than *endMargin* turn the thyristor fully OFF. +// Tune this parameters accordingly to your setup (electrical network, MCU, and ZC circuitry). +// Values are expressed in microseconds. static const uint16_t startMargin = 200; static const uint16_t endMargin = 500; -// This parameter represents the time span in which 2 (or more) very near delays are merged: -// This could be necessary for 2 main reasons: +// This parameter represents the time span in which 2 (or more) very near delays are merged (the +// higher ones are merged in the smaller one). This could be necessary for 2 main reasons: // 1) Efficiency, in fact in some applications you will never seem differences between // near delays, hence raising many interrupts is useless. // 2) MCU inability to satisfy very tight "timer start". // After some experiments on incandescence light bulbs, I noted that even 50 microseconds -// are not negligible, so I decided to set threshold lower that 20microsecond (note that -// ESP8266 API documentation suggests to set timer on >10us). If you would to use 8bit -// timers on AVR, you should set a bigger mergePeriod (e.g. 100us). +// are not negligible, so I decided to set threshold lower than 20microsecond. Before lowering this +// value, check the documentation of the specific MCU since some have limitations. For example, +// ESP8266 API documentation suggests to set timer dealy higher than >10us. If you use 8-bit timers +// on AVR, you should set a bigger mergePeriod (e.g. 100us). static const uint16_t mergePeriod = 20; -// Period (in us) before the end of the semiperiod, when an interrupt is trigged to -// turn off each gate signal. Ignore this parameter if you use a predefined pulse length. -// Look at PREDEFINED_PULSE_LENGTH constant for more info. +// Period in microseconds before the end of the semiperiod when an interrupt is triggered to +// turn off all gate signals. This parameter doesn't have any effect if you enable +// PREDEFINED_PULSE_LENGTH. static const uint16_t gateTurnOffTime = 300; static_assert(endMargin - gateTurnOffTime > mergePeriod, "endMargin must be greater than " "(gateTurnOffTime + mergePeriod)"); #ifdef PREDEFINED_PULSE_LENGTH -// Length of pulse of sync gate. this parameter is not applied if thyristor is fully on or off +// Length of pulse on thyristor's gate pin. This parameter is not applied if thyristor is fully on +// or off. This option is suitable only for very short pulses, since it blocks the ISR for the +// specified amount of time. static uint8_t pulseWidth = 15; #endif @@ -103,37 +103,41 @@ struct PinDelay { uint16_t delay; }; +enum class INT_TYPE { ACTIVATE_THYRISTORS, TURN_OFF_GATES }; + +static INT_TYPE nextISR = INT_TYPE::ACTIVATE_THYRISTORS; + /** - * Temporary struct to provide the interrupt a memory concurrent-safe - * NOTE: this structure is manipulated by interrupt routine + * Temporary struct manipulated by the ISR storing the timing information about each dimmer. */ static struct PinDelay pinDelay[Thyristor::N]; /** - * Summary about thyristors state used by interrupt (concurrent-safe) + * Summary of thyristors' state used by ISR (concurrent-safe). */ static bool _allThyristorsOnOff = true; /** - * Tell if zero cross interrupt is enabled. + * Tell if zero-cross interrupt is enabled. */ static bool interruptEnabled = false; /** - * Number of thyristors already managed in the current semi-period + * Number of thyristors already managed in the current semi-period. */ static uint8_t thyristorManaged = 0; /** * Number of thyristors FULLY on. The remaining ones must be turned - * to be turned off by turn_off_gates_int at the end of the semi-period. + * off by turn_off_gates_int at the end of the semi-period. */ static uint8_t alwaysOnCounter = 0; +static uint8_t alwaysOffCounter = 0; #if defined(ARDUINO_ARCH_ESP8266) void HW_TIMER_IRAM_ATTR turn_off_gates_int() { #elif defined(ARDUINO_ARCH_ESP32) -void IRAM_ATTR turn_off_gates_int() { +void ARDUINO_ISR_ATTR turn_off_gates_int() { #else void turn_off_gates_int() { #endif @@ -143,14 +147,13 @@ void turn_off_gates_int() { } /** - * Timer routine to turn on one or more thyristors. - * This function will be called multiple times per semi-period (in case of multi - * lamps with different at least a different delay value). + * Timer routine to turn on one or more thyristors. This function may be be called multiple times + * per semi-period depending on the current thyristors configuration. */ #if defined(ARDUINO_ARCH_ESP8266) void HW_TIMER_IRAM_ATTR activate_thyristors() { #elif defined(ARDUINO_ARCH_ESP32) -void IRAM_ATTR activate_thyristors() { +void ARDUINO_ISR_ATTR activate_thyristors() { #else void activate_thyristors() { #endif @@ -163,17 +166,16 @@ void activate_thyristors() { // Consider the "near" thyristors pinDelay[thyristorManaged + 1].delay - pinDelay[firstToBeUpdated].delay < mergePeriod && // Exclude the one who must remain totally off - pinDelay[thyristorManaged].delay < semiPeriodLength - endMargin; + pinDelay[thyristorManaged].delay <= semiPeriodLength - endMargin; thyristorManaged++) { digitalWrite(pinDelay[thyristorManaged].pin, HIGH); } digitalWrite(pinDelay[thyristorManaged].pin, HIGH); thyristorManaged++; - // This while is dedicated to all those thyristor wih delay >= semiPeriodLength-margin; those are + // This while is dedicated to all those thyristor wih delay == semiPeriodLength-margin; those are // the ones who shouldn't turn on, hence they can be skipped - while (thyristorManaged < Thyristor::nThyristors - && pinDelay[thyristorManaged].delay >= semiPeriodLength - endMargin) { + while (thyristorManaged < Thyristor::nThyristors && pinDelay[thyristorManaged].delay == semiPeriodLength) { thyristorManaged++; } @@ -184,22 +186,22 @@ void activate_thyristors() { #endif if (thyristorManaged < Thyristor::nThyristors) { -#ifdef PREDEFINED_PULSE_LENGTH - int delay = pinDelay[thyristorManaged].delay - pinDelay[firstToBeUpdated].delay - pulseWidth; -#else - int delay = pinDelay[thyristorManaged].delay - pinDelay[firstToBeUpdated].delay; + int delayAbsolute = pinDelay[thyristorManaged].delay; + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_SAMD) + int delayRelative = delayAbsolute - pinDelay[firstToBeUpdated].delay; #endif #if defined(ARDUINO_ARCH_ESP8266) - timer1_write(US_TO_RTC_TIMER_TICKS(delay)); + timer1_write(US_TO_RTC_TIMER_TICKS(delayRelative)); #elif defined(ARDUINO_ARCH_ESP32) - startTimerAndTrigger(delay); + setAlarm(delayAbsolute); #elif defined(ARDUINO_ARCH_AVR) - if (!timerStartAndTrigger(microsecond2Tick(delay))) { + if (!timerStartAndTrigger(microsecond2Tick(delayRelative))) { Serial.println("activate_thyristors() error timer"); } #elif defined(ARDUINO_ARCH_SAMD) - timerStart(microsecond2Tick(delay)); + timerStart(microsecond2Tick(delayRelative)); #endif } else { @@ -216,21 +218,26 @@ void activate_thyristors() { #endif #else // If there are not more thyristors to serve, set timer to turn off gates' signal - uint16_t delay = semiPeriodLength - gateTurnOffTime - pinDelay[firstToBeUpdated].delay; + uint16_t delayAbsolute = semiPeriodLength - gateTurnOffTime; + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_SAMD) + uint16_t delayRelative = delayAbsolute - pinDelay[firstToBeUpdated].delay; +#endif + #if defined(ARDUINO_ARCH_ESP8266) timer1_attachInterrupt(turn_off_gates_int); - timer1_write(US_TO_RTC_TIMER_TICKS(delay)); + timer1_write(US_TO_RTC_TIMER_TICKS(delayRelative)); #elif defined(ARDUINO_ARCH_ESP32) - setCallback(turn_off_gates_int); - startTimerAndTrigger(delay); + nextISR = INT_TYPE::TURN_OFF_GATES; + setAlarm(delayAbsolute); #elif defined(ARDUINO_ARCH_AVR) timerSetCallback(turn_off_gates_int); - if (!timerStartAndTrigger(microsecond2Tick(delay))) { + if (!timerStartAndTrigger(microsecond2Tick(delayRelative))) { Serial.println("activate_thyristors() error timer"); } #elif defined(ARDUINO_ARCH_SAMD) timerSetCallback(turn_off_gates_int); - timerStart(microsecond2Tick(delay)); + timerStart(microsecond2Tick(delayRelative)); #endif #endif } @@ -247,7 +254,7 @@ static uint32_t lastTime = 0; #endif #ifdef MONITOR_FREQUENCY -// Circular Queue to store the value to compute the moving average +// Circular queue to compute the moving average static CircularQueue queue; static uint32_t total = 0; #endif @@ -255,7 +262,7 @@ static uint32_t total = 0; #if defined(ARDUINO_ARCH_ESP8266) void HW_TIMER_IRAM_ATTR zero_cross_int() { #elif defined(ARDUINO_ARCH_ESP32) -void IRAM_ATTR zero_cross_int() { +void ARDUINO_ISR_ATTR zero_cross_int() { #else void zero_cross_int() { #endif @@ -270,8 +277,20 @@ void zero_cross_int() { uint32_t diff = now - lastTime; #ifdef PRINT_INT_PERIOD - if (diff < semiPeriodLength - semiPeriodShrinkMargin) { Serial.println(String('B') + diff); } - if (diff > semiPeriodLength + semiPeriodExpandMargin) { Serial.println(String('A') + diff); } + if (diff < semiPeriodLength - semiPeriodShrinkMargin) { +#ifdef ARDUINO_ARCH_ESP32 + ets_printf("B%d\n", diff); +#else + Serial.println(String('B') + diff); +#endif + } + if (diff > semiPeriodLength + semiPeriodExpandMargin) { +#ifdef ARDUINO_ARCH_ESP32 + ets_printf("A%d\n", diff); +#else + Serial.println(String('A') + diff); +#endif + } #endif #ifdef FILTER_INT_PERIOD @@ -307,22 +326,36 @@ void zero_cross_int() { #ifdef CHECK_MANAGED_THYR if (thyristorManaged != Thyristor::nThyristors) { +#ifdef ARDUINO_ARCH_ESP32 + ets_printf("E%d\n", thyristorManaged); +#else Serial.print("E"); Serial.println(thyristorManaged); +#endif } #endif // Update the structures and set thresholds, if needed if (Thyristor::newDelayValues && !Thyristor::updatingStruct) { Thyristor::newDelayValues = false; + alwaysOffCounter = 0; + alwaysOnCounter = 0; for (int i = 0; i < Thyristor::nThyristors; i++) { pinDelay[i].pin = Thyristor::thyristors[i]->pin; - // Rounding delays to avoid error and unexpected behaviour due to + // Rounding delays to avoid error and unexpected behavior due to // non-ideal thyristors and not perfect sine wave - if (Thyristor::thyristors[i]->delay > 0 && Thyristor::thyristors[i]->delay <= startMargin) { - pinDelay[i].delay = startMargin; - } else if (Thyristor::thyristors[i]->delay >= semiPeriodLength - endMargin) { - pinDelay[i].delay = semiPeriodLength - endMargin; + if (Thyristor::thyristors[i]->delay == 0) { + alwaysOnCounter++; + pinDelay[i].delay = 0; + } else if (Thyristor::thyristors[i]->delay < startMargin) { + alwaysOnCounter++; + pinDelay[i].delay = 0; + } else if (Thyristor::thyristors[i]->delay == semiPeriodLength) { + alwaysOffCounter++; + pinDelay[i].delay = semiPeriodLength; + } else if (Thyristor::thyristors[i]->delay > semiPeriodLength - endMargin) { + alwaysOffCounter++; + pinDelay[i].delay = semiPeriodLength; } else { pinDelay[i].delay = Thyristor::thyristors[i]->delay; } @@ -335,7 +368,7 @@ void zero_cross_int() { // if all are on and off, I can disable the zero cross interrupt if (_allThyristorsOnOff) { for (int i = 0; i < Thyristor::nThyristors; i++) { - if (pinDelay[i].delay == semiPeriodLength - endMargin) { + if (pinDelay[i].delay == semiPeriodLength) { digitalWrite(pinDelay[i].pin, LOW); } else { digitalWrite(pinDelay[i].pin, HIGH); @@ -370,7 +403,6 @@ void zero_cross_int() { digitalWrite(pinDelay[thyristorManaged].pin, HIGH); thyristorManaged++; } - alwaysOnCounter = thyristorManaged; // This block of code is inteded to manage the case near to the next semi-period: // In this case we should avoid to trigger the timer, because the effective semiperiod @@ -381,23 +413,32 @@ void zero_cross_int() { // NOTE: don't know why, but the timer seem trigger even when it is not set... // so a provvisory solution if to set the relative callback to NULL! // NOTE 2: this improvement should be think even for multiple lamp! - if (thyristorManaged < Thyristor::nThyristors && pinDelay[thyristorManaged].delay < semiPeriodLength - 50) { + if (thyristorManaged < Thyristor::nThyristors && pinDelay[thyristorManaged].delay < semiPeriodLength) { + uint16_t delayAbsolute = pinDelay[thyristorManaged].delay; #if defined(ARDUINO_ARCH_ESP8266) timer1_attachInterrupt(activate_thyristors); - timer1_write(US_TO_RTC_TIMER_TICKS(pinDelay[thyristorManaged].delay)); + timer1_write(US_TO_RTC_TIMER_TICKS(delayAbsolute)); #elif defined(ARDUINO_ARCH_ESP32) - setCallback(activate_thyristors); - startTimerAndTrigger(pinDelay[thyristorManaged].delay); + // setCallback(activate_thyristors); + nextISR = INT_TYPE::ACTIVATE_THYRISTORS; + startTimerAndTrigger(delayAbsolute); #elif defined(ARDUINO_ARCH_AVR) timerSetCallback(activate_thyristors); - if (!timerStartAndTrigger(microsecond2Tick(pinDelay[thyristorManaged].delay))) { + if (!timerStartAndTrigger(microsecond2Tick(delayAbsolute))) { Serial.println("zero_cross_int() error timer"); } #elif defined(ARDUINO_ARCH_SAMD) timerSetCallback(activate_thyristors); - timerStart(microsecond2Tick(pinDelay[thyristorManaged].delay)); + timerStart(microsecond2Tick(delayAbsolute)); #endif } else { + + // This while is dedicated to all those thyristor wih delay == semiPeriodLength-margin; those + // are the ones who shouldn't turn on, hence they can be skipped + while (thyristorManaged < Thyristor::nThyristors && pinDelay[thyristorManaged].delay == semiPeriodLength) { + thyristorManaged++; + } + #if defined(ARDUINO_ARCH_ESP8266) // Given the Arduino HAL and esp8266 technical reference manual, // when timer triggers, the counter stops because it has reached zero @@ -410,6 +451,20 @@ void zero_cross_int() { } } +#if defined(ARDUINO_ARCH_ESP8266) +void HW_TIMER_IRAM_ATTR isr_selector() { +#elif defined(ARDUINO_ARCH_ESP32) +void ARDUINO_ISR_ATTR isr_selector() { +#else +void isr_selector() { +#endif + if (nextISR == INT_TYPE::ACTIVATE_THYRISTORS) { + activate_thyristors(); + } else if (nextISR == INT_TYPE::TURN_OFF_GATES) { + turn_off_gates_int(); + } +} + void Thyristor::setDelay(uint16_t newDelay) { if (verbosity > 2) { for (int i = 0; i < Thyristor::nThyristors; i++) { @@ -420,12 +475,14 @@ void Thyristor::setDelay(uint16_t newDelay) { } } + if (newDelay > semiPeriodLength) { newDelay = semiPeriodLength; } + // Reorder the array to speed up the interrupt. - // This mini-algorithm works on a different memory area wrt the interrupt, - // so it is concurrent-safe code + // This mini-algorithm works on a different memory area w.r.t. the ISR, + // so it is concurrent-safe updatingStruct = true; - // Array example, it is always ordered, higher values means lower delay + // Array example, it is always ordered, higher values means lower brightness levels // [45,678,5000,7500,9000] if (newDelay > delay) { if (verbosity > 2) Serial.println("\tlowering the light.."); @@ -528,19 +585,19 @@ void Thyristor::begin() { #if defined(ARDUINO_ARCH_ESP8266) timer1_attachInterrupt(activate_thyristors); - // These 2 registers assignements are the "unrolling" of: + // These 2 registers assignments are the "unrolling" of: // timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE); T1C = (1 << TCTE) | ((TIM_DIV16 & 3) << TCPD) | ((TIM_EDGE & 1) << TCIT) | ((TIM_SINGLE & 1) << TCAR); T1I = 0; #elif defined(ARDUINO_ARCH_ESP32) - timerInit(activate_thyristors); + timerInit(isr_selector); #elif defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_SAMD) timerSetCallback(activate_thyristors); timerBegin(); #endif #ifdef MONITOR_FREQUENCY - // Starts immediatly to sense the electrical network + // Starts immediately to sense the eletricity grid interruptEnabled = true; attachInterrupt(digitalPinToInterrupt(syncPin), zero_cross_int, RISING); diff --git a/src/thyristor.h b/src/thyristor.h index bce3230..b39cf7a 100644 --- a/src/thyristor.h +++ b/src/thyristor.h @@ -1,22 +1,22 @@ -/*************************************************************************** - * This file is part of Dimmable Light for Arduino, a library to * - * control dimmers. * - * * - * Copyright (C) 2018-2022 Fabiano Riccardi * - * * - * Dimmable Light for Arduino is free software; you can redistribute * - * it and/or modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 * - * Lesser 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 * - ***************************************************************************/ +/****************************************************************************** + * This file is part of Dimmable Light for Arduino, a library to control * + * dimmers. * + * * + * Copyright (C) 2018-2023 Fabiano Riccardi * + * * + * Dimmable Light for Arduino is free software; you can redistribute * + * it and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this library; if not, see . * + ******************************************************************************/ #ifndef THYRISTOR_H #define THYRISTOR_H @@ -26,12 +26,11 @@ * These defines affect the declaration of this class and the relative wrappers. */ -// Set how the network frequency is selected. -// The first 2 options fix the frequency to the common values (respectively to -// 50 and 60Hz) at compile time. -// The third option allows you change network frequency at runtime. This method -// enables the setFrequency() method. The main drawback is that it is a bit more -// inefficient w.r.t the "fixed frequency" alternatives. +// Set the network frequency. +// The first 2 options fix the frequency to the common values (respectively to 50 and 60Hz) at +// compile time. The third option allows you change network frequency at runtime. This option +// automatically enables the setFrequency() method. The main drawback is that it requires a few more +// resources w.r.t. the "fixed frequency" alternatives. // Select one and ONLY one among the following alternatives: //#define NETWORK_FREQ_FIXED_50HZ //#define NETWORK_FREQ_FIXED_60HZ @@ -46,27 +45,23 @@ //#define MONITOR_FREQUENCY /** - * This is the core class of this library, to be used to get the finest control - * on thyristors. + * This is the core class of this library, that provides the finest control on thyristors. * - * NOTE: Design Principle for this library: The concept of Thyristor is agnostic - * to controlled appliance, hence measurement unit to control them should be also - * appliance-agnostic. However, this will lead to more abstract code, - * an undesiderable effect especially in contextes such as Arduino, - * oriented to be as user-friendly as possible. + * NOTE: Design Principle for this library: There are 2 main abstraction levels: the first one, + * represented by Thyristor class, is agnostic about the controlled load (it doesn't assume a lamp, + * a heater or a motor). The second one provides a simpler and more concrete interface, presenting + * simplified APIs to the user as expected by an Arduino library, and it is exemplified by + * DimmableLight class. + * Now, I'm aware that this is positive because it allows to write very + * readable code IF the appliance is a light, but it is limiting and weird if the user is going to + * use another appliance. * - * For these reason, I decided to separate the library in 2 main level of classes: - * a low level one (appliance-agnostic), and a higher level one for final - * user (a nicer appliance-dependent wrapper, e.g. Dimmable Light). - * - * About this class, the "core" of the library, the name of the method - * to control a dimmer is setDelay(..) and not, for example, setPower(..), - * setBrightness(..),... This gives a precise idea of what's - * happening in electrical world, that is controlling the activation time - * of the thyristor. - * Secondly, the measurement unit is expressed in microsecond, - * allowing the finest and feasible control reachable with old and cheap - * MCUs such as Arduino Uno (often still exceeding the real need). + * About this class, the "core" of the library, the name of the method to control a dimmer is + * setDelay(..) and not, for example, setPower(..), setBrightness(..), ... This gives a precise idea + * of what's happening at electrical level, that is controlling the activation time of the + * thyristor. Secondly, the measurement unit is expressed in microseconds, allowing the finest and + * feasible control reachable with almost any MCU avaialble on the market (including Arduino UNO + * based on ATmega328p). */ class Thyristor { public: