├── .gitignore ├── LICENSE ├── README.md ├── fade_demo_01 └── fade_demo_01.ino ├── fade_demo_02 ├── Display.cpp ├── Display.hpp └── fade_demo_02.ino ├── fade_demo_03 ├── Buttons.cpp ├── Buttons.hpp ├── Display.cpp ├── Display.hpp └── fade_demo_03.ino ├── fade_demo_04 ├── Animation.cpp ├── Animation.hpp ├── Buttons.cpp ├── Buttons.hpp ├── Display.cpp ├── Display.hpp └── fade_demo_04.ino ├── fade_demo_05 ├── Animation.cpp ├── Animation.hpp ├── Buttons.cpp ├── Buttons.hpp ├── Display.cpp ├── Display.hpp └── fade_demo_05.ino ├── fade_demo_06 ├── Animation.cpp ├── Animation.hpp ├── Buttons.cpp ├── Buttons.hpp ├── Display.cpp ├── Display.hpp └── fade_demo_06.ino ├── fade_demo_07 ├── Animation.cpp ├── Animation.hpp ├── AnimationMode.cpp ├── AnimationMode.hpp ├── Buttons.cpp ├── Buttons.hpp ├── Display.cpp ├── Display.hpp └── fade_demo_07.ino └── fade_demo_08 ├── Animation.cpp ├── Animation.hpp ├── AnimationMode.cpp ├── AnimationMode.hpp ├── Buttons.cpp ├── Buttons.hpp ├── Display.cpp ├── Display.hpp └── fade_demo_08.ino /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Lucky Resistor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Guide to Modular Firmware Example Code 2 | 3 | This reposiitory contains the example code for this article: 4 | 5 | [Guide to Modular Firmware](https://luckyresistor.me/2019/07/14/guide-to-modular-firmware/) 6 | 7 | *⚠️ Warning:* The article illustrates how to convert bad code into mopdular one, therefore this example code 8 | is _not well written_ code and should not be used as reference! 9 | 10 | ## License 11 | 12 | This program 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 3 of the License, or (at your option) any later version. 13 | 14 | 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 Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/. 17 | -------------------------------------------------------------------------------- /fade_demo_01/fade_demo_01.ino: -------------------------------------------------------------------------------- 1 | // Warning: This is a bad code example for the article "guideline for modular firmware". 2 | // Please never write code like this! 3 | 4 | const uint8_t cMaxLevel = 0x20; 5 | const uint8_t cModeCount = 4; 6 | const uint8_t cShiftSpeed = 8; 7 | const uint8_t cBlinkSpeed = 10; 8 | volatile uint8_t gFadeCounter = 0; 9 | volatile uint8_t gOrangeLevel = 0x0c; 10 | volatile uint8_t gGreenLevel = 0x12; 11 | volatile uint8_t gRedLevel = 0x18; 12 | uint8_t gLedMode = 0; 13 | uint8_t gLedIndex = 0; 14 | uint8_t gLedCounter = 0; 15 | bool gLedDirection = false; 16 | bool gLastButtonA = true; 17 | bool gLastButtonB = true; 18 | bool gOrangeDirection = true; 19 | bool gGreenDirection = true; 20 | bool gRedDirection = true; 21 | 22 | void setup() { 23 | // Initialize Timer 24 | TCCR2A = 0; 25 | TCCR2B = 1; 26 | OCR2A = 0; 27 | OCR2B = 0; 28 | TIMSK2 = _BV(TOIE2); 29 | // Ports 30 | DDRB |= 0b00111000; 31 | DDRB &= ~0b00000110; 32 | PORTB &= ~0b00111000; 33 | PORTB |= 0b00000110; 34 | } 35 | 36 | void loop() { 37 | if (gLedMode == 0) { 38 | if (gOrangeDirection) { 39 | ++gOrangeLevel; 40 | if (gOrangeLevel == cMaxLevel) { 41 | gOrangeDirection = false; 42 | } 43 | } else { 44 | --gOrangeLevel; 45 | if (gOrangeLevel == 0) { 46 | gOrangeDirection = true; 47 | } 48 | } 49 | if (gGreenDirection) { 50 | gGreenLevel += 1; 51 | if (gGreenLevel == cMaxLevel) { 52 | gGreenDirection = false; 53 | } 54 | } else { 55 | --gGreenLevel; 56 | if (gGreenLevel == 0) { 57 | gGreenDirection = true; 58 | } 59 | } 60 | if (!gRedDirection) { 61 | --gRedLevel; 62 | if (gRedLevel == 0) { 63 | gRedDirection = true; 64 | } 65 | } else { 66 | ++gRedLevel; 67 | if (gRedLevel == 0x20) { 68 | gRedDirection = !gRedDirection; 69 | } 70 | } 71 | } else if (gLedMode == 1) { 72 | ++gLedCounter; 73 | if (gLedCounter >= cShiftSpeed) { 74 | gLedCounter = 0; 75 | gLedIndex = ((gLedIndex + 1) & 0x3); 76 | gGreenLevel = ((gLedIndex + 1) & 0x3) * 8; 77 | gOrangeLevel = (gLedIndex & 0x3) * 8; 78 | gRedLevel = ((gLedIndex + 2) & 0x3) * 8; 79 | } 80 | } else if (gLedMode == 2) { 81 | ++gLedCounter; 82 | if (gLedCounter >= cShiftSpeed) { 83 | --gLedIndex; 84 | gLedIndex &= 0x3; 85 | gLedCounter = 0; 86 | gOrangeLevel = (gLedIndex & 0x3) * 8; 87 | gGreenLevel = ((gLedIndex + 1) & 0x3) * 8; 88 | gRedLevel = ((gLedIndex + 2) & 0x3) * 8; 89 | } 90 | } else if (gLedMode == 3) { 91 | ++gLedCounter; 92 | if (gLedCounter >= cBlinkSpeed) { 93 | gLedCounter = 0; 94 | if (gOrangeLevel != 0) { 95 | gOrangeLevel = 0; 96 | gGreenLevel = 0; 97 | gRedLevel = 0; 98 | } else { 99 | gOrangeLevel = cMaxLevel; 100 | gGreenLevel = cMaxLevel; 101 | gRedLevel = cMaxLevel; 102 | } 103 | } 104 | } 105 | bool modeNeedsInitialization = false; 106 | const uint8_t pinInput = PINB; 107 | const bool buttonPressA = ((pinInput & 0b00000100) != 0); 108 | if (buttonPressA != gLastButtonA) { 109 | if (!buttonPressA) { 110 | ++gLedMode; 111 | if (gLedMode >= cModeCount) { 112 | gLedMode = 0; 113 | } 114 | modeNeedsInitialization = true; 115 | } 116 | gLastButtonA = buttonPressA; 117 | } 118 | bool buttonPressB = (pinInput & 0b00000010); 119 | if (buttonPressB != gLastButtonB) { 120 | if (!buttonPressB) { 121 | if (gLedMode == 0) { 122 | gLedMode = (cModeCount-1); 123 | } else { 124 | --gLedMode; 125 | } 126 | modeNeedsInitialization = true; 127 | } 128 | gLastButtonB = buttonPressB; 129 | } 130 | if (modeNeedsInitialization) { 131 | if (gLedMode == 0) { 132 | gOrangeLevel = 0x0c; 133 | gGreenLevel = 0x12; 134 | gRedLevel = 0x18; 135 | } else if (gLedMode == 1) { 136 | gLedIndex = 0; 137 | gLedDirection = true; 138 | gOrangeLevel = (gLedIndex & 0x3) * 8; 139 | gGreenLevel = ((gLedIndex + 1) & 0x3) * 8; 140 | gRedLevel = ((gLedIndex + 2) & 0x3) * 8; 141 | gLedCounter = 0; 142 | } else if (gLedMode == 2) { 143 | gLedIndex = 0; 144 | gLedDirection = false; 145 | gOrangeLevel = (gLedIndex & 0x3) * 8; 146 | gGreenLevel = ((gLedIndex + 1) & 0x3) * 8; 147 | gRedLevel = ((gLedIndex + 2) & 0x3) * 8; 148 | gLedCounter = 0; 149 | } 150 | } 151 | delay(50); 152 | } 153 | 154 | ISR(TIMER2_OVF_vect) 155 | { 156 | ++gFadeCounter; 157 | gFadeCounter &= 0x1f; 158 | uint8_t mask = 0; 159 | if (gFadeCounter < gOrangeLevel) { 160 | mask |= 0b100000; 161 | } 162 | if (gFadeCounter < gGreenLevel) { 163 | mask |= 0b010000; 164 | } 165 | if (gFadeCounter < gRedLevel) { 166 | mask |= 0b001000; 167 | } 168 | PORTB = ((PORTB & ~0b111000) | mask); 169 | } 170 | -------------------------------------------------------------------------------- /fade_demo_02/Display.cpp: -------------------------------------------------------------------------------- 1 | #include "Display.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Display { 7 | 8 | 9 | const uint8_t cMaxLevel = 0x20; 10 | 11 | volatile uint8_t gFadeCounter = 0; 12 | volatile uint8_t gOrangeLevel = 0x0c; 13 | volatile uint8_t gGreenLevel = 0x12; 14 | volatile uint8_t gRedLevel = 0x18; 15 | 16 | 17 | void initialize() 18 | { 19 | // Initialize Timer 20 | TCCR2A = 0; 21 | TCCR2B = 1; 22 | OCR2A = 0; 23 | OCR2B = 0; 24 | TIMSK2 = _BV(TOIE2); 25 | // Ports 26 | DDRB |= 0b00111000; 27 | PORTB &= ~0b00111000; 28 | } 29 | 30 | 31 | uint8_t getMaximumLevel() 32 | { 33 | return cMaxLevel; 34 | } 35 | 36 | 37 | uint8_t getLevel(Color color) 38 | { 39 | switch (color) { 40 | case Color::Orange: return gOrangeLevel; 41 | case Color::Green: return gGreenLevel; 42 | case Color::Red: return gRedLevel; 43 | } 44 | return 0; 45 | } 46 | 47 | 48 | void setLevel(Color color, uint8_t level) 49 | { 50 | switch (color) { 51 | case Color::Orange: gOrangeLevel = level; break; 52 | case Color::Green: gGreenLevel = level; break; 53 | case Color::Red: gRedLevel = level; break; 54 | } 55 | } 56 | 57 | 58 | inline void timerInterrupt() 59 | { 60 | ++gFadeCounter; 61 | gFadeCounter &= 0x1f; 62 | uint8_t mask = 0; 63 | if (gFadeCounter < gOrangeLevel) { 64 | mask |= 0b100000; 65 | } 66 | if (gFadeCounter < gGreenLevel) { 67 | mask |= 0b010000; 68 | } 69 | if (gFadeCounter < gRedLevel) { 70 | mask |= 0b001000; 71 | } 72 | PORTB = ((PORTB & ~0b111000) | mask); 73 | } 74 | 75 | 76 | } 77 | 78 | 79 | ISR(TIMER2_OVF_vect) 80 | { 81 | Display::timerInterrupt(); 82 | } 83 | -------------------------------------------------------------------------------- /fade_demo_02/Display.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The display module to control the attached LEDs 9 | /// 10 | namespace Display { 11 | 12 | 13 | /// The color of the LED 14 | /// 15 | enum class Color : uint8_t { 16 | Orange = 0, 17 | Green = 1, 18 | Red = 2 19 | }; 20 | 21 | /// Initialize the display module. 22 | /// 23 | void initialize(); 24 | 25 | /// Get the maximum level. 26 | /// 27 | /// @return The maximum level value. 28 | /// 29 | uint8_t getMaximumLevel(); 30 | 31 | /// Get the level of the givem LED. 32 | /// 33 | /// @param color The color of the LED to retrieve. 34 | /// 35 | uint8_t getLevel(Color color); 36 | 37 | /// Set the level of the given LED. 38 | /// 39 | /// @param color The color of the LED to change. 40 | /// @param level The level for the LED. 41 | /// 42 | void setLevel(Color color, uint8_t level); 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /fade_demo_02/fade_demo_02.ino: -------------------------------------------------------------------------------- 1 | // Warning: This is a code example for the article "guideline for modular firmware". 2 | // It is code in a transition phase, do not use it as it is! 3 | 4 | #include "Display.hpp" 5 | 6 | const uint8_t cModeCount = 4; 7 | const uint8_t cShiftSpeed = 8; 8 | const uint8_t cBlinkSpeed = 10; 9 | uint8_t gLedMode = 0; 10 | uint8_t gLedIndex = 0; 11 | uint8_t gLedCounter = 0; 12 | bool gLedDirection = false; 13 | bool gLastButtonA = true; 14 | bool gLastButtonB = true; 15 | bool gOrangeDirection = true; 16 | bool gGreenDirection = true; 17 | bool gRedDirection = true; 18 | 19 | void setup() { 20 | Display::initialize(); 21 | // Ports 22 | DDRB &= ~0b00000110; 23 | PORTB |= 0b00000110; 24 | } 25 | 26 | void loop() { 27 | if (gLedMode == 0) { 28 | if (gOrangeDirection) { 29 | auto level = Display::getLevel(Display::Color::Orange); 30 | ++level; 31 | Display::setLevel(Display::Color::Orange, level); 32 | if (level == Display::getMaximumLevel()) { 33 | gOrangeDirection = false; 34 | } 35 | } else { 36 | auto level = Display::getLevel(Display::Color::Orange); 37 | --level; 38 | Display::setLevel(Display::Color::Orange, level); 39 | if (level == 0) { 40 | gOrangeDirection = true; 41 | } 42 | } 43 | if (gGreenDirection) { 44 | auto level = Display::getLevel(Display::Color::Green); 45 | ++level; 46 | Display::setLevel(Display::Color::Green, level); 47 | if (level == Display::getMaximumLevel()) { 48 | gGreenDirection = false; 49 | } 50 | } else { 51 | auto level = Display::getLevel(Display::Color::Green); 52 | --level; 53 | Display::setLevel(Display::Color::Green, level); 54 | if (level == 0) { 55 | gGreenDirection = true; 56 | } 57 | } 58 | if (!gRedDirection) { 59 | auto level = Display::getLevel(Display::Color::Red); 60 | --level; 61 | Display::setLevel(Display::Color::Red, level); 62 | if (level == 0) { 63 | gRedDirection = true; 64 | } 65 | } else { 66 | auto level = Display::getLevel(Display::Color::Red); 67 | ++level; 68 | Display::setLevel(Display::Color::Red, level); 69 | if (level == Display::getMaximumLevel()) { 70 | gRedDirection = !gRedDirection; 71 | } 72 | } 73 | } else if (gLedMode == 1) { 74 | ++gLedCounter; 75 | if (gLedCounter >= cShiftSpeed) { 76 | gLedCounter = 0; 77 | gLedIndex = ((gLedIndex + 1) & 0x3); 78 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 79 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 80 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 81 | } 82 | } else if (gLedMode == 2) { 83 | ++gLedCounter; 84 | if (gLedCounter >= cShiftSpeed) { 85 | --gLedIndex; 86 | gLedIndex &= 0x3; 87 | gLedCounter = 0; 88 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 89 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 90 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 91 | } 92 | } else if (gLedMode == 3) { 93 | ++gLedCounter; 94 | if (gLedCounter >= cBlinkSpeed) { 95 | gLedCounter = 0; 96 | auto orangeLevel = Display::getLevel(Display::Color::Orange); 97 | if (orangeLevel != 0) { 98 | Display::setLevel(Display::Color::Orange, 0); 99 | Display::setLevel(Display::Color::Green, 0); 100 | Display::setLevel(Display::Color::Red, 0); 101 | } else { 102 | Display::setLevel(Display::Color::Orange, Display::getMaximumLevel()); 103 | Display::setLevel(Display::Color::Green, Display::getMaximumLevel()); 104 | Display::setLevel(Display::Color::Red, Display::getMaximumLevel()); 105 | } 106 | } 107 | } 108 | bool modeNeedsInitialization = false; 109 | const uint8_t pinInput = PINB; 110 | const bool buttonPressA = ((pinInput & 0b00000100) != 0); 111 | if (buttonPressA != gLastButtonA) { 112 | if (!buttonPressA) { 113 | ++gLedMode; 114 | if (gLedMode >= cModeCount) { 115 | gLedMode = 0; 116 | } 117 | modeNeedsInitialization = true; 118 | } 119 | gLastButtonA = buttonPressA; 120 | } 121 | bool buttonPressB = (pinInput & 0b00000010); 122 | if (buttonPressB != gLastButtonB) { 123 | if (!buttonPressB) { 124 | if (gLedMode == 0) { 125 | gLedMode = (cModeCount-1); 126 | } else { 127 | --gLedMode; 128 | } 129 | modeNeedsInitialization = true; 130 | } 131 | gLastButtonB = buttonPressB; 132 | } 133 | if (modeNeedsInitialization) { 134 | if (gLedMode == 0) { 135 | Display::setLevel(Display::Color::Orange, 0x0c); 136 | Display::setLevel(Display::Color::Green, 0x12); 137 | Display::setLevel(Display::Color::Red, 0x18); 138 | } else if (gLedMode == 1) { 139 | gLedIndex = 0; 140 | gLedDirection = true; 141 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 142 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 143 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 144 | gLedCounter = 0; 145 | } else if (gLedMode == 2) { 146 | gLedIndex = 0; 147 | gLedDirection = false; 148 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 149 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 150 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 151 | gLedCounter = 0; 152 | } 153 | } 154 | delay(50); 155 | } 156 | -------------------------------------------------------------------------------- /fade_demo_03/Buttons.cpp: -------------------------------------------------------------------------------- 1 | #include "Buttons.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Buttons { 7 | 8 | 9 | bool gLastButtonA = true; 10 | bool gLastButtonB = true; 11 | Function gPlusCallback = nullptr; 12 | Function gMinusCallback = nullptr; 13 | 14 | 15 | void initialize() 16 | { 17 | DDRB &= ~0b00000110; 18 | PORTB |= 0b00000110; 19 | } 20 | 21 | 22 | void setCallback(Button button, Function fn) 23 | { 24 | switch (button) { 25 | case Plus: gPlusCallback = fn; break; 26 | case Minus: gMinusCallback = fn; break; 27 | default: break; 28 | } 29 | } 30 | 31 | 32 | void poll() 33 | { 34 | Button pressedButton = NoButton; 35 | const uint8_t pinInput = PINB; 36 | const bool buttonPressA = ((pinInput & 0b00000100) != 0); 37 | if (buttonPressA != gLastButtonA) { 38 | if (!buttonPressA) { 39 | pressedButton = Plus; 40 | } 41 | gLastButtonA = buttonPressA; 42 | } 43 | bool buttonPressB = (pinInput & 0b00000010); 44 | if (buttonPressB != gLastButtonB) { 45 | if (!buttonPressB) { 46 | pressedButton = Minus; 47 | } 48 | gLastButtonB = buttonPressB; 49 | } 50 | switch (pressedButton) { 51 | case Plus: gPlusCallback(); break; 52 | case Minus: gMinusCallback(); break; 53 | default: break; 54 | } 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /fade_demo_03/Buttons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Warning: This is a code example for the article "guideline for modular firmware". 6 | // It is code in a transition phase, do not use it as it is! 7 | 8 | /// A module to handle button presses. 9 | /// 10 | namespace Buttons { 11 | 12 | /// The callback function. 13 | /// 14 | typedef void (*Function)(); 15 | 16 | /// The button. 17 | /// 18 | enum Button : uint8_t { 19 | NoButton = 0, 20 | Plus = 1, 21 | Minus = 2 22 | }; 23 | 24 | 25 | /// Initialize the buttons module. 26 | /// 27 | void initialize(); 28 | 29 | /// Set a callback if the given button is pressed. 30 | /// 31 | void setCallback(Button button, Function fn); 32 | 33 | /// Poll the button states. 34 | /// 35 | void poll(); 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /fade_demo_03/Display.cpp: -------------------------------------------------------------------------------- 1 | #include "Display.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Display { 7 | 8 | 9 | const uint8_t cMaxLevel = 0x20; 10 | 11 | volatile uint8_t gFadeCounter = 0; 12 | volatile uint8_t gOrangeLevel = 0x0c; 13 | volatile uint8_t gGreenLevel = 0x12; 14 | volatile uint8_t gRedLevel = 0x18; 15 | 16 | 17 | void initialize() 18 | { 19 | // Initialize Timer 20 | TCCR2A = 0; 21 | TCCR2B = 1; 22 | OCR2A = 0; 23 | OCR2B = 0; 24 | TIMSK2 = _BV(TOIE2); 25 | // Ports 26 | DDRB |= 0b00111000; 27 | PORTB &= ~0b00111000; 28 | } 29 | 30 | 31 | uint8_t getMaximumLevel() 32 | { 33 | return cMaxLevel; 34 | } 35 | 36 | 37 | uint8_t getLevel(Color color) 38 | { 39 | switch (color) { 40 | case Color::Orange: return gOrangeLevel; 41 | case Color::Green: return gGreenLevel; 42 | case Color::Red: return gRedLevel; 43 | } 44 | return 0; 45 | } 46 | 47 | 48 | void setLevel(Color color, uint8_t level) 49 | { 50 | switch (color) { 51 | case Color::Orange: gOrangeLevel = level; break; 52 | case Color::Green: gGreenLevel = level; break; 53 | case Color::Red: gRedLevel = level; break; 54 | } 55 | } 56 | 57 | 58 | inline void timerInterrupt() 59 | { 60 | ++gFadeCounter; 61 | gFadeCounter &= 0x1f; 62 | uint8_t mask = 0; 63 | if (gFadeCounter < gOrangeLevel) { 64 | mask |= 0b100000; 65 | } 66 | if (gFadeCounter < gGreenLevel) { 67 | mask |= 0b010000; 68 | } 69 | if (gFadeCounter < gRedLevel) { 70 | mask |= 0b001000; 71 | } 72 | PORTB = ((PORTB & ~0b111000) | mask); 73 | } 74 | 75 | 76 | } 77 | 78 | 79 | ISR(TIMER2_OVF_vect) 80 | { 81 | Display::timerInterrupt(); 82 | } 83 | -------------------------------------------------------------------------------- /fade_demo_03/Display.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The display module to control the attached LEDs 9 | /// 10 | namespace Display { 11 | 12 | 13 | /// The color of the LED 14 | /// 15 | enum class Color : uint8_t { 16 | Orange = 0, 17 | Green = 1, 18 | Red = 2 19 | }; 20 | 21 | /// Initialize the display module. 22 | /// 23 | void initialize(); 24 | 25 | /// Get the maximum level. 26 | /// 27 | /// @return The maximum level value. 28 | /// 29 | uint8_t getMaximumLevel(); 30 | 31 | /// Get the level of the givem LED. 32 | /// 33 | /// @param color The color of the LED to retrieve. 34 | /// 35 | uint8_t getLevel(Color color); 36 | 37 | /// Set the level of the given LED. 38 | /// 39 | /// @param color The color of the LED to change. 40 | /// @param level The level for the LED. 41 | /// 42 | void setLevel(Color color, uint8_t level); 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /fade_demo_03/fade_demo_03.ino: -------------------------------------------------------------------------------- 1 | // Warning: This is a code example for the article "guideline for modular firmware". 2 | // It is code in a transition phase, do not use it as it is! 3 | 4 | #include "Display.hpp" 5 | #include "Buttons.hpp" 6 | 7 | const uint8_t cModeCount = 4; 8 | const uint8_t cShiftSpeed = 8; 9 | const uint8_t cBlinkSpeed = 10; 10 | uint8_t gLedMode = 0; 11 | uint8_t gLedIndex = 0; 12 | uint8_t gLedCounter = 0; 13 | bool gLedDirection = false; 14 | bool gOrangeDirection = true; 15 | bool gGreenDirection = true; 16 | bool gRedDirection = true; 17 | 18 | void onPlusPressed(); 19 | void onMinusPressed(); 20 | 21 | void setup() { 22 | Display::initialize(); 23 | Buttons::initialize(); 24 | Buttons::setCallback(Buttons::Plus, &onPlusPressed); 25 | Buttons::setCallback(Buttons::Minus, &onMinusPressed); 26 | } 27 | 28 | void loop() { 29 | if (gLedMode == 0) { 30 | if (gOrangeDirection) { 31 | auto level = Display::getLevel(Display::Color::Orange); 32 | ++level; 33 | Display::setLevel(Display::Color::Orange, level); 34 | if (level == Display::getMaximumLevel()) { 35 | gOrangeDirection = false; 36 | } 37 | } else { 38 | auto level = Display::getLevel(Display::Color::Orange); 39 | --level; 40 | Display::setLevel(Display::Color::Orange, level); 41 | if (level == 0) { 42 | gOrangeDirection = true; 43 | } 44 | } 45 | if (gGreenDirection) { 46 | auto level = Display::getLevel(Display::Color::Green); 47 | ++level; 48 | Display::setLevel(Display::Color::Green, level); 49 | if (level == Display::getMaximumLevel()) { 50 | gGreenDirection = false; 51 | } 52 | } else { 53 | auto level = Display::getLevel(Display::Color::Green); 54 | --level; 55 | Display::setLevel(Display::Color::Green, level); 56 | if (level == 0) { 57 | gGreenDirection = true; 58 | } 59 | } 60 | if (!gRedDirection) { 61 | auto level = Display::getLevel(Display::Color::Red); 62 | --level; 63 | Display::setLevel(Display::Color::Red, level); 64 | if (level == 0) { 65 | gRedDirection = true; 66 | } 67 | } else { 68 | auto level = Display::getLevel(Display::Color::Red); 69 | ++level; 70 | Display::setLevel(Display::Color::Red, level); 71 | if (level == Display::getMaximumLevel()) { 72 | gRedDirection = !gRedDirection; 73 | } 74 | } 75 | } else if (gLedMode == 1) { 76 | ++gLedCounter; 77 | if (gLedCounter >= cShiftSpeed) { 78 | gLedCounter = 0; 79 | gLedIndex = ((gLedIndex + 1) & 0x3); 80 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 81 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 82 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 83 | } 84 | } else if (gLedMode == 2) { 85 | ++gLedCounter; 86 | if (gLedCounter >= cShiftSpeed) { 87 | --gLedIndex; 88 | gLedIndex &= 0x3; 89 | gLedCounter = 0; 90 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 91 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 92 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 93 | } 94 | } else if (gLedMode == 3) { 95 | ++gLedCounter; 96 | if (gLedCounter >= cBlinkSpeed) { 97 | gLedCounter = 0; 98 | auto orangeLevel = Display::getLevel(Display::Color::Orange); 99 | if (orangeLevel != 0) { 100 | Display::setLevel(Display::Color::Orange, 0); 101 | Display::setLevel(Display::Color::Green, 0); 102 | Display::setLevel(Display::Color::Red, 0); 103 | } else { 104 | Display::setLevel(Display::Color::Orange, Display::getMaximumLevel()); 105 | Display::setLevel(Display::Color::Green, Display::getMaximumLevel()); 106 | Display::setLevel(Display::Color::Red, Display::getMaximumLevel()); 107 | } 108 | } 109 | } 110 | Buttons::poll(); 111 | delay(50); 112 | } 113 | 114 | 115 | void initializeMode() 116 | { 117 | if (gLedMode == 0) { 118 | Display::setLevel(Display::Color::Orange, 0x0c); 119 | Display::setLevel(Display::Color::Green, 0x12); 120 | Display::setLevel(Display::Color::Red, 0x18); 121 | } else if (gLedMode == 1) { 122 | gLedIndex = 0; 123 | gLedDirection = true; 124 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 125 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 126 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 127 | gLedCounter = 0; 128 | } else if (gLedMode == 2) { 129 | gLedIndex = 0; 130 | gLedDirection = false; 131 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 132 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 133 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 134 | gLedCounter = 0; 135 | } 136 | } 137 | 138 | 139 | void onPlusPressed() 140 | { 141 | ++gLedMode; 142 | if (gLedMode >= cModeCount) { 143 | gLedMode = 0; 144 | } 145 | initializeMode(); 146 | } 147 | 148 | 149 | void onMinusPressed() 150 | { 151 | if (gLedMode == 0) { 152 | gLedMode = (cModeCount-1); 153 | } else { 154 | --gLedMode; 155 | } 156 | initializeMode(); 157 | } 158 | -------------------------------------------------------------------------------- /fade_demo_04/Animation.cpp: -------------------------------------------------------------------------------- 1 | #include "Animation.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include "Display.hpp" 7 | 8 | 9 | namespace Animation { 10 | 11 | 12 | const uint8_t cModeCount = 4; 13 | const uint8_t cShiftSpeed = 8; 14 | const uint8_t cBlinkSpeed = 10; 15 | Mode gLedMode = Mode::Fade; 16 | uint8_t gLedIndex = 0; 17 | uint8_t gLedCounter = 0; 18 | bool gLedDirection = false; 19 | bool gOrangeDirection = true; 20 | bool gGreenDirection = true; 21 | bool gRedDirection = true; 22 | 23 | 24 | void initialize() 25 | { 26 | // nothing to do. 27 | } 28 | 29 | 30 | void initializeMode() 31 | { 32 | if (gLedMode == Mode::Fade) { 33 | Display::setLevel(Display::Color::Orange, 0x0c); 34 | Display::setLevel(Display::Color::Green, 0x12); 35 | Display::setLevel(Display::Color::Red, 0x18); 36 | } else if (gLedMode == Mode::RollLeft) { 37 | gLedIndex = 0; 38 | gLedDirection = true; 39 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 40 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 41 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 42 | gLedCounter = 0; 43 | } else if (gLedMode == Mode::RollRight) { 44 | gLedIndex = 0; 45 | gLedDirection = false; 46 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 47 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 48 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 49 | gLedCounter = 0; 50 | } 51 | } 52 | 53 | 54 | void setMode(Mode mode) 55 | { 56 | if (gLedMode != mode) { 57 | gLedMode = mode; 58 | initializeMode(); 59 | } 60 | } 61 | 62 | 63 | void cycleMode(CycleDirection cycleDirection) 64 | { 65 | uint8_t mode = static_cast(gLedMode); 66 | if (cycleDirection == Next) { 67 | ++mode; 68 | } else { 69 | --mode; 70 | } 71 | mode &= 0x3; 72 | setMode(static_cast(mode)); 73 | } 74 | 75 | 76 | void progress() 77 | { 78 | if (gLedMode == Mode::Fade) { 79 | if (gOrangeDirection) { 80 | auto level = Display::getLevel(Display::Color::Orange); 81 | ++level; 82 | Display::setLevel(Display::Color::Orange, level); 83 | if (level == Display::getMaximumLevel()) { 84 | gOrangeDirection = false; 85 | } 86 | } else { 87 | auto level = Display::getLevel(Display::Color::Orange); 88 | --level; 89 | Display::setLevel(Display::Color::Orange, level); 90 | if (level == 0) { 91 | gOrangeDirection = true; 92 | } 93 | } 94 | if (gGreenDirection) { 95 | auto level = Display::getLevel(Display::Color::Green); 96 | ++level; 97 | Display::setLevel(Display::Color::Green, level); 98 | if (level == Display::getMaximumLevel()) { 99 | gGreenDirection = false; 100 | } 101 | } else { 102 | auto level = Display::getLevel(Display::Color::Green); 103 | --level; 104 | Display::setLevel(Display::Color::Green, level); 105 | if (level == 0) { 106 | gGreenDirection = true; 107 | } 108 | } 109 | if (!gRedDirection) { 110 | auto level = Display::getLevel(Display::Color::Red); 111 | --level; 112 | Display::setLevel(Display::Color::Red, level); 113 | if (level == 0) { 114 | gRedDirection = true; 115 | } 116 | } else { 117 | auto level = Display::getLevel(Display::Color::Red); 118 | ++level; 119 | Display::setLevel(Display::Color::Red, level); 120 | if (level == Display::getMaximumLevel()) { 121 | gRedDirection = !gRedDirection; 122 | } 123 | } 124 | } else if (gLedMode == Mode::RollRight) { 125 | ++gLedCounter; 126 | if (gLedCounter >= cShiftSpeed) { 127 | gLedCounter = 0; 128 | gLedIndex = ((gLedIndex + 1) & 0x3); 129 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 130 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 131 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 132 | } 133 | } else if (gLedMode == Mode::RollLeft) { 134 | ++gLedCounter; 135 | if (gLedCounter >= cShiftSpeed) { 136 | --gLedIndex; 137 | gLedIndex &= 0x3; 138 | gLedCounter = 0; 139 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 140 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 141 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 142 | } 143 | } else if (gLedMode == Mode::Blink) { 144 | ++gLedCounter; 145 | if (gLedCounter >= cBlinkSpeed) { 146 | gLedCounter = 0; 147 | auto orangeLevel = Display::getLevel(Display::Color::Orange); 148 | if (orangeLevel != 0) { 149 | Display::setLevel(Display::Color::Orange, 0); 150 | Display::setLevel(Display::Color::Green, 0); 151 | Display::setLevel(Display::Color::Red, 0); 152 | } else { 153 | Display::setLevel(Display::Color::Orange, Display::getMaximumLevel()); 154 | Display::setLevel(Display::Color::Green, Display::getMaximumLevel()); 155 | Display::setLevel(Display::Color::Red, Display::getMaximumLevel()); 156 | } 157 | } 158 | } 159 | } 160 | 161 | 162 | } 163 | -------------------------------------------------------------------------------- /fade_demo_04/Animation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The module to animate the LEDs 9 | /// 10 | namespace Animation { 11 | 12 | 13 | /// The animation mode. 14 | /// 15 | enum class Mode : uint8_t { 16 | Fade = 0, 17 | RollRight = 1, 18 | RollLeft = 2, 19 | Blink = 3 20 | }; 21 | 22 | 23 | /// The Cycle Direction. 24 | /// 25 | enum CycleDirection : uint8_t { 26 | Next, 27 | Previous 28 | }; 29 | 30 | 31 | /// Initialize the animation module. 32 | /// 33 | void initialize(); 34 | 35 | /// Set the current animation mode. 36 | /// 37 | void setMode(Mode mode); 38 | 39 | /// Cycle the current mode. 40 | /// 41 | void cycleMode(CycleDirection cycleDirection); 42 | 43 | /// Get the current animation mode. 44 | /// 45 | Mode getMode(); 46 | 47 | /// Progress the current animation. 48 | /// 49 | void progress(); 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /fade_demo_04/Buttons.cpp: -------------------------------------------------------------------------------- 1 | #include "Buttons.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Buttons { 7 | 8 | 9 | bool gLastButtonA = true; 10 | bool gLastButtonB = true; 11 | Function gPlusCallback = nullptr; 12 | Function gMinusCallback = nullptr; 13 | 14 | 15 | void initialize() 16 | { 17 | DDRB &= ~0b00000110; 18 | PORTB |= 0b00000110; 19 | } 20 | 21 | 22 | void setCallback(Button button, Function fn) 23 | { 24 | switch (button) { 25 | case Plus: gPlusCallback = fn; break; 26 | case Minus: gMinusCallback = fn; break; 27 | default: break; 28 | } 29 | } 30 | 31 | 32 | void poll() 33 | { 34 | Button pressedButton = NoButton; 35 | const uint8_t pinInput = PINB; 36 | const bool buttonPressA = ((pinInput & 0b00000100) != 0); 37 | if (buttonPressA != gLastButtonA) { 38 | if (!buttonPressA) { 39 | pressedButton = Plus; 40 | } 41 | gLastButtonA = buttonPressA; 42 | } 43 | bool buttonPressB = (pinInput & 0b00000010); 44 | if (buttonPressB != gLastButtonB) { 45 | if (!buttonPressB) { 46 | pressedButton = Minus; 47 | } 48 | gLastButtonB = buttonPressB; 49 | } 50 | switch (pressedButton) { 51 | case Plus: gPlusCallback(); break; 52 | case Minus: gMinusCallback(); break; 53 | default: break; 54 | } 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /fade_demo_04/Buttons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Warning: This is a code example for the article "guideline for modular firmware". 6 | // It is code in a transition phase, do not use it as it is! 7 | 8 | /// A module to handle button presses. 9 | /// 10 | namespace Buttons { 11 | 12 | /// The callback function. 13 | /// 14 | typedef void (*Function)(); 15 | 16 | /// The button. 17 | /// 18 | enum Button : uint8_t { 19 | NoButton = 0, 20 | Plus = 1, 21 | Minus = 2 22 | }; 23 | 24 | 25 | /// Initialize the buttons module. 26 | /// 27 | void initialize(); 28 | 29 | /// Set a callback if the given button is pressed. 30 | /// 31 | void setCallback(Button button, Function fn); 32 | 33 | /// Poll the button states. 34 | /// 35 | void poll(); 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /fade_demo_04/Display.cpp: -------------------------------------------------------------------------------- 1 | #include "Display.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Display { 7 | 8 | 9 | const uint8_t cMaxLevel = 0x20; 10 | 11 | volatile uint8_t gFadeCounter = 0; 12 | volatile uint8_t gOrangeLevel = 0x0c; 13 | volatile uint8_t gGreenLevel = 0x12; 14 | volatile uint8_t gRedLevel = 0x18; 15 | 16 | 17 | void initialize() 18 | { 19 | // Initialize Timer 20 | TCCR2A = 0; 21 | TCCR2B = 1; 22 | OCR2A = 0; 23 | OCR2B = 0; 24 | TIMSK2 = _BV(TOIE2); 25 | // Ports 26 | DDRB |= 0b00111000; 27 | PORTB &= ~0b00111000; 28 | } 29 | 30 | 31 | uint8_t getMaximumLevel() 32 | { 33 | return cMaxLevel; 34 | } 35 | 36 | 37 | uint8_t getLevel(Color color) 38 | { 39 | switch (color) { 40 | case Color::Orange: return gOrangeLevel; 41 | case Color::Green: return gGreenLevel; 42 | case Color::Red: return gRedLevel; 43 | } 44 | return 0; 45 | } 46 | 47 | 48 | void setLevel(Color color, uint8_t level) 49 | { 50 | switch (color) { 51 | case Color::Orange: gOrangeLevel = level; break; 52 | case Color::Green: gGreenLevel = level; break; 53 | case Color::Red: gRedLevel = level; break; 54 | } 55 | } 56 | 57 | 58 | inline void timerInterrupt() 59 | { 60 | ++gFadeCounter; 61 | gFadeCounter &= 0x1f; 62 | uint8_t mask = 0; 63 | if (gFadeCounter < gOrangeLevel) { 64 | mask |= 0b100000; 65 | } 66 | if (gFadeCounter < gGreenLevel) { 67 | mask |= 0b010000; 68 | } 69 | if (gFadeCounter < gRedLevel) { 70 | mask |= 0b001000; 71 | } 72 | PORTB = ((PORTB & ~0b111000) | mask); 73 | } 74 | 75 | 76 | } 77 | 78 | 79 | ISR(TIMER2_OVF_vect) 80 | { 81 | Display::timerInterrupt(); 82 | } 83 | -------------------------------------------------------------------------------- /fade_demo_04/Display.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The display module to control the attached LEDs 9 | /// 10 | namespace Display { 11 | 12 | 13 | /// The color of the LED 14 | /// 15 | enum class Color : uint8_t { 16 | Orange = 0, 17 | Green = 1, 18 | Red = 2 19 | }; 20 | 21 | /// Initialize the display module. 22 | /// 23 | void initialize(); 24 | 25 | /// Get the maximum level. 26 | /// 27 | /// @return The maximum level value. 28 | /// 29 | uint8_t getMaximumLevel(); 30 | 31 | /// Get the level of the givem LED. 32 | /// 33 | /// @param color The color of the LED to retrieve. 34 | /// 35 | uint8_t getLevel(Color color); 36 | 37 | /// Set the level of the given LED. 38 | /// 39 | /// @param color The color of the LED to change. 40 | /// @param level The level for the LED. 41 | /// 42 | void setLevel(Color color, uint8_t level); 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /fade_demo_04/fade_demo_04.ino: -------------------------------------------------------------------------------- 1 | // Warning: This is a code example for the article "guideline for modular firmware". 2 | // It is code in a transition phase, do not use it as it is! 3 | 4 | #include "Display.hpp" 5 | #include "Buttons.hpp" 6 | #include "Animation.hpp" 7 | 8 | 9 | void onPlusPressed(); 10 | void onMinusPressed(); 11 | 12 | 13 | void setup() { 14 | Display::initialize(); 15 | Buttons::initialize(); 16 | Buttons::setCallback(Buttons::Plus, &onPlusPressed); 17 | Buttons::setCallback(Buttons::Minus, &onMinusPressed); 18 | Animation::initialize(); 19 | } 20 | 21 | void loop() { 22 | Animation::progress(); 23 | Buttons::poll(); 24 | delay(50); 25 | } 26 | 27 | 28 | void onPlusPressed() 29 | { 30 | Animation::cycleMode(Animation::Next); 31 | } 32 | 33 | 34 | void onMinusPressed() 35 | { 36 | Animation::cycleMode(Animation::Previous); 37 | } 38 | -------------------------------------------------------------------------------- /fade_demo_05/Animation.cpp: -------------------------------------------------------------------------------- 1 | #include "Animation.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include "Display.hpp" 7 | 8 | 9 | namespace Animation { 10 | 11 | 12 | const uint8_t cModeCount = 4; 13 | const uint8_t cShiftSpeed = 8; 14 | const uint8_t cBlinkSpeed = 10; 15 | Mode gLedMode = Mode::Fade; 16 | uint8_t gLedIndex = 0; 17 | uint8_t gLedCounter = 0; 18 | bool gLedDirection = false; 19 | bool gOrangeDirection = true; 20 | bool gGreenDirection = true; 21 | bool gRedDirection = true; 22 | 23 | 24 | void initialize() 25 | { 26 | // nothing to do. 27 | } 28 | 29 | 30 | void initializeMode() 31 | { 32 | if (gLedMode == Mode::Fade) { 33 | Display::setLevel(Display::Color::Orange, 0x0c); 34 | Display::setLevel(Display::Color::Green, 0x12); 35 | Display::setLevel(Display::Color::Red, 0x18); 36 | } else if (gLedMode == Mode::RollLeft) { 37 | gLedIndex = 0; 38 | gLedDirection = true; 39 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 40 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 41 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 42 | gLedCounter = 0; 43 | } else if (gLedMode == Mode::RollRight) { 44 | gLedIndex = 0; 45 | gLedDirection = false; 46 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 47 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 48 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 49 | gLedCounter = 0; 50 | } 51 | } 52 | 53 | 54 | void setMode(Mode mode) 55 | { 56 | if (gLedMode != mode) { 57 | gLedMode = mode; 58 | initializeMode(); 59 | } 60 | } 61 | 62 | 63 | void cycleMode(CycleDirection cycleDirection) 64 | { 65 | uint8_t mode = static_cast(gLedMode); 66 | if (cycleDirection == Next) { 67 | ++mode; 68 | } else { 69 | --mode; 70 | } 71 | mode &= 0x3; 72 | setMode(static_cast(mode)); 73 | } 74 | 75 | 76 | void progress() 77 | { 78 | if (gLedMode == Mode::Fade) { 79 | if (gOrangeDirection) { 80 | auto level = Display::getLevel(Display::Color::Orange); 81 | ++level; 82 | Display::setLevel(Display::Color::Orange, level); 83 | if (level == Display::getMaximumLevel()) { 84 | gOrangeDirection = false; 85 | } 86 | } else { 87 | auto level = Display::getLevel(Display::Color::Orange); 88 | --level; 89 | Display::setLevel(Display::Color::Orange, level); 90 | if (level == 0) { 91 | gOrangeDirection = true; 92 | } 93 | } 94 | if (gGreenDirection) { 95 | auto level = Display::getLevel(Display::Color::Green); 96 | ++level; 97 | Display::setLevel(Display::Color::Green, level); 98 | if (level == Display::getMaximumLevel()) { 99 | gGreenDirection = false; 100 | } 101 | } else { 102 | auto level = Display::getLevel(Display::Color::Green); 103 | --level; 104 | Display::setLevel(Display::Color::Green, level); 105 | if (level == 0) { 106 | gGreenDirection = true; 107 | } 108 | } 109 | if (!gRedDirection) { 110 | auto level = Display::getLevel(Display::Color::Red); 111 | --level; 112 | Display::setLevel(Display::Color::Red, level); 113 | if (level == 0) { 114 | gRedDirection = true; 115 | } 116 | } else { 117 | auto level = Display::getLevel(Display::Color::Red); 118 | ++level; 119 | Display::setLevel(Display::Color::Red, level); 120 | if (level == Display::getMaximumLevel()) { 121 | gRedDirection = !gRedDirection; 122 | } 123 | } 124 | } else if (gLedMode == Mode::RollRight) { 125 | ++gLedCounter; 126 | if (gLedCounter >= cShiftSpeed) { 127 | gLedCounter = 0; 128 | gLedIndex = ((gLedIndex + 1) & 0x3); 129 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 130 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 131 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 132 | } 133 | } else if (gLedMode == Mode::RollLeft) { 134 | ++gLedCounter; 135 | if (gLedCounter >= cShiftSpeed) { 136 | --gLedIndex; 137 | gLedIndex &= 0x3; 138 | gLedCounter = 0; 139 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 140 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 141 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 142 | } 143 | } else if (gLedMode == Mode::Blink) { 144 | ++gLedCounter; 145 | if (gLedCounter >= cBlinkSpeed) { 146 | gLedCounter = 0; 147 | auto orangeLevel = Display::getLevel(Display::Color::Orange); 148 | if (orangeLevel != 0) { 149 | Display::setLevel(Display::Color::Orange, 0); 150 | Display::setLevel(Display::Color::Green, 0); 151 | Display::setLevel(Display::Color::Red, 0); 152 | } else { 153 | Display::setLevel(Display::Color::Orange, Display::getMaximumLevel()); 154 | Display::setLevel(Display::Color::Green, Display::getMaximumLevel()); 155 | Display::setLevel(Display::Color::Red, Display::getMaximumLevel()); 156 | } 157 | } 158 | } 159 | } 160 | 161 | 162 | } 163 | -------------------------------------------------------------------------------- /fade_demo_05/Animation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The module to animate the LEDs 9 | /// 10 | namespace Animation { 11 | 12 | 13 | /// The animation mode. 14 | /// 15 | enum class Mode : uint8_t { 16 | Fade = 0, 17 | RollRight = 1, 18 | RollLeft = 2, 19 | Blink = 3 20 | }; 21 | 22 | 23 | /// The Cycle Direction. 24 | /// 25 | enum CycleDirection : uint8_t { 26 | Next, 27 | Previous 28 | }; 29 | 30 | 31 | /// Initialize the animation module. 32 | /// 33 | void initialize(); 34 | 35 | /// Set the current animation mode. 36 | /// 37 | void setMode(Mode mode); 38 | 39 | /// Cycle the current mode. 40 | /// 41 | void cycleMode(CycleDirection cycleDirection); 42 | 43 | /// Get the current animation mode. 44 | /// 45 | Mode getMode(); 46 | 47 | /// Progress the current animation. 48 | /// 49 | void progress(); 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /fade_demo_05/Buttons.cpp: -------------------------------------------------------------------------------- 1 | #include "Buttons.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Buttons { 7 | 8 | 9 | bool gLastButtonA = true; 10 | bool gLastButtonB = true; 11 | Function gPlusCallback = nullptr; 12 | Function gMinusCallback = nullptr; 13 | 14 | 15 | void initialize() 16 | { 17 | DDRB &= ~0b00000110; 18 | PORTB |= 0b00000110; 19 | } 20 | 21 | 22 | void setCallback(Button button, Function fn) 23 | { 24 | switch (button) { 25 | case Plus: gPlusCallback = fn; break; 26 | case Minus: gMinusCallback = fn; break; 27 | default: break; 28 | } 29 | } 30 | 31 | 32 | void poll() 33 | { 34 | Button pressedButton = NoButton; 35 | const uint8_t pinInput = PINB; 36 | const bool buttonPressA = ((pinInput & 0b00000100) != 0); 37 | if (buttonPressA != gLastButtonA) { 38 | if (!buttonPressA) { 39 | pressedButton = Plus; 40 | } 41 | gLastButtonA = buttonPressA; 42 | } 43 | bool buttonPressB = (pinInput & 0b00000010); 44 | if (buttonPressB != gLastButtonB) { 45 | if (!buttonPressB) { 46 | pressedButton = Minus; 47 | } 48 | gLastButtonB = buttonPressB; 49 | } 50 | switch (pressedButton) { 51 | case Plus: gPlusCallback(); break; 52 | case Minus: gMinusCallback(); break; 53 | default: break; 54 | } 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /fade_demo_05/Buttons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Warning: This is a code example for the article "guideline for modular firmware". 6 | // It is code in a transition phase, do not use it as it is! 7 | 8 | /// A module to handle button presses. 9 | /// 10 | namespace Buttons { 11 | 12 | /// The callback function. 13 | /// 14 | typedef void (*Function)(); 15 | 16 | /// The button. 17 | /// 18 | enum Button : uint8_t { 19 | NoButton = 0, 20 | Plus = 1, 21 | Minus = 2 22 | }; 23 | 24 | 25 | /// Initialize the buttons module. 26 | /// 27 | void initialize(); 28 | 29 | /// Set a callback if the given button is pressed. 30 | /// 31 | void setCallback(Button button, Function fn); 32 | 33 | /// Poll the button states. 34 | /// 35 | void poll(); 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /fade_demo_05/Display.cpp: -------------------------------------------------------------------------------- 1 | #include "Display.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Display { 7 | 8 | 9 | const uint8_t cMaximumLevel = 0x20; 10 | const uint8_t cLedCount = 3; 11 | const uint8_t cOrangeMask = 0b00001000u; 12 | const uint8_t cGreenMask = 0b00010000u; 13 | const uint8_t cRedMask = 0b00100000u; 14 | const uint8_t cAllLedsMask = cOrangeMask|cGreenMask|cRedMask; 15 | const uint8_t cLedMask[cLedCount] = {cOrangeMask, cGreenMask, cRedMask}; 16 | 17 | volatile uint8_t gFadeCounter = 0; 18 | volatile uint8_t gLevel[cLedCount]; 19 | 20 | 21 | void initialize() 22 | { 23 | // Initialize the time and enable the interrupt. 24 | TCCR2A = 0; 25 | TCCR2B = 1; 26 | OCR2A = 0; 27 | OCR2B = 0; 28 | TIMSK2 = _BV(TOIE2); 29 | // Set all LED pins in output mode. 30 | DDRB |= cAllLedsMask; 31 | PORTB &= ~cAllLedsMask; 32 | // Set all levels to zero. 33 | memset(gLevel, 0, sizeof gLevel); 34 | } 35 | 36 | 37 | uint8_t getMaximumLevel() 38 | { 39 | return cMaximumLevel; 40 | } 41 | 42 | 43 | uint8_t getLevel(Color color) 44 | { 45 | return gLevel[static_cast(color)]; 46 | } 47 | 48 | 49 | void setLevel(Color color, uint8_t level) 50 | { 51 | gLevel[static_cast(color)] = level; 52 | } 53 | 54 | 55 | inline void timerInterrupt() 56 | { 57 | ++gFadeCounter; 58 | gFadeCounter &= 0x1f; 59 | uint8_t mask = 0; 60 | for (uint8_t i = 0; i < cLedCount; ++i) { 61 | if (gFadeCounter < gLevel[i]) { 62 | mask |= cLedMask[i]; 63 | } 64 | } 65 | PORTB = ((PORTB & ~cAllLedsMask) | mask); 66 | } 67 | 68 | 69 | } 70 | 71 | 72 | ISR(TIMER2_OVF_vect) 73 | { 74 | Display::timerInterrupt(); 75 | } 76 | -------------------------------------------------------------------------------- /fade_demo_05/Display.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The display module to control the attached LEDs 9 | /// 10 | namespace Display { 11 | 12 | 13 | /// The color of the LED 14 | /// 15 | enum class Color : uint8_t { 16 | Orange = 0, 17 | Green = 1, 18 | Red = 2 19 | }; 20 | 21 | /// Initialize the display module. 22 | /// 23 | void initialize(); 24 | 25 | /// Get the maximum level. 26 | /// 27 | /// @return The maximum level value. 28 | /// 29 | uint8_t getMaximumLevel(); 30 | 31 | /// Get the level of the givem LED. 32 | /// 33 | /// @param color The color of the LED to retrieve. 34 | /// 35 | uint8_t getLevel(Color color); 36 | 37 | /// Set the level of the given LED. 38 | /// 39 | /// @param color The color of the LED to change. 40 | /// @param level The level for the LED. 41 | /// 42 | void setLevel(Color color, uint8_t level); 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /fade_demo_05/fade_demo_05.ino: -------------------------------------------------------------------------------- 1 | // Warning: This is a code example for the article "guideline for modular firmware". 2 | // It is code in a transition phase, do not use it as it is! 3 | 4 | #include "Display.hpp" 5 | #include "Buttons.hpp" 6 | #include "Animation.hpp" 7 | 8 | 9 | void onPlusPressed(); 10 | void onMinusPressed(); 11 | 12 | 13 | void setup() { 14 | Display::initialize(); 15 | Buttons::initialize(); 16 | Buttons::setCallback(Buttons::Plus, &onPlusPressed); 17 | Buttons::setCallback(Buttons::Minus, &onMinusPressed); 18 | Animation::initialize(); 19 | } 20 | 21 | void loop() { 22 | Animation::progress(); 23 | Buttons::poll(); 24 | delay(50); 25 | } 26 | 27 | 28 | void onPlusPressed() 29 | { 30 | Animation::cycleMode(Animation::Next); 31 | } 32 | 33 | 34 | void onMinusPressed() 35 | { 36 | Animation::cycleMode(Animation::Previous); 37 | } 38 | -------------------------------------------------------------------------------- /fade_demo_06/Animation.cpp: -------------------------------------------------------------------------------- 1 | #include "Animation.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include "Display.hpp" 7 | 8 | 9 | namespace Animation { 10 | 11 | 12 | const uint8_t cModeCount = 4; 13 | const uint8_t cShiftSpeed = 8; 14 | const uint8_t cBlinkSpeed = 10; 15 | Mode gLedMode = Mode::Fade; 16 | uint8_t gLedIndex = 0; 17 | uint8_t gLedCounter = 0; 18 | bool gLedDirection = false; 19 | bool gOrangeDirection = true; 20 | bool gGreenDirection = true; 21 | bool gRedDirection = true; 22 | 23 | 24 | void initialize() 25 | { 26 | // nothing to do. 27 | } 28 | 29 | 30 | void initializeMode() 31 | { 32 | if (gLedMode == Mode::Fade) { 33 | Display::setLevel(Display::Color::Orange, 0x0c); 34 | Display::setLevel(Display::Color::Green, 0x12); 35 | Display::setLevel(Display::Color::Red, 0x18); 36 | } else if (gLedMode == Mode::RollLeft) { 37 | gLedIndex = 0; 38 | gLedDirection = true; 39 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 40 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 41 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 42 | gLedCounter = 0; 43 | } else if (gLedMode == Mode::RollRight) { 44 | gLedIndex = 0; 45 | gLedDirection = false; 46 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 47 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 48 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 49 | gLedCounter = 0; 50 | } 51 | } 52 | 53 | 54 | void setMode(Mode mode) 55 | { 56 | if (gLedMode != mode) { 57 | gLedMode = mode; 58 | initializeMode(); 59 | } 60 | } 61 | 62 | 63 | void cycleMode(CycleDirection cycleDirection) 64 | { 65 | uint8_t mode = static_cast(gLedMode); 66 | if (cycleDirection == Next) { 67 | ++mode; 68 | } else { 69 | --mode; 70 | } 71 | mode &= 0x3; 72 | setMode(static_cast(mode)); 73 | } 74 | 75 | 76 | void progress() 77 | { 78 | if (gLedMode == Mode::Fade) { 79 | if (gOrangeDirection) { 80 | auto level = Display::getLevel(Display::Color::Orange); 81 | ++level; 82 | Display::setLevel(Display::Color::Orange, level); 83 | if (level == Display::getMaximumLevel()) { 84 | gOrangeDirection = false; 85 | } 86 | } else { 87 | auto level = Display::getLevel(Display::Color::Orange); 88 | --level; 89 | Display::setLevel(Display::Color::Orange, level); 90 | if (level == 0) { 91 | gOrangeDirection = true; 92 | } 93 | } 94 | if (gGreenDirection) { 95 | auto level = Display::getLevel(Display::Color::Green); 96 | ++level; 97 | Display::setLevel(Display::Color::Green, level); 98 | if (level == Display::getMaximumLevel()) { 99 | gGreenDirection = false; 100 | } 101 | } else { 102 | auto level = Display::getLevel(Display::Color::Green); 103 | --level; 104 | Display::setLevel(Display::Color::Green, level); 105 | if (level == 0) { 106 | gGreenDirection = true; 107 | } 108 | } 109 | if (!gRedDirection) { 110 | auto level = Display::getLevel(Display::Color::Red); 111 | --level; 112 | Display::setLevel(Display::Color::Red, level); 113 | if (level == 0) { 114 | gRedDirection = true; 115 | } 116 | } else { 117 | auto level = Display::getLevel(Display::Color::Red); 118 | ++level; 119 | Display::setLevel(Display::Color::Red, level); 120 | if (level == Display::getMaximumLevel()) { 121 | gRedDirection = !gRedDirection; 122 | } 123 | } 124 | } else if (gLedMode == Mode::RollRight) { 125 | ++gLedCounter; 126 | if (gLedCounter >= cShiftSpeed) { 127 | gLedCounter = 0; 128 | gLedIndex = ((gLedIndex + 1) & 0x3); 129 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 130 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 131 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 132 | } 133 | } else if (gLedMode == Mode::RollLeft) { 134 | ++gLedCounter; 135 | if (gLedCounter >= cShiftSpeed) { 136 | --gLedIndex; 137 | gLedIndex &= 0x3; 138 | gLedCounter = 0; 139 | Display::setLevel(Display::Color::Orange, (gLedIndex & 0x3) * 8); 140 | Display::setLevel(Display::Color::Green, ((gLedIndex + 1) & 0x3) * 8); 141 | Display::setLevel(Display::Color::Red, ((gLedIndex + 2) & 0x3) * 8); 142 | } 143 | } else if (gLedMode == Mode::Blink) { 144 | ++gLedCounter; 145 | if (gLedCounter >= cBlinkSpeed) { 146 | gLedCounter = 0; 147 | auto orangeLevel = Display::getLevel(Display::Color::Orange); 148 | if (orangeLevel != 0) { 149 | Display::setLevel(Display::Color::Orange, 0); 150 | Display::setLevel(Display::Color::Green, 0); 151 | Display::setLevel(Display::Color::Red, 0); 152 | } else { 153 | Display::setLevel(Display::Color::Orange, Display::getMaximumLevel()); 154 | Display::setLevel(Display::Color::Green, Display::getMaximumLevel()); 155 | Display::setLevel(Display::Color::Red, Display::getMaximumLevel()); 156 | } 157 | } 158 | } 159 | } 160 | 161 | 162 | } 163 | -------------------------------------------------------------------------------- /fade_demo_06/Animation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The module to animate the LEDs 9 | /// 10 | namespace Animation { 11 | 12 | 13 | /// The animation mode. 14 | /// 15 | enum class Mode : uint8_t { 16 | Fade = 0, 17 | RollRight = 1, 18 | RollLeft = 2, 19 | Blink = 3 20 | }; 21 | 22 | 23 | /// The Cycle Direction. 24 | /// 25 | enum CycleDirection : uint8_t { 26 | Next, 27 | Previous 28 | }; 29 | 30 | 31 | /// Initialize the animation module. 32 | /// 33 | void initialize(); 34 | 35 | /// Set the current animation mode. 36 | /// 37 | void setMode(Mode mode); 38 | 39 | /// Cycle the current mode. 40 | /// 41 | void cycleMode(CycleDirection cycleDirection); 42 | 43 | /// Get the current animation mode. 44 | /// 45 | Mode getMode(); 46 | 47 | /// Progress the current animation. 48 | /// 49 | void progress(); 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /fade_demo_06/Buttons.cpp: -------------------------------------------------------------------------------- 1 | #include "Buttons.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Buttons { 7 | 8 | 9 | const uint8_t cButtonCount = 2; 10 | const uint8_t cPlusButtonMask = 0b00000100u; 11 | const uint8_t cMinusButtonMask = 0b00000010u; 12 | const uint8_t cAllButtonMasks = cPlusButtonMask|cMinusButtonMask; 13 | const uint8_t cButtonMask[] = {cPlusButtonMask, cMinusButtonMask}; 14 | const Button cButton[] = {Plus, Minus}; 15 | 16 | bool gLastState[cButtonCount]; 17 | Function gCallback[cButtonCount]; 18 | 19 | 20 | void initialize() 21 | { 22 | // Set all button pins to input with a pull-up 23 | DDRB &= ~cAllButtonMasks; 24 | PORTB |= cAllButtonMasks; 25 | // Set the last state to true. 26 | memset(gLastState, static_cast(true), sizeof gLastState); 27 | // Set all callbacks to `nullptr` 28 | memset(gCallback, 0, sizeof gCallback); 29 | } 30 | 31 | 32 | void setCallback(Button button, Function fn) 33 | { 34 | gCallback[static_cast(button)] = fn; 35 | } 36 | 37 | 38 | void poll() 39 | { 40 | uint8_t pressedButtonIndex = cButtonCount; 41 | const uint8_t pinInput = PINB; 42 | for (uint8_t i = 0; i < cButtonCount; ++i) { 43 | const bool buttonPressed = ((pinInput & cButtonMask[i]) != 0); 44 | if (buttonPressed != gLastState[i]) { 45 | gLastState[i] = buttonPressed; 46 | if (!buttonPressed) { 47 | pressedButtonIndex = i; 48 | } 49 | } 50 | } 51 | if (pressedButtonIndex < cButtonCount) { 52 | if (gCallback[pressedButtonIndex] != nullptr) { 53 | gCallback[pressedButtonIndex](); 54 | } 55 | } 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /fade_demo_06/Buttons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Warning: This is a code example for the article "guideline for modular firmware". 6 | // It is code in a transition phase, do not use it as it is! 7 | 8 | /// A module to handle button presses. 9 | /// 10 | namespace Buttons { 11 | 12 | /// The callback function. 13 | /// 14 | typedef void (*Function)(); 15 | 16 | /// The button. 17 | /// 18 | enum Button : uint8_t { 19 | Plus = 0, 20 | Minus = 1, 21 | }; 22 | 23 | 24 | /// Initialize the buttons module. 25 | /// 26 | void initialize(); 27 | 28 | /// Set a callback if the given button is pressed. 29 | /// 30 | void setCallback(Button button, Function fn); 31 | 32 | /// Poll the button states. 33 | /// 34 | void poll(); 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /fade_demo_06/Display.cpp: -------------------------------------------------------------------------------- 1 | #include "Display.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Display { 7 | 8 | 9 | const uint8_t cMaximumLevel = 0x20; 10 | const uint8_t cLedCount = 3; 11 | const uint8_t cOrangeMask = 0b00001000u; 12 | const uint8_t cGreenMask = 0b00010000u; 13 | const uint8_t cRedMask = 0b00100000u; 14 | const uint8_t cAllLedsMask = cOrangeMask|cGreenMask|cRedMask; 15 | const uint8_t cLedMask[cLedCount] = {cOrangeMask, cGreenMask, cRedMask}; 16 | 17 | volatile uint8_t gFadeCounter = 0; 18 | volatile uint8_t gLevel[cLedCount]; 19 | 20 | 21 | void initialize() 22 | { 23 | // Initialize the time and enable the interrupt. 24 | TCCR2A = 0; 25 | TCCR2B = 1; 26 | OCR2A = 0; 27 | OCR2B = 0; 28 | TIMSK2 = _BV(TOIE2); 29 | // Set all LED pins in output mode. 30 | DDRB |= cAllLedsMask; 31 | PORTB &= ~cAllLedsMask; 32 | // Set all levels to zero. 33 | memset(gLevel, 0, sizeof gLevel); 34 | } 35 | 36 | 37 | uint8_t getMaximumLevel() 38 | { 39 | return cMaximumLevel; 40 | } 41 | 42 | 43 | uint8_t getLevel(Color color) 44 | { 45 | return gLevel[static_cast(color)]; 46 | } 47 | 48 | 49 | void setLevel(Color color, uint8_t level) 50 | { 51 | gLevel[static_cast(color)] = level; 52 | } 53 | 54 | 55 | inline void timerInterrupt() 56 | { 57 | ++gFadeCounter; 58 | gFadeCounter &= 0x1f; 59 | uint8_t mask = 0; 60 | for (uint8_t i = 0; i < cLedCount; ++i) { 61 | if (gFadeCounter < gLevel[i]) { 62 | mask |= cLedMask[i]; 63 | } 64 | } 65 | PORTB = ((PORTB & ~cAllLedsMask) | mask); 66 | } 67 | 68 | 69 | } 70 | 71 | 72 | ISR(TIMER2_OVF_vect) 73 | { 74 | Display::timerInterrupt(); 75 | } 76 | -------------------------------------------------------------------------------- /fade_demo_06/Display.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The display module to control the attached LEDs 9 | /// 10 | namespace Display { 11 | 12 | 13 | /// The color of the LED 14 | /// 15 | enum class Color : uint8_t { 16 | Orange = 0, 17 | Green = 1, 18 | Red = 2 19 | }; 20 | 21 | /// Initialize the display module. 22 | /// 23 | void initialize(); 24 | 25 | /// Get the maximum level. 26 | /// 27 | /// @return The maximum level value. 28 | /// 29 | uint8_t getMaximumLevel(); 30 | 31 | /// Get the level of the givem LED. 32 | /// 33 | /// @param color The color of the LED to retrieve. 34 | /// 35 | uint8_t getLevel(Color color); 36 | 37 | /// Set the level of the given LED. 38 | /// 39 | /// @param color The color of the LED to change. 40 | /// @param level The level for the LED. 41 | /// 42 | void setLevel(Color color, uint8_t level); 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /fade_demo_06/fade_demo_06.ino: -------------------------------------------------------------------------------- 1 | // Warning: This is a code example for the article "guideline for modular firmware". 2 | // It is code in a transition phase, do not use it as it is! 3 | 4 | #include "Display.hpp" 5 | #include "Buttons.hpp" 6 | #include "Animation.hpp" 7 | 8 | 9 | void onPlusPressed(); 10 | void onMinusPressed(); 11 | 12 | 13 | void setup() { 14 | Display::initialize(); 15 | Buttons::initialize(); 16 | Buttons::setCallback(Buttons::Plus, &onPlusPressed); 17 | Buttons::setCallback(Buttons::Minus, &onMinusPressed); 18 | Animation::initialize(); 19 | } 20 | 21 | void loop() { 22 | Animation::progress(); 23 | Buttons::poll(); 24 | delay(50); 25 | } 26 | 27 | 28 | void onPlusPressed() 29 | { 30 | Animation::cycleMode(Animation::Next); 31 | } 32 | 33 | 34 | void onMinusPressed() 35 | { 36 | Animation::cycleMode(Animation::Previous); 37 | } 38 | -------------------------------------------------------------------------------- /fade_demo_07/Animation.cpp: -------------------------------------------------------------------------------- 1 | #include "Animation.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include "Display.hpp" 7 | #include "AnimationMode.hpp" 8 | 9 | 10 | namespace Animation { 11 | 12 | 13 | const uint8_t cModeCount = 4; 14 | 15 | AnimationMode* const gAnimation[] = { 16 | new AnimationModeFade(), 17 | new AnimationModeRoll(AnimationModeRoll::Left), 18 | new AnimationModeRoll(AnimationModeRoll::Right), 19 | new AnimationModeBlink(), 20 | }; 21 | Mode gCurrentMode = Mode::Fade; 22 | 23 | const uint8_t cShiftSpeed = 8; 24 | const uint8_t cBlinkSpeed = 10; 25 | uint8_t gLedIndex = 0; 26 | uint8_t gLedCounter = 0; 27 | bool gLedDirection = false; 28 | bool gOrangeDirection = true; 29 | bool gGreenDirection = true; 30 | bool gRedDirection = true; 31 | 32 | 33 | void initialize() 34 | { 35 | gAnimation[static_cast(gCurrentMode)]->initialize(); 36 | } 37 | 38 | 39 | void setMode(Mode mode) 40 | { 41 | if (gCurrentMode != mode) { 42 | gCurrentMode = mode; 43 | gAnimation[static_cast(gCurrentMode)]->initialize(); 44 | } 45 | } 46 | 47 | 48 | void cycleMode(CycleDirection cycleDirection) 49 | { 50 | uint8_t mode = static_cast(gCurrentMode); 51 | if (cycleDirection == Next) { 52 | ++mode; 53 | } else { 54 | --mode; 55 | } 56 | mode &= 0x3; 57 | setMode(static_cast(mode)); 58 | } 59 | 60 | 61 | void progress() 62 | { 63 | gAnimation[static_cast(gCurrentMode)]->progress(); 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /fade_demo_07/Animation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The module to animate the LEDs 9 | /// 10 | namespace Animation { 11 | 12 | 13 | /// The animation mode. 14 | /// 15 | enum class Mode : uint8_t { 16 | Fade = 0, 17 | RollRight = 1, 18 | RollLeft = 2, 19 | Blink = 3 20 | }; 21 | 22 | 23 | /// The Cycle Direction. 24 | /// 25 | enum CycleDirection : uint8_t { 26 | Next, 27 | Previous 28 | }; 29 | 30 | 31 | /// Initialize the animation module. 32 | /// 33 | void initialize(); 34 | 35 | /// Set the current animation mode. 36 | /// 37 | void setMode(Mode mode); 38 | 39 | /// Cycle the current mode. 40 | /// 41 | void cycleMode(CycleDirection cycleDirection); 42 | 43 | /// Get the current animation mode. 44 | /// 45 | Mode getMode(); 46 | 47 | /// Progress the current animation. 48 | /// 49 | void progress(); 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /fade_demo_07/AnimationMode.cpp: -------------------------------------------------------------------------------- 1 | #include "AnimationMode.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | 7 | #include "Display.hpp" 8 | 9 | 10 | void AnimationModeFade::initialize() 11 | { 12 | _orangeDirection = true; 13 | _greenDirection = true; 14 | _redDirection = true; 15 | Display::setLevel(Display::Color::Orange, 0x0c); 16 | Display::setLevel(Display::Color::Green, 0x12); 17 | Display::setLevel(Display::Color::Red, 0x18); 18 | } 19 | 20 | 21 | void AnimationModeFade::progress() 22 | { 23 | if (_orangeDirection) { 24 | auto level = Display::getLevel(Display::Color::Orange); 25 | ++level; 26 | Display::setLevel(Display::Color::Orange, level); 27 | if (level == Display::getMaximumLevel()) { 28 | _orangeDirection = false; 29 | } 30 | } else { 31 | auto level = Display::getLevel(Display::Color::Orange); 32 | --level; 33 | Display::setLevel(Display::Color::Orange, level); 34 | if (level == 0) { 35 | _orangeDirection = true; 36 | } 37 | } 38 | if (_greenDirection) { 39 | auto level = Display::getLevel(Display::Color::Green); 40 | ++level; 41 | Display::setLevel(Display::Color::Green, level); 42 | if (level == Display::getMaximumLevel()) { 43 | _greenDirection = false; 44 | } 45 | } else { 46 | auto level = Display::getLevel(Display::Color::Green); 47 | --level; 48 | Display::setLevel(Display::Color::Green, level); 49 | if (level == 0) { 50 | _greenDirection = true; 51 | } 52 | } 53 | if (!_redDirection) { 54 | auto level = Display::getLevel(Display::Color::Red); 55 | --level; 56 | Display::setLevel(Display::Color::Red, level); 57 | if (level == 0) { 58 | _redDirection = true; 59 | } 60 | } else { 61 | auto level = Display::getLevel(Display::Color::Red); 62 | ++level; 63 | Display::setLevel(Display::Color::Red, level); 64 | if (level == Display::getMaximumLevel()) { 65 | _redDirection = false; 66 | } 67 | } 68 | } 69 | 70 | 71 | AnimationModeRoll::AnimationModeRoll(Direction direction) 72 | : _direction(direction) 73 | { 74 | } 75 | 76 | 77 | void AnimationModeRoll::initialize() 78 | { 79 | _index = 0; 80 | _counter = 0; 81 | } 82 | 83 | 84 | void AnimationModeRoll::progress() 85 | { 86 | ++_counter; 87 | if (_counter >= _speed) { 88 | _counter = 0; 89 | if (_direction == Left) { 90 | _index += 1; 91 | } else { 92 | _index -= 1; 93 | } 94 | _index &= 0x3; 95 | Display::setLevel(Display::Color::Green, ((_index + 1) & 0x3) * 8); 96 | Display::setLevel(Display::Color::Orange, (_index & 0x3) * 8); 97 | Display::setLevel(Display::Color::Red, ((_index + 2) & 0x3) * 8); 98 | } 99 | } 100 | 101 | 102 | void AnimationModeRoll::updateLeds() 103 | { 104 | Display::setLevel(Display::Color::Orange, (_index & 0x3) * 8); 105 | Display::setLevel(Display::Color::Green, ((_index + 1) & 0x3) * 8); 106 | Display::setLevel(Display::Color::Red, ((_index + 2) & 0x3) * 8); 107 | } 108 | 109 | 110 | void AnimationModeBlink::initialize() 111 | { 112 | _counter = 0; 113 | Display::setLevel(Display::Color::Orange, 0); 114 | Display::setLevel(Display::Color::Green, 0); 115 | Display::setLevel(Display::Color::Red, 0); 116 | } 117 | 118 | 119 | void AnimationModeBlink::progress() 120 | { 121 | ++_counter; 122 | if (_counter >= _speed) { 123 | _counter = 0; 124 | auto orangeLevel = Display::getLevel(Display::Color::Orange); 125 | if (orangeLevel != 0) { 126 | Display::setLevel(Display::Color::Orange, 0); 127 | Display::setLevel(Display::Color::Green, 0); 128 | Display::setLevel(Display::Color::Red, 0); 129 | } else { 130 | Display::setLevel(Display::Color::Orange, Display::getMaximumLevel()); 131 | Display::setLevel(Display::Color::Green, Display::getMaximumLevel()); 132 | Display::setLevel(Display::Color::Red, Display::getMaximumLevel()); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /fade_demo_07/AnimationMode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | class AnimationMode 9 | { 10 | public: 11 | virtual void initialize() = 0; 12 | virtual void progress() = 0; 13 | }; 14 | 15 | 16 | class AnimationModeFade : public AnimationMode 17 | { 18 | public: // Implement AnimationMode 19 | void initialize() override; 20 | void progress() override; 21 | private: 22 | bool _orangeDirection; 23 | bool _greenDirection; 24 | bool _redDirection; 25 | }; 26 | 27 | 28 | class AnimationModeRoll : public AnimationMode 29 | { 30 | public: 31 | enum Direction { Left, Right }; 32 | public: 33 | AnimationModeRoll(Direction direction); 34 | public: // Implement AnimationMode 35 | void initialize() override; 36 | void progress() override; 37 | private: 38 | void updateLeds(); 39 | private: 40 | constexpr static uint8_t _speed = 8; 41 | const Direction _direction; 42 | uint8_t _counter; 43 | uint8_t _index; 44 | }; 45 | 46 | class AnimationModeBlink : public AnimationMode 47 | { 48 | public: // Implement AnimationMode 49 | void initialize() override; 50 | void progress() override; 51 | private: 52 | constexpr static uint8_t _speed = 10; 53 | uint8_t _counter; 54 | }; 55 | -------------------------------------------------------------------------------- /fade_demo_07/Buttons.cpp: -------------------------------------------------------------------------------- 1 | #include "Buttons.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Buttons { 7 | 8 | 9 | const uint8_t cButtonCount = 2; 10 | const uint8_t cPlusButtonMask = 0b00000100u; 11 | const uint8_t cMinusButtonMask = 0b00000010u; 12 | const uint8_t cAllButtonMasks = cPlusButtonMask|cMinusButtonMask; 13 | const uint8_t cButtonMask[] = {cPlusButtonMask, cMinusButtonMask}; 14 | const Button cButton[] = {Plus, Minus}; 15 | 16 | bool gLastState[cButtonCount]; 17 | Function gCallback[cButtonCount]; 18 | 19 | 20 | void initialize() 21 | { 22 | // Set all button pins to input with a pull-up 23 | DDRB &= ~cAllButtonMasks; 24 | PORTB |= cAllButtonMasks; 25 | // Set the last state to true. 26 | memset(gLastState, static_cast(true), sizeof gLastState); 27 | // Set all callbacks to `nullptr` 28 | memset(gCallback, 0, sizeof gCallback); 29 | } 30 | 31 | 32 | void setCallback(Button button, Function fn) 33 | { 34 | gCallback[static_cast(button)] = fn; 35 | } 36 | 37 | 38 | void poll() 39 | { 40 | uint8_t pressedButtonIndex = cButtonCount; 41 | const uint8_t pinInput = PINB; 42 | for (uint8_t i = 0; i < cButtonCount; ++i) { 43 | const bool buttonPressed = ((pinInput & cButtonMask[i]) != 0); 44 | if (buttonPressed != gLastState[i]) { 45 | gLastState[i] = buttonPressed; 46 | if (!buttonPressed) { 47 | pressedButtonIndex = i; 48 | } 49 | } 50 | } 51 | if (pressedButtonIndex < cButtonCount) { 52 | if (gCallback[pressedButtonIndex] != nullptr) { 53 | gCallback[pressedButtonIndex](); 54 | } 55 | } 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /fade_demo_07/Buttons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Warning: This is a code example for the article "guideline for modular firmware". 6 | // It is code in a transition phase, do not use it as it is! 7 | 8 | /// A module to handle button presses. 9 | /// 10 | namespace Buttons { 11 | 12 | /// The callback function. 13 | /// 14 | typedef void (*Function)(); 15 | 16 | /// The button. 17 | /// 18 | enum Button : uint8_t { 19 | Plus = 0, 20 | Minus = 1, 21 | }; 22 | 23 | 24 | /// Initialize the buttons module. 25 | /// 26 | void initialize(); 27 | 28 | /// Set a callback if the given button is pressed. 29 | /// 30 | void setCallback(Button button, Function fn); 31 | 32 | /// Poll the button states. 33 | /// 34 | void poll(); 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /fade_demo_07/Display.cpp: -------------------------------------------------------------------------------- 1 | #include "Display.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Display { 7 | 8 | 9 | const uint8_t cMaximumLevel = 0x20; 10 | const uint8_t cLedCount = 3; 11 | const uint8_t cOrangeMask = 0b00001000u; 12 | const uint8_t cGreenMask = 0b00010000u; 13 | const uint8_t cRedMask = 0b00100000u; 14 | const uint8_t cAllLedsMask = cOrangeMask|cGreenMask|cRedMask; 15 | const uint8_t cLedMask[cLedCount] = {cOrangeMask, cGreenMask, cRedMask}; 16 | 17 | volatile uint8_t gFadeCounter = 0; 18 | volatile uint8_t gLevel[cLedCount]; 19 | 20 | 21 | void initialize() 22 | { 23 | // Initialize the time and enable the interrupt. 24 | TCCR2A = 0; 25 | TCCR2B = 1; 26 | OCR2A = 0; 27 | OCR2B = 0; 28 | TIMSK2 = _BV(TOIE2); 29 | // Set all LED pins in output mode. 30 | DDRB |= cAllLedsMask; 31 | PORTB &= ~cAllLedsMask; 32 | // Set all levels to zero. 33 | memset(gLevel, 0, sizeof gLevel); 34 | } 35 | 36 | 37 | uint8_t getMaximumLevel() 38 | { 39 | return cMaximumLevel; 40 | } 41 | 42 | 43 | uint8_t getLevel(Color color) 44 | { 45 | return gLevel[static_cast(color)]; 46 | } 47 | 48 | 49 | void setLevel(Color color, uint8_t level) 50 | { 51 | gLevel[static_cast(color)] = level; 52 | } 53 | 54 | 55 | inline void timerInterrupt() 56 | { 57 | ++gFadeCounter; 58 | gFadeCounter &= 0x1f; 59 | uint8_t mask = 0; 60 | for (uint8_t i = 0; i < cLedCount; ++i) { 61 | if (gFadeCounter < gLevel[i]) { 62 | mask |= cLedMask[i]; 63 | } 64 | } 65 | PORTB = ((PORTB & ~cAllLedsMask) | mask); 66 | } 67 | 68 | 69 | } 70 | 71 | 72 | ISR(TIMER2_OVF_vect) 73 | { 74 | Display::timerInterrupt(); 75 | } 76 | -------------------------------------------------------------------------------- /fade_demo_07/Display.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The display module to control the attached LEDs 9 | /// 10 | namespace Display { 11 | 12 | 13 | /// The color of the LED 14 | /// 15 | enum class Color : uint8_t { 16 | Orange = 0, 17 | Green = 1, 18 | Red = 2 19 | }; 20 | 21 | /// Initialize the display module. 22 | /// 23 | void initialize(); 24 | 25 | /// Get the maximum level. 26 | /// 27 | /// @return The maximum level value. 28 | /// 29 | uint8_t getMaximumLevel(); 30 | 31 | /// Get the level of the givem LED. 32 | /// 33 | /// @param color The color of the LED to retrieve. 34 | /// 35 | uint8_t getLevel(Color color); 36 | 37 | /// Set the level of the given LED. 38 | /// 39 | /// @param color The color of the LED to change. 40 | /// @param level The level for the LED. 41 | /// 42 | void setLevel(Color color, uint8_t level); 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /fade_demo_07/fade_demo_07.ino: -------------------------------------------------------------------------------- 1 | // Warning: This is a code example for the article "guideline for modular firmware". 2 | // It is code in a transition phase, do not use it as it is! 3 | 4 | #include "Display.hpp" 5 | #include "Buttons.hpp" 6 | #include "Animation.hpp" 7 | 8 | 9 | void onPlusPressed(); 10 | void onMinusPressed(); 11 | 12 | 13 | void setup() { 14 | Display::initialize(); 15 | Buttons::initialize(); 16 | Buttons::setCallback(Buttons::Plus, &onPlusPressed); 17 | Buttons::setCallback(Buttons::Minus, &onMinusPressed); 18 | Animation::initialize(); 19 | } 20 | 21 | void loop() { 22 | Animation::progress(); 23 | Buttons::poll(); 24 | delay(50); 25 | } 26 | 27 | 28 | void onPlusPressed() 29 | { 30 | Animation::cycleMode(Animation::Next); 31 | } 32 | 33 | 34 | void onMinusPressed() 35 | { 36 | Animation::cycleMode(Animation::Previous); 37 | } 38 | -------------------------------------------------------------------------------- /fade_demo_08/Animation.cpp: -------------------------------------------------------------------------------- 1 | #include "Animation.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include "Display.hpp" 7 | #include "AnimationMode.hpp" 8 | 9 | 10 | namespace Animation { 11 | 12 | 13 | const uint8_t cModeCount = 4; 14 | 15 | AnimationMode* const gAnimation[] = { 16 | new AnimationModeFade(), 17 | new AnimationModeRoll(AnimationModeRoll::Left), 18 | new AnimationModeRoll(AnimationModeRoll::Right), 19 | new AnimationModeBlink(), 20 | }; 21 | Mode gCurrentMode = Mode::Fade; 22 | 23 | 24 | void initialize() 25 | { 26 | gAnimation[static_cast(gCurrentMode)]->initialize(); 27 | } 28 | 29 | 30 | void setMode(Mode mode) 31 | { 32 | if (gCurrentMode != mode) { 33 | gCurrentMode = mode; 34 | gAnimation[static_cast(gCurrentMode)]->initialize(); 35 | } 36 | } 37 | 38 | 39 | void cycleMode(CycleDirection cycleDirection) 40 | { 41 | uint8_t mode = static_cast(gCurrentMode); 42 | if (cycleDirection == Next) { 43 | ++mode; 44 | } else { 45 | --mode; 46 | } 47 | mode &= 0x3; 48 | setMode(static_cast(mode)); 49 | } 50 | 51 | 52 | void progress() 53 | { 54 | gAnimation[static_cast(gCurrentMode)]->progress(); 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /fade_demo_08/Animation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The module to animate the LEDs 9 | /// 10 | namespace Animation { 11 | 12 | 13 | /// The animation mode. 14 | /// 15 | enum class Mode : uint8_t { 16 | Fade = 0, 17 | RollRight = 1, 18 | RollLeft = 2, 19 | Blink = 3 20 | }; 21 | 22 | 23 | /// The Cycle Direction. 24 | /// 25 | enum CycleDirection : uint8_t { 26 | Next, 27 | Previous 28 | }; 29 | 30 | 31 | /// Initialize the animation module. 32 | /// 33 | void initialize(); 34 | 35 | /// Set the current animation mode. 36 | /// 37 | void setMode(Mode mode); 38 | 39 | /// Cycle the current mode. 40 | /// 41 | void cycleMode(CycleDirection cycleDirection); 42 | 43 | /// Get the current animation mode. 44 | /// 45 | Mode getMode(); 46 | 47 | /// Progress the current animation. 48 | /// 49 | void progress(); 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /fade_demo_08/AnimationMode.cpp: -------------------------------------------------------------------------------- 1 | #include "AnimationMode.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | 7 | #include "Display.hpp" 8 | 9 | 10 | void AnimationModeFade::initialize() 11 | { 12 | for (uint8_t i = 0; i < 3; ++i) { 13 | _direction[i] = true; 14 | Display::setLevel(i, i*8); 15 | } 16 | } 17 | 18 | 19 | void AnimationModeFade::progress() 20 | { 21 | for (uint8_t i = 0; i < 3; ++i) { 22 | if (_direction[i]) { 23 | auto level = Display::getLevel(i); 24 | ++level; 25 | Display::setLevel(i, level); 26 | if (level == Display::getMaximumLevel()) { 27 | _direction[i] = false; 28 | } 29 | } else { 30 | auto level = Display::getLevel(i); 31 | --level; 32 | Display::setLevel(i, level); 33 | if (level == 0) { 34 | _direction[i] = true; 35 | } 36 | } 37 | } 38 | } 39 | 40 | 41 | AnimationModeRoll::AnimationModeRoll(Direction direction) 42 | : _direction(direction) 43 | { 44 | } 45 | 46 | 47 | void AnimationModeRoll::initialize() 48 | { 49 | _index = 0; 50 | _counter = 0; 51 | } 52 | 53 | 54 | void AnimationModeRoll::progress() 55 | { 56 | ++_counter; 57 | if (_counter >= _speed) { 58 | _counter = 0; 59 | if (_direction == Left) { 60 | _index += 1; 61 | } else { 62 | _index -= 1; 63 | } 64 | _index &= 0x3; 65 | updateLeds(); 66 | } 67 | } 68 | 69 | 70 | void AnimationModeRoll::updateLeds() 71 | { 72 | for (uint8_t i = 0; i < 3; ++i) { 73 | Display::setLevel(i, ((_index + i) & 0x3) * 8); 74 | } 75 | } 76 | 77 | 78 | void AnimationModeBlink::initialize() 79 | { 80 | _counter = 0; 81 | for (uint8_t i = 0; i < 3; ++i) { 82 | Display::setLevel(i, 0); 83 | } 84 | } 85 | 86 | 87 | void AnimationModeBlink::progress() 88 | { 89 | ++_counter; 90 | if (_counter >= _speed) { 91 | _counter = 0; 92 | auto level = Display::getLevel(Display::Color::Orange); 93 | if (level != 0) { 94 | level = 0; 95 | } else { 96 | level = Display::getMaximumLevel(); 97 | } 98 | for (uint8_t i = 0; i < 3; ++i) { 99 | Display::setLevel(i, level); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /fade_demo_08/AnimationMode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | class AnimationMode 9 | { 10 | public: 11 | virtual void initialize() = 0; 12 | virtual void progress() = 0; 13 | }; 14 | 15 | 16 | class AnimationModeFade : public AnimationMode 17 | { 18 | public: // Implement AnimationMode 19 | void initialize() override; 20 | void progress() override; 21 | private: 22 | bool _direction[3]; 23 | }; 24 | 25 | 26 | class AnimationModeRoll : public AnimationMode 27 | { 28 | public: 29 | enum Direction { Left, Right }; 30 | public: 31 | AnimationModeRoll(Direction direction); 32 | public: // Implement AnimationMode 33 | void initialize() override; 34 | void progress() override; 35 | private: 36 | void updateLeds(); 37 | private: 38 | constexpr static uint8_t _speed = 8; 39 | const Direction _direction; 40 | uint8_t _counter; 41 | uint8_t _index; 42 | }; 43 | 44 | class AnimationModeBlink : public AnimationMode 45 | { 46 | public: // Implement AnimationMode 47 | void initialize() override; 48 | void progress() override; 49 | private: 50 | constexpr static uint8_t _speed = 10; 51 | uint8_t _counter; 52 | }; 53 | -------------------------------------------------------------------------------- /fade_demo_08/Buttons.cpp: -------------------------------------------------------------------------------- 1 | #include "Buttons.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Buttons { 7 | 8 | 9 | const uint8_t cButtonCount = 2; 10 | const uint8_t cPlusButtonMask = 0b00000100u; 11 | const uint8_t cMinusButtonMask = 0b00000010u; 12 | const uint8_t cAllButtonMasks = cPlusButtonMask|cMinusButtonMask; 13 | const uint8_t cButtonMask[] = {cPlusButtonMask, cMinusButtonMask}; 14 | const Button cButton[] = {Plus, Minus}; 15 | 16 | bool gLastState[cButtonCount]; 17 | Function gCallback[cButtonCount]; 18 | 19 | 20 | void initialize() 21 | { 22 | // Set all button pins to input with a pull-up 23 | DDRB &= ~cAllButtonMasks; 24 | PORTB |= cAllButtonMasks; 25 | // Set the last state to true. 26 | memset(gLastState, static_cast(true), sizeof gLastState); 27 | // Set all callbacks to `nullptr` 28 | memset(gCallback, 0, sizeof gCallback); 29 | } 30 | 31 | 32 | void setCallback(Button button, Function fn) 33 | { 34 | gCallback[static_cast(button)] = fn; 35 | } 36 | 37 | 38 | void poll() 39 | { 40 | uint8_t pressedButtonIndex = cButtonCount; 41 | const uint8_t pinInput = PINB; 42 | for (uint8_t i = 0; i < cButtonCount; ++i) { 43 | const bool buttonPressed = ((pinInput & cButtonMask[i]) != 0); 44 | if (buttonPressed != gLastState[i]) { 45 | gLastState[i] = buttonPressed; 46 | if (!buttonPressed) { 47 | pressedButtonIndex = i; 48 | } 49 | } 50 | } 51 | if (pressedButtonIndex < cButtonCount) { 52 | if (gCallback[pressedButtonIndex] != nullptr) { 53 | gCallback[pressedButtonIndex](); 54 | } 55 | } 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /fade_demo_08/Buttons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Warning: This is a code example for the article "guideline for modular firmware". 6 | // It is code in a transition phase, do not use it as it is! 7 | 8 | /// A module to handle button presses. 9 | /// 10 | namespace Buttons { 11 | 12 | /// The callback function. 13 | /// 14 | typedef void (*Function)(); 15 | 16 | /// The button. 17 | /// 18 | enum Button : uint8_t { 19 | Plus = 0, 20 | Minus = 1, 21 | }; 22 | 23 | 24 | /// Initialize the buttons module. 25 | /// 26 | void initialize(); 27 | 28 | /// Set a callback if the given button is pressed. 29 | /// 30 | void setCallback(Button button, Function fn); 31 | 32 | /// Poll the button states. 33 | /// 34 | void poll(); 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /fade_demo_08/Display.cpp: -------------------------------------------------------------------------------- 1 | #include "Display.hpp" 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | namespace Display { 7 | 8 | 9 | const uint8_t cMaximumLevel = 0x20; 10 | const uint8_t cLedCount = 3; 11 | const uint8_t cOrangeMask = 0b00001000u; 12 | const uint8_t cGreenMask = 0b00010000u; 13 | const uint8_t cRedMask = 0b00100000u; 14 | const uint8_t cAllLedsMask = cOrangeMask|cGreenMask|cRedMask; 15 | const uint8_t cLedMask[cLedCount] = {cOrangeMask, cGreenMask, cRedMask}; 16 | 17 | volatile uint8_t gFadeCounter = 0; 18 | volatile uint8_t gLevel[cLedCount]; 19 | 20 | 21 | void initialize() 22 | { 23 | // Initialize the time and enable the interrupt. 24 | TCCR2A = 0; 25 | TCCR2B = 1; 26 | OCR2A = 0; 27 | OCR2B = 0; 28 | TIMSK2 = _BV(TOIE2); 29 | // Set all LED pins in output mode. 30 | DDRB |= cAllLedsMask; 31 | PORTB &= ~cAllLedsMask; 32 | // Set all levels to zero. 33 | for (uint8_t i = 0; i < cLedCount; ++i) { 34 | gLevel[i] = 0; 35 | } 36 | } 37 | 38 | 39 | uint8_t getMaximumLevel() 40 | { 41 | return cMaximumLevel; 42 | } 43 | 44 | 45 | uint8_t getLevel(Color color) 46 | { 47 | return getLevel(static_cast(color)); 48 | } 49 | 50 | 51 | uint8_t getLevel(uint8_t index) 52 | { 53 | return gLevel[index]; 54 | } 55 | 56 | 57 | void setLevel(Color color, uint8_t level) 58 | { 59 | setLevel(static_cast(color), level); 60 | } 61 | 62 | 63 | void setLevel(uint8_t index, uint8_t level) 64 | { 65 | gLevel[index] = level; 66 | } 67 | 68 | 69 | inline void timerInterrupt() 70 | { 71 | ++gFadeCounter; 72 | gFadeCounter &= 0x1f; 73 | uint8_t mask = 0; 74 | for (uint8_t i = 0; i < cLedCount; ++i) { 75 | if (gFadeCounter < gLevel[i]) { 76 | mask |= cLedMask[i]; 77 | } 78 | } 79 | PORTB = ((PORTB & ~cAllLedsMask) | mask); 80 | } 81 | 82 | 83 | } 84 | 85 | 86 | ISR(TIMER2_OVF_vect) 87 | { 88 | Display::timerInterrupt(); 89 | } 90 | -------------------------------------------------------------------------------- /fade_demo_08/Display.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Warning: This is a code example for the article "guideline for modular firmware". 4 | // It is code in a transition phase, do not use it as it is! 5 | 6 | #include 7 | 8 | /// The display module to control the attached LEDs 9 | /// 10 | namespace Display { 11 | 12 | 13 | /// The color of the LED 14 | /// 15 | enum class Color : uint8_t { 16 | Orange = 0, 17 | Green = 1, 18 | Red = 2 19 | }; 20 | 21 | /// Initialize the display module. 22 | /// 23 | void initialize(); 24 | 25 | /// Get the maximum level. 26 | /// 27 | /// @return The maximum level value. 28 | /// 29 | uint8_t getMaximumLevel(); 30 | 31 | /// Get the level of the givem LED. 32 | /// 33 | /// @param color The color of the LED to retrieve. 34 | /// 35 | uint8_t getLevel(Color color); 36 | 37 | /// Get the level of the givem LED. 38 | /// 39 | /// @param index The index of the LED to retrieve (0-2). 40 | /// 41 | uint8_t getLevel(uint8_t index); 42 | 43 | /// Set the level of the given LED. 44 | /// 45 | /// @param color The color of the LED to change. 46 | /// @param level The level for the LED. 47 | /// 48 | void setLevel(Color color, uint8_t level); 49 | 50 | /// Set the level of the given LED. 51 | /// 52 | /// @param index The index of the LED to change (0-2). 53 | /// @param level The level for the LED. 54 | /// 55 | void setLevel(uint8_t index, uint8_t level); 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /fade_demo_08/fade_demo_08.ino: -------------------------------------------------------------------------------- 1 | // Warning: This is a code example for the article "guideline for modular firmware". 2 | // It is code in a transition phase, do not use it as it is! 3 | 4 | #include "Display.hpp" 5 | #include "Buttons.hpp" 6 | #include "Animation.hpp" 7 | 8 | 9 | void onPlusPressed(); 10 | void onMinusPressed(); 11 | 12 | 13 | void setup() { 14 | Display::initialize(); 15 | Buttons::initialize(); 16 | Buttons::setCallback(Buttons::Plus, &onPlusPressed); 17 | Buttons::setCallback(Buttons::Minus, &onMinusPressed); 18 | Animation::initialize(); 19 | } 20 | 21 | void loop() { 22 | Animation::progress(); 23 | Buttons::poll(); 24 | delay(50); 25 | } 26 | 27 | 28 | void onPlusPressed() 29 | { 30 | Animation::cycleMode(Animation::Next); 31 | } 32 | 33 | 34 | void onMinusPressed() 35 | { 36 | Animation::cycleMode(Animation::Previous); 37 | } 38 | --------------------------------------------------------------------------------