SimpleCharliePlexer.hpp

/** * SimpleCharliePlexer.hpp * * Klasse SimpleCharliePlexer - Zeigt das Prinzip von CharliePlexing. * * $Id: SimpleCharliePlexer.hpp 15125 2022-04-10 08:05:56Z martin $ * * (C) 2022 by Martin Pischky (martin@pischky.de) * */ #ifndef SIMPLE_CHARLIE_PLEXER_HPP_ #define SIMPLE_CHARLIE_PLEXER_HPP_ #include <cstdint> // uint8_t, uint16_t #include <bitset> // std::bitset using std::uint8_t; using std::bitset; /** * Abstrakte Klasse mit dem Algorithmus für das Charlieplexen. * * Es werden die Anzahl der benutzen Pins (N) und die Anzahl der tatsächlichen * benutzen LED-Positionen (M) angegeben. * * Die LEDs werden über "setStatus" gesteuert. Eine LED die leuchten * soll muss im Parameter gesetzt sein. * * Die Methode "cycle" ist im Hauptprogramm (loop) ohne weitere Verzögerungen * zyklisch aufzurufen. * * Wie funktioniert das: * * Mit N Port-Pins kann man N*(N-1) LEDs ansteuern. Dabei * sind immer zwei Ports mit jeweils L und H anzusteuern so das jeweils nur eine * LED leuchtet. Es sind alle Kombinationen zu durchlaufen, nur müssen die * Ports paarweise verschieden sein. Das ist hier als zwei geschachtelten * FOR-Schleifen implementiert. Bei jedem Durchlauf wird in "status" geschaut ob * die LED einzuschalten wird oder dunkel bleibt. Dann wird dieser Zustand * einige Zeit angezeigt. * * Sind weniger als N*(N-1) anzusteuern können wir die Scheifen auch vorzeitig * abbrechen. Damit sparen wir etwas Zeit und können den Strom durch die LDEs * etwas kleiner wählen. * * Das ganze ist insofern dumm, das durch die delayMicros-Routine alle * Rechenzeit verbraucht wird. Irgendwer muss ja auch noch auf DCC oder * LocoNet-Meldungen regieren und "status" ändern. Sonst ist das angezeigte * Signalbild ziemlich langweilig. * * In der Praxis wird man also die Schleifen aufbrechen müssen und jeweils * nur einen inneren Durchlauf ausführen. Dazu muss man i,j und k global * speichern. Dann kann man die Routine zeitgesteuert regelmäßig aufrufen. * Hierfür bietet sich der Timer-Interrupt an. */ template <uint8_t N, uint8_t M> class SimpleCharliePlexer { // @suppress("Class has a virtual method and non-virtual destructor") static_assert(M <= N*(N-1), "Number of controlled LEDs to high"); public: using LedStatus = bitset<M>; static constexpr uint8_t NUM_PINS = N; static constexpr uint8_t NUM_LEDS = M; void setStatus(const LedStatus& s) { status = s; } const LedStatus& getStatus() const { return status; } void cycle() { uint8_t k = 0; for (uint8_t i = 0; i < N; ++i) { for (uint8_t j = 0; j < N; ++j) { if (i == j) continue; this->setAllTristate(); if (status[k]) { this->setHigh(i); this->setLow(j); } this->delayMicros(10000/M); // Frequenz: 100Hz k++; if (k == M) goto exit_outer_loop; } } exit_outer_loop: this->setAllTristate(); } /** Alle LEDs abschalten */ void setAllTristate() { for (uint8_t i = 0; i < N; ++i) this->setTristate(i); } private: LedStatus status = {0}; protected: /** Port auf TRISTATE schalten */ virtual void setTristate(uint8_t portNr) = 0; /** Port auf HIGH schalten */ virtual void setHigh(uint8_t portNr) = 0; /** Port auf LOW schalten */ virtual void setLow(uint8_t portNr) = 0; /** Verzögerung der Programmausführung um angegebene Microsekunden*/ virtual void delayMicros(uint16_t us) = 0; // eclipse CodAn meckert hier einen fehlenen Destruktor an: //~SimpleCharliePlexer() {}; }; #endif // SIMPLE_CHARLIE_PLEXER_HPP_