
Mein Sohn war fest mit dem
Magnetkonstruktor Magformers verbunden . Nachdem
das Kind eine
Reihe von Fixiks durchgesehen hatte, in denen derselbe Konstruktor vorgestellt wurde, fragte es: "Dad, warum haben Fixics Details, die glänzen, aber wir nicht?"
Es stellte sich heraus, dass es wirklich ein „Magformers Neon LED Set“ gibt, bei dem es neben den üblichen Bausteinen auch ein Element mit einer LED gibt. Da wir zu diesem Zeitpunkt bereits eine ganze Schachtel Magnete in allen möglichen Formen und Größen gesammelt hatten (was mich
betrifft, sind die
chinesischen Magformer dem Original überhaupt nicht unterlegen), wollte ich
irgendwie kein weiteres Set nur für eine Glühbirne kaufen. Darüber hinaus kostete dieses Set deutlich mehr als ein ähnliches Set ohne Hintergrundbeleuchtung.
Nachdem ich geschätzt hatte, dass die Komponenten nur ein paar Dollar enthalten, von denen ich die meisten bereits hatte, beschloss ich, meine Morgulka abzuholen. Ja, und mit Effekten, die das Original nicht hatte.
Unter der Katze finden Sie die Option eines Blinkers am ATTiny85 und des LED-Panels an den WS8212-LEDs. Ich werde über Schaltkreise sprechen, wie ich das Ganze mit der Batterie betrieben habe, sowie über die nicht offensichtlichen Probleme, die ich auf dem Weg gemacht habe. Ich werde auch ausführlich über die Softwarekomponente des Projekts sprechen.
Erste Schritte
Es schien mir, dass das Leuchten einer normalen LED (sogar RGB) langweilig und banal ist. Aber so etwas wie WS8212 zu fühlen, schien interessant. Auf ebee wurden sowohl einzelne LEDs als auch Matrizen mit einer Größe von bis zu 16 x 16 angeboten. Nachdem ich verschiedene Module gekauft hatte, entschied ich mich für eine 4x4 Matrix. Es gibt viele LEDs, um verschiedene visuelle Effekte zu erzielen, während das Modul in seiner Größe mit dem Fenster des quadratischen Blocks des Designers vergleichbar ist.

Zur Steuerung der LED-Matrix reicht nur ein Pin des Mikrocontrollers aus, sodass selbst der Arduino Nano wie eine Büste aussieht (außerdem passt er nicht in das Gehäuse). Der Digispark-Klon auf dem ATTiny85-Controller erwies sich jedoch als genau richtig - er verfügt nicht über viel Speicher und Pins, ist aber mehr als genug für den LED-Blinker. Das Modul lässt sich perfekt in die Arduino IDE integrieren und verfügt über einen USB-Bootloader an Bord, sodass die Programmierung dieses Moduls sehr einfach und komfortabel ist. Ich wollte es schon lange versuchen.
Begonnen mit dem einfachsten Schema.

In dieser Form war es möglich, alle Glow / Blink-Algorithmen (dazu weiter unten) schnell zu debuggen. Ein drahtgebundenes Spielzeug ist jedoch nicht der Fall - Sie müssen über die Batterieleistung nachdenken. Darüber hinaus wurde beschlossen, Lithium zu verwenden, um nicht mit Fingerbatterien bankrott zu gehen (die außerdem nicht in den Umschlag passen). Und da es eine Lithiumbatterie gibt, müssen Sie sich überlegen, wie Sie sie aufladen sollen. In den Behältern haben wir gerade einen
„beliebten“ Laderegler auf dem TP4056-Chip gefunden, der bei dieser Gelegenheit gekauft wurde.
Aber es hat nicht sofort funktioniert. Die Schaltung des Digispark ATTiny85-Moduls ist nicht sehr dafür ausgelegt - sie wird entweder über USB mit Strom versorgt, aber der Strom wird direkt an den Mikrocontroller (über den + 5-Bus) oder über den VIN-Eingang geliefert, aber dann geht der Strom über den linearen Stabilisator 7805. Option, wenn das Lithium-Lademodul Das Einstecken in die Lücke zwischen dem USB-Anschluss und dem Mikrocontroller ist nicht vorgesehen. Ich musste die Schaltung ein wenig modifizieren und die zusätzlichen Details entfernen.

Jetzt wird der VIN-Pin mit USB-Strom versorgt und geht dann zum Ladegerät-Eingang. Der Ladegerätausgang (tatsächlich ist der Akku direkt angeschlossen) geht über den 5-V-Fuß zurück in die Platine. Und obwohl es tatsächlich 3 bis 4,2 V (Batteriespannung) geben wird, ist dies ganz normal - der Betriebsspannungsbereich des Mikrocontrollers beträgt 1,8 bis 5,5 V. Und selbst das LED-Modul arbeitet normal ab 2,7 V, obwohl unter 3,2 V die blaue LED etwas fehlt und die Farben ein wenig in Gelb „schweben“.
Um Energie zu sparen, verschwand auch die ständig leuchtende D2-LED. Das allgemeine Schema sieht jetzt so aus

Es wäre möglich, den Stromkreis über den USB-Anschluss im Ladegerät zu speisen, aber dann würde die Möglichkeit, Firmware über den USB-Anschluss auf der Controller-Karte hochzuladen, verloren gehen. Es wäre möglich, zwei USB-Anschlüsse für verschiedene Zwecke zu belassen - einen zum Aufladen und einen für die Firmware, aber das ist irgendwie falsch.
Ich habe bei ebay einen Akku der Größe 6x25x35 gekauft, der sich jedoch als defekt herausstellte oder durch einen Kurzschluss oder einen großen Ladestrom abgeschaltet wurde (standardmäßig ist der Ladestrom auf 1A eingestellt und Sie müssen einen Widerstand löten, um den Strom zu reduzieren). In jedem Fall fiel die Spannung an der Batterie auf 1 V ab, wenn die Last angeschlossen wurde, selbst bei 10 mA. Zum Zeitpunkt des Tests habe ich von einem kleinen Quadrocopter auf einen halb leeren LiPo-Akku umgestellt. Wenig später bestellte ich die Batterie bei einem anderen Verkäufer und es stellte sich heraus, dass sie gut war.
Im Prinzip wäre es möglich, hier anzuhalten, die Verbindungsdrähte zu löten und alles vorsichtig in eine Art Gehäuse zu schieben, aber ich entschied mich, den Verbrauch der Schaltung zu messen. Und dann habe ich geweint. Nun, im funktionierenden Zustand (wenn die Glühbirnen voll leuchten) frisst dieses Ding bis zu 130 mA, also beträgt der Verbrauch in Ruhe mehr als 25 mA! Das heißt, Dieser Blinker verbraucht meine 600mAh Batterie in weniger als einem Tag!
Es stellte sich heraus, dass etwa 10 mA LEDs verbrauchen. Auch wenn sie nicht aufleuchten, arbeitet in jedem ein Mikrocontroller und wartet auf einen Befehl. Das heißt, Sie müssen eine Ausschaltschaltung für die LEDs erstellen.
Die restlichen 15 mA werden vom Mikrocontroller verbraucht. Ja, es kann ins Bett gebracht werden und laut Datenblatt wird der Verbrauch mit Mikroampere gemessen, aber tatsächlich war es nicht möglich, weniger als 1 mA zu erhalten. Ich schaltete den ADC aus und übersetzte die Pins in Eingang. Es scheint, dass irgendwo in der Schaltung eine Art Leck vorhanden ist, aber meine bescheidenen Kenntnisse der Elektronik reichen nicht aus, um es zu finden und zu verstehen.
Wir erschweren das Schema
Dann erinnerte ich mich, dass ich einen PT1502-Chip für einen Test gekauft hatte. Dieser Chip ist ein Lithium-Batterieladecontroller mit einem Netzteil mit mehreren Steuereingängen. Die einzige Schwierigkeit besteht darin, dass die Mikroschaltung in einem 4x4 mm QFN20-Gehäuse geliefert wird und einige Umreifungen erfordert. Das Löten zu Hause ist schwierig, aber möglich. Die Gebühr ist für eine reguläre LUT schwierig und muss bei den Chinesen bestellt werden. Aber wir haben keine Angst vor Schwierigkeiten, oder?
In mehreren Feldern kann das Schema wie folgt beschrieben werden.

Im ausgeschalteten Zustand werden die Steuerung und die LEDs nicht mit Strom versorgt. Das Gerät verfügt über eine Ein- / Aus-Taste, die den Blinker einschaltet (es wechselt auch den Modus). Die LED leuchtet beispielsweise eine Minute lang, und wenn keine Benutzeraktivität vorliegt (niemand drückt eine Taste), schaltet sich das Gerät aus. Das heißt, Es geht nicht nur schlafen, sondern schaltet sich durch das Power Hold-Signal aus. Und es schaltet alles auf einmal aus - sowohl den Mikrocontroller als auch die LEDs. Die Ein- und Ausschaltfunktion ist im PT1502-Chip implementiert
Sie müssen nur noch einen Schaltplan zeichnen und eine Leiterplatte erstellen. Die Schaltung besteht größtenteils aus dem Datenblatt PT1502 sowie dem Digispark ATTiny85-Modul. Die Mikroschaltung des Leistungsreglers PT1502 ist funktional in mehrere Teile unterteilt, daher ist sie in Blöcke in der Schaltung unterteilt.

Dies ist in der Tat ein Lithium-Batterieladecontroller mit einem eigenen Kabelbaum. LED1 zeigt den Ladezustand an - dann ist die Ladung eingeschaltet. Der Widerstand R6 stellt den Ladestrom auf 470 mA ein. Da ich eine 600-mAh-Batterie habe, können Sie im Prinzip den Strom erhöhen und einen Widerstand bei 780-800 Ohm bis zu 600 mA einstellen. Ich bin mir jedoch nicht sicher über die besondere Qualität meines Akkus - es ist besser, langsamer zu laden, aber es hält länger.
Betrachten Sie einen Energieplan

Die SW1-Taste startet das gesamte System - der PT1502-Chip wacht von selbst auf und startet dann alle Stromquellen (von denen er 3 hat). Wenn die Stromversorgung installiert ist, startet die Mikroschaltung den Mikrocontroller, indem sie das RESET-Signal freigibt. Zur Vereinfachung des Debuggens habe ich außerdem eine separate Schaltfläche zum Zurücksetzen hinzugefügt.
Das HOLD-Signal wird verwendet, um das gesamte System auszuschalten. Wenn der Mikrocontroller startet, sollte er das Gerät auf diese Leitung stellen. Wenn es Zeit zum Abrunden ist, setzt der Mikrocontroller auf der HOLD-Leitung Null und der PT1502-Leistungschip stoppt alle Stromquellen.
Es wäre möglich, die niedrige Batterieladung mithilfe des BAT_LOW-Ausgangs zu verfolgen, aber in diesem Artikel habe ich sie bewertet. Sie müssen keine Daten speichern, und nichts explodiert, wenn Sie nicht rechtzeitig eine leere Batterie bemerken. Stirbt so stirbt. Aber nur für den Fall, dass der Vorstand den Kontakt für dieses Geschäft bereitstellte.
Kehren wir für eine Sekunde zur SW1-Taste zurück. Ich habe beschlossen, keine 2 separaten Tasten zum Einschalten und Steuern zu erstellen. Daher ist dieselbe Taste auch mit dem ATTiny85 verbunden und schaltet während des Betriebs die Blinkmodi um. Die Werte des Teilers R7-R8 werden so gewählt, dass der Port des PB2-Mikrocontrollers nicht verbrannt wird. Für alle Batteriespannungsbereiche (3,3 - 4,2 V) wird die Spannung innerhalb der angegebenen Datenblattgrenzen (0,7 * VCC - VCC + 0,5 V) an den Fuß des Controllers geliefert.
Betrachten Sie eine Stromquelle

Dies ist ein gepulster DC-DC-Wandler. Die Ausgangsspannung wird durch die Widerstände R10-R11 eingestellt und gemäß der Formel aus dem Datenblatt auf 3,3 V eingestellt. Alles andere ist ein einfaches Umschnallen.
Eine solche ausgetrickste Stromquelle wird für immer nicht wirklich benötigt - es wäre möglich, den Mikrocontroller im Allgemeinen direkt über die Batterie mit Strom zu versorgen. Es ist nur so, dass diese Quelle bereits im PT1502-Chip implementiert ist und bei Bedarf ein- und ausgeschaltet werden kann - warum nicht?

Der Chip hat auch 2 lineare Stabilisatoren, aber ich werde sie nicht verwenden. Wie sich herausstellte, ist es leider immer noch notwendig, die Eingangsspannung an diese Quelle zu liefern, andernfalls glaubt die Mikroschaltung, dass die Leistung immer noch nicht stabil genug ist und startet den Mikrocontroller nicht (dieses Wissen wurde mir durch eine Woche Löten der Testplatine hin und her vermittelt - ich konnte nicht verstehen, warum es nicht funktioniert )
Kommen wir zum logischen Teil.

Das USB-Kabel wird unverändert von der Digispark-Karte geläppt. Dies ist erforderlich, um die USB-Spannung (die 3,3 V beträgt) und die Signale des Mikrocontrollers (der im Original mit 5 V betrieben wird) zu koordinieren. Da in meinem Fall der Mikrocontroller auch mit 3,3 V betrieben wird, könnte die Schaltung vereinfacht werden, aber nur für den Fall, dass ich die ursprüngliche Schaltung auf der Platine geschieden habe.

An der Bindung des Mikrocontrollers ist nichts Interessantes.
Der letzte Schliff ist der Stecker

Tatsächlich habe ich mir auf ATTiny85 mit USB-Unterstützung und einem Leistungsregler mit Lithiumbatterie ein solches Debugging-Board besorgt. Daher habe ich mich nicht darauf beschränkt, nur die Leitung an die LED auszugeben. Stattdessen habe ich alle Leitungen des Mikrocontrollers zum Kamm gebracht - gleichzeitig ist es bequem, eine Verbindung zum Programmierer herzustellen.
Und lassen Sie in Zukunft fast alle Leitungen fest an eine bestimmte Funktionalität gebunden sein (PB1 - Leitung halten, PB2 - Netzschalter, PB3 / PB4 - USB, PB5 - Zurücksetzen), wird es möglich sein, einige Grenzen zu umgehen. Löten Sie beispielsweise das USB-Kabel nicht und lassen Sie die PB3 / PB4-Leitungen los. Oder lehnen Sie beispielsweise einen Reset ab und geben Sie PB5 frei. In der Zwischenzeit bleibt nur PB0 frei - und schließen Sie unsere LED daran an.
Wir gehen an die Tafel. Angesichts der Einschränkungen hinsichtlich der Größe der Platine in 40 x 40 mm, der Anzahl der Komponenten und des QFN20-Gehäuses des PT1502-Chips habe ich nicht einmal daran gedacht, die Platine zu Hause herzustellen. Deshalb habe ich sofort angefangen, das kompakteste Zweischichtbrett zu züchten. Das habe ich bekommen

Zur Vereinfachung der Verwendung habe ich auf der Rückseite alle möglichen Ausgabefunktionen signiert (die Idee kam vom Digispark-Board).

Ich habe das
Board bei
JLCPCB bestellt . Um ehrlich zu sein, bin ich mit der Qualität nicht sehr zufrieden. Wenn Sie den Chip mehrmals löten, ist die Maske in der Nähe der kleinen Kontakte des PT1502 etwas getrübt. Nun, kleine Inschriften schwebten ein wenig. Wenn jedoch beim ersten Mal alles gelötet wird, dann die Normen.
Zum Löten von QFN20 benötigen Sie einen Lötkolben, alles andere kann mit einem bestimmten Lötkolben mit einer bestimmten Fähigkeit gelötet werden. So sieht die gelötete Platine aus

Gehäuse
Es ist Zeit, zum Rumpf überzugehen. Ich habe es auf einem 3D-Drucker gedruckt. Schnörkelloses Design - Box und Knopf. An der Box sind spezielle Haken vorgesehen, um die Glühwürmchen im quadratischen Standardmodul des Designers zu installieren.

Die Hauptplatine und der Akku befinden sich im Gehäuse.


Das LED-Panel ist auf der Abdeckung montiert, die wiederum mit Schrauben an der Hauptbox angeschraubt wird
Zuerst dachte ich daran, das LED-Panel mit Schrauben an der Abdeckung festzuschrauben, aber am Ende klebte ich es einfach auf ein doppelseitiges Klebeband. Es stellte sich so heraus

In dieser Form kann das Gerät bereits verwendet werden, sieht aber trotzdem hässlich aus - es ist nicht genügend Diffusor vorhanden.
Ich habe versucht, die erste Version des Diffusors mit der Technologie des Schrumpfens von PET-Flaschen mit einem Bau-Haartrockner herzustellen (siehe Flugzeugmodelle).
Also, zuerst brauchst du ein Leerzeichen. Ich habe es aus Gips gemacht, den ich in eine Form gegossen habe, die ich auf einem 3D-Drucker gedruckt habe. In der ersten Version war die Form einteilig und ich konnte die gegossene Scheibe nie herausziehen. Deshalb musste ich eine zweiteilige Form machen.

Die Idee der Methode ist wie folgt. Sie stellen eine Flasche Babyjoghurt auf einen Rohling und setzen sie mit einem Haartrockner auf. Hier sind nur Teile von 20 verschiedenen Behältern unter unterschiedlicher Milch wiedergegeben. Ich habe es nie geschafft, dieses Ding schön zu platzieren, ohne Falten und Blasen. Anscheinend müssen Sie eine Art Vakuuminstallation und Sitzplastikfolie umzäunen. Im Allgemeinen stellte sich heraus, dass es für ein solches Fahrzeug zu schwierig war.
Nachdem ich durch die Gophers gemurrt hatte, fand ich ein paar Meter Verbatim PET Transparent Plastiksonde. Ich beschloss, den Diffusor nur zum Drucken auszuprobieren. Und obwohl der Kunststoff am Eingang zum Drucker kristallklar erscheint, ist der Realteil langweilig. Dies liegt wahrscheinlich an der internen Struktur, wie Schichten füllen das Volumen nicht vollständig aus, sondern überlappen sich mit Lücken und Lücken. Wenn Sie versuchen, das Teil mit Sandpapier für eine glattere Oberfläche zu verarbeiten, erhalten wir außerdem noch mehr Mattierung. Dies ist jedoch genau das, was ich brauchte.
Ich war zu faul, um mich mit der Halterung für den Diffusor zu beschäftigen, also fügte ich sie dem Heißkleber hinzu. Mein Design ist jetzt bedingt zusammenklappbar. Ich könnte mit der Erfindung einer Art Riegel verwechselt werden, aber mir ist bereits die transparente Kunststoffsonde ausgegangen. Also lass es heiß schmelzen.


Firmware
Bei LED-Blinkern müssen Sie nicht besonders in die Peripherie des Mikrocontrollers eintauchen - nur ein paar Funktionen für die Arbeit mit GPIO reichen aus. Da das Modul jedoch an die Arduino-Plattform angedockt ist, können Sie dies nutzen.
Zunächst einige Definitionen und Konstanten
Dies bestimmt die Anzahl der Pixel in meiner Matrix, die Pin-Nummern und die maximale Helligkeit der LEDs (während des Debuggens war es zweckmäßig, sie auf 50 einzustellen, damit meine Augen nicht geblendet werden).
Die LEDs in meiner Matrix sind nicht offensichtlich angeordnet - im Zickzack. Daher musste ich für verschiedene Effekte neu nummerieren.
Um die LEDs zu steuern, habe ich das Rad nicht neu erfunden und eine
vorgefertigte Bibliothek für die Arbeit mit WS8211-LEDs genommen . Die Bibliotheksoberfläche ist leicht weiß getüncht. Einige Zusatzfunktionen (z. B. die Konvertierung von HSV in RGB) blieben ebenfalls hängen.
Zunächst müssen die Karte und die WS8211-Bibliothek initialisiert werden.
Zunächst müssen Sie das POWER HOLD-Signal auf Eins setzen. Dies ist ein Signal an den PT1502-Chip, dass der Mikrocontroller aufgewickelt wurde und ordnungsgemäß funktioniert. Die Mikroschaltung wiederum versorgt den Mikrocontroller und die LEDs regelmäßig mit Strom, solange das HOLD-Signal auf Eins eingestellt ist.
Als nächstes werden die Beine zur Steuerung der LED am Ausgang und die Tasten am Eingang konfiguriert. Danach können Sie die WS8211-Bibliothek initialisieren.
Da dies ein ziemlich autonomes Gerät ist, kann man nicht zulassen, dass der Mikrocontroller in einem unverständlichen Zustand bleibt und die gesamte Batterie verschlingt. Dazu starte ich den Watchdog-Timer für 2 Sekunden. Der Timer wird in der Hauptprogrammschleife neu gestartet.
Jetzt müssen Sie einige Hilfsfunktionen definieren. Die WS8211-Bibliothek speichert einen Puffer mit den Farbwerten jeder LED. Das direkte Arbeiten mit dem Puffer ist nicht sehr praktisch, da ich eine einfache Funktion zum Schreiben von RGB-Werten auf eine bestimmte LED geschrieben habe
void setRgb(uint8_t led_idx, uint8_t r, uint8_t g, uint8_t b) { CRGB * leds = ws2811.getRGBData(); leds[led_idx].r = r; leds[led_idx].g = g; leds[led_idx].b = b; }
In den meisten Fällen ist das Zählen von Farben im RGB-Farbmodell jedoch nicht sehr bequem oder sogar unmöglich. Wenn Sie beispielsweise einen Regenbogen zeichnen, ist es bequemer, mit
dem HSV-Farbmodell zu arbeiten . Die Farbe jedes Pixels wird durch den Wert des Farbtons und der Helligkeit festgelegt. Die Sättigung wird der Einfachheit halber weggelassen (Maximum wird verwendet). Die Farbtonwerte werden auf einen Bereich von 0 bis 255 reduziert (anstelle des Standards 0 bis 359).
void setHue(uint8_t led_idx, int hue, int brightness) {
Die Funktion wurde aus der Ai_WS8211-Bibliothek übernommen und leicht abgelegt. In der Originalversion dieser Funktion aus der Bibliothek gab es einige Fehler, aufgrund derer die Farbe auf den Regenbogen mit Rucken angezeigt wurde.
Kommen wir zur Implementierung verschiedener Effekte. Jede Funktion wird von der Hauptschleife aufgerufen, um einen „Frame“ zu zeichnen. Da jeder Effekt zwischen den Aufrufen mit unterschiedlichen Parametern arbeitet, werden sie in statischen Variablen gespeichert.
Dies ist der einfachste Effekt - alle LEDs sind mit einer Farbe gefüllt, die sich reibungslos ändert.
void rainbow() { static uint8_t hue = 0; hue++; for (int led = 0; led < NUM_HW_PIXELS; led++) setHue(led, hue, MAX_VAL); ws2811.sendLedData(); delay(80); }
Der nächste Effekt ist interessanter: Er zeigt einen Regenbogen entlang der Kontur der Matrix an und die Farben im Regenbogen verschieben sich allmählich in einem Kreis.
void slidingRainbow() { static uint8_t pos = 0; pos++; for (int led = 0; led < ARRAY_SIZE(circleLEDIndexes); led++) { int hue = (pos + led*256/ARRAY_SIZE(circleLEDIndexes)) % 256; setHue(circleLEDIndexes[led], hue, MAX_VAL); } ws2811.sendLedData(); delay(10); }
Und dieser Effekt füllt die gesamte Matrix mit einer zufälligen Farbe, die zuerst sanft aufleuchtet und dann auch glatt erlischt.
void randomColorsFadeInOut() { static uint8_t color = 0; static bool goesUp = false; static uint8_t curLevel = 0; if(curLevel == 0 && !goesUp) { color = rand() % 256; goesUp = true; } if(curLevel == MAX_VAL && goesUp) { goesUp = false; } for(int led = 0; led < NUM_HW_PIXELS; led++) setHue(led, color, curLevel); if(goesUp) curLevel++; else curLevel--; ws2811.sendLedData(); delay(10); }
Die nächste Gruppe von Effekten zeichnet verschiedene blinkende Leuchtfeuer. So baut zum Beispiel ein Kind gerne einen Bulldozer aus Magneten und ein orangefarbener Blinker ist dort sehr nützlich.
void orangeBeacon() { const int ORANGE_HUE = 17; static uint8_t pos = 0; pos+=3; for (int led = 0; led < ARRAY_SIZE(circleLEDIndexes); led++) { int brightness = brightnessByPos(pos, led*255/ARRAY_SIZE(circleLEDIndexes), 70); setHue(circleLEDIndexes[led], ORANGE_HUE, brightness); } ws2811.sendLedData(); delay(1); }
Technisch gesehen sieht der Effekt wie ein heller Punkt aus, der sich entlang der Matrix bewegt. Damit es jedoch schön aussieht, verschwinden benachbarte LEDs allmählich, wenn Sie sich vom Hauptpunkt entfernen. Daher brauchte ich eine Funktion, die dieselbe Helligkeit berechnet.
int brightnessByPos(int pos, int ledPos, int delta) { int diff = abs(pos - ledPos); if(diff > 127) diff = abs(256-diff); int brightness = MAX_VAL - constrain(MAX_VAL*diff/delta, 0, MAX_VAL); return brightness; }
Pos ist eine bestimmte bedingte Position des Lichthelligkeitspunkts, die auf einen Loopback-Bereich von 0 bis 255 abgebildet wird. ledPos ist die Position der LED (im selben Bereich angezeigt), deren Helligkeit Sie berechnen müssen. Wenn der Positionsunterschied größer als Delta ist, leuchtet die LED nicht auf und je näher an der Position, desto heller leuchtet sie.
Oder zum Beispiel ein rot-blaues Blinklicht der Polizei
void policeBeacon() { const int RED_HUE = 0; const int BLUE_HUE = 170; static uint8_t pos = 0; pos += 2; for (int led = 0; led < ARRAY_SIZE(policeLEDIndexes); led++) { int ledPos = led*255/ARRAY_SIZE(policeLEDIndexes); int brightness = brightnessByPos(pos, ledPos, 50); setHue(policeLEDIndexes[led], RED_HUE, brightness); if(brightness == 0) { brightness = brightnessByPos((pos+100) % 256, ledPos, 50); setHue(policeLEDIndexes[led], BLUE_HUE, brightness); } } ws2811.sendLedData(); delay(1); }
Da es sich um Autos handelt, ist die Ampel hier kein Problem zu implementieren.
Dies sind Funktionen, die verschiedene Verkehrssignale an verschiedenen Positionen enthalten.
void clearPixels() { for(int i=0; i<NUM_HW_PIXELS; i++) { setRgb(i, 0, 0, 0); } } void redTrafficLights() { for(int i=0; i<4; i++) setRgb(i, MAX_VAL, 0, 0); ws2811.sendLedData(); } void yellowTrafficLights() { for(int i=4; i<8; i++) setRgb(i, MAX_VAL, MAX_VAL, 0); ws2811.sendLedData(); } void greenTrafficLights() { for(int i=8; i<16; i++) setRgb(i, 0, MAX_VAL, 0); ws2811.sendLedData(); }
Es ist Zeit, es wiederzubeleben. Die Ampel arbeitet nach einem speziellen Programm, das in einer Art Bytecode definiert ist. Die Platte beschreibt den Modus und die Zeit, für die dieser Modus eingeschaltet werden muss.
enum TRAFFIC_LIGHTS { NONE, RED, YELLOW, GREEN }; struct trafficLightState { uint8_t state; uint16_t duration; }; const trafficLightState trafficLightStates[] = { {NONE, 1},
Eigentlich die Funktion, die alles verarbeitet
void trafficLights() { static uint8_t curStateIdx = 0; static unsigned long curStateTimeStamp = 0;
Bei Erreichen des angegebenen Zeitintervalls wird der nächste Ampelmodus eingeschaltet und der Countdown erneut gestartet.
Der letzte Effekt, bei dem meine Vorstellungskraft ausreichte, sind Sternchen. 5 zufällige LEDs leuchten mit zufälliger Helligkeit und schalten sich dann sanft aus. Wenn ein Stern ausgeht, leuchtet ein anderer an einer zufälligen Stelle auf.
void stars() { const uint8_t numleds = 5; static uint8_t ledIndexes[numleds] = {0}; static uint8_t curVal[numleds] = {0}; static uint8_t maxVal[numleds] = {0}; for(int i=0; i<numleds; i++) { if(ledIndexes[i] == 0) { uint8_t led = rand() % (NUM_HW_PIXELS+1); CRGB * leds = ws2811.getRGBData(); if(leds[led].r == 0) { ledIndexes[i] = led; maxVal[i] = rand() % (MAX_VAL-1) + 1; curVal[i] = 0; } } else { uint8_t led = ledIndexes[i]; if(curVal[i] < maxVal[i]) curVal[i]++; else if(curVal[i] == maxVal[i]) maxVal[i] = 0; else if(curVal[i] == 0 || --curVal[i] == 0) ledIndexes[i] = 0; setRgb(led-1, curVal[i], curVal[i], curVal[i]); } } ws2811.sendLedData(); delay(80); }
Irgendwo hier hat sich ein böser Käfer eingeschlichen. Manchmal leuchten die Sterne scharf auf oder umgekehrt gehen sie plötzlich aus. Aber um ehrlich zu sein, war ich zu faul, um es herauszufinden - es sieht ganz normal aus.
Es ist Zeit, über das Sparen von Batterie nachzudenken. Ich habe bereits die Verbrauchswerte dieser ganzen Sache angegeben. Wenn Sie nicht daran denken, den Strom auszuschalten, verbrauchen die LEDs den Akku in ein paar Stunden. Diese Funktion ist für das Ausschalten der Stromversorgung nach 90 Sekunden Inaktivität verantwortlich. Anfangs waren es 60 Sekunden, aber mit einem echten Spiel war das nicht genug und 2 Minuten waren irgendwie lang.
void shutdownOnTimeOut(bool resetTimer = false) { static unsigned long periodStartTime = 0; if(periodStartTime == 0 || resetTimer) { periodStartTime = millis(); return; } if(millis() - periodStartTime >= 90000UL) { periodStartTime = 0; shutDown(); } }
Tatsächlich erfolgt das Ausschalten wie folgt.
void shutDown() { clearPixels(); ws2811.sendLedData(); wdt_disable(); digitalWrite(POWER_EN_PIN, LOW);
Wenn der Benutzer die Tasten drückt, wird der Timer zurückgesetzt. Nach Ablauf der eingestellten Zeit setzt die Funktion das HOLD-Signal auf Null. Dies ist ein PT1502-Befehl zum Ausschalten der Stromversorgung. Watchdog muss übrigens auch gestoppt werden, sonst weckt es nach 2 Sekunden das System und schaltet den Strom wieder ein.
Endlich die Hauptschleife, die alles startet
Durch Drücken der Taste werden die Modi umgeschaltet und der Auto-Off-Timer zurückgesetzt. Abhängig vom aktuellen Modus wird eine der Effektfunktionen aus der Liste Modi gestartet. Bei jedem Zyklus wird auch der Watchdog zurückgesetzt.
Wenn beispielsweise ein Kind ein Polizeiauto gespielt hat und nach 1,5 Minuten das Notlicht ausgeschaltet wurde, möchte der Sohn höchstwahrscheinlich nach einem zweiten Einschalten das Polizeiauto weiter spielen. Dazu wird der ausgewählte Modus im EEPROM gespeichert (Zellennummer 10 wird vom Bulldozer ausgewählt).
Hier ist ein Video, das zeigt, wie alles funktioniert.
Bootloader
Fast alles ist fertig. Aber es gibt noch eine Sache, die abgelegt werden muss - einen Bootloader. Tatsache ist, dass der Standard-Bootloader nicht zu uns passt.
Erstens, wenn Sie das Gerät einschalten, wartet es bis zu 6 Sekunden - möglicherweise fließt die Firmware hinein. Erst danach wird die Steuerung auf die Hauptfirmware übertragen. Dies ist in der Entwicklungsphase praktisch, im fertigen Gerät jedoch ärgerlich.
Und zweitens weiß der Standard-Bootloader nichts über den PT1502-Chip, was schön wäre, ein HOLD-Signal zu geben. Ohne dieses Signal glaubt die Mikroschaltung, dass der Mikrocontroller entweder nicht gestartet wurde oder sich im Gegenteil ausschalten möchte. In diesem Fall unterbricht der PT1502 nach einigen Millisekunden die Stromversorgung des gesamten Stromkreises.
Der Vorteil der Behebung beider Probleme ist nicht schwierig. Der Digispark ATTiny85 verwendet
den Mikronukleus-Bootloader . Dieser Bootloader ist einfach genug, um für unsere Bedürfnisse zu archivieren. Es ist nur erforderlich, die entsprechenden Definitionen in der Konfigurationsdatei zu korrigieren.
Zunächst habe ich die Standardkonfiguration Firmware \ configuration \ t85_default in mein eigenes Verzeichnis kopiert und bereits alle Änderungen daran vorgenommen. In diesem Fall ist es einfach, zum ursprünglichen Bootloader zurückzukehren.
In der Datei bootloaderconfig.h können Sie den Bootloader aufrufen. Von dem, was sofort angeboten wird, passt nichts zu uns, aber die nächstgelegene Option ist ENTRY_JUMPER. Bei dieser Option wird nur dann auf den Bootloader zugegriffen, wenn an einem bestimmten Pin eine bestimmte Ebene angezeigt wird (der Jumper ist auf der Platine geschlossen).
#define ENTRYMODE ENTRY_JUMPER
Wir haben keinen Jumper, aber am Fuß von PB2 befindet sich ein Knopf. Lassen Sie den Bootloader eintreten, wenn die Taste beim Einschalten 5-7 Sekunden lang gedrückt gehalten wird. Wenn jedoch gedrückt und losgelassen wird, erfolgt der Übergang zur Hauptfirmware sofort.
Wir müssen 3 Funktionen definieren - Initialisierung, Deinitialisierung und tatsächliche Überprüfung, ob es Zeit ist, den Bootloader aufzurufen. Im Original sind sie alle einfach und mit Makros implementiert. Nur die ersten 2 werden einfach sein
#define HOLD_PIN PB1 #define JUMPER_PIN PB2 #define JUMPER_PORT PORTB #define JUMPER_DDR DDRB #define JUMPER_INP PINB #define bootLoaderInit() {JUMPER_DDR &= ~_BV(JUMPER_PIN); JUMPER_DDR |= _BV(HOLD_PIN); JUMPER_PORT &= ~_BV(JUMPER_PIN); JUMPER_PORT |= _BV(HOLD_PIN); _delay_ms(1);} #define bootLoaderExit() {;}
bootLoaderInit () konfiguriert den Button-Pin (JUMPER_PIN) für den Eingang und schaltet den Hosenträger darauf aus. Wir haben bereits einen Klimmzug auf dem Brett und auf dem Boden, und wenn Sie einen Knopf auf dem Stift drücken, wird es im Gegenteil einen geben. Gleichzeitig können Sie das HOLD-Signal sofort für die Ausgabe konfigurieren und das Gerät darauf einstellen ...
Eine Erklärung der Bit-Arithmetik finden Sie beispielsweise
hier . Ein Verständnis der GPIO-Setup-Register in AVR-Controllern kann beispielsweise
von hier aus gewonnen werden .
Die Funktion bootLoaderExit () ist leer, weil Die exponierte Konfiguration eignet sich gut für den anschließenden Übergang zur Hauptfirmware
Die Funktion bootLoaderStartCondition (), die für die Eingabe des Bootloaders im Makroformat verantwortlich ist, passt nicht hinein und ist daher zu einer vollwertigen Funktion geworden
#ifndef __ASSEMBLER__
Die Funktion überprüft innerhalb weniger Sekunden (tatsächlich ca. 6-7) den Status der Taste. Wenn die Schaltfläche früher losgelassen wurde, müssen wir den Bootloader nicht aufrufen. Geduldig und hartnäckig dürfen Sie den Bootloader betreten.
Wie sich herausstellte, ist die Datei bootloaderconfig.h an der Kompilierung von Assembler-Dateien beteiligt, und der Code in dieser Datei verursacht Fehler. Ich musste die Funktion in den Block #ifndef __ASSEMBLER__ einfügen
Ein weiterer Parameter, den ich optimiert habe, teilt dem Bootloader mit, was zu tun ist, wenn er nicht an USB angeschlossen ist. Beenden Sie ihn nach einer Sekunde. Tatsache ist, dass der Sohn während des Einbruchs oft den Knopf drückte und versehentlich in den Bootloader ging. Ich weiß nicht, wie wunderbar es ist, aber wenn der Bootloader die USB-Verbindung nicht sieht, kann er versehentlich einige Speicherseiten überschreiben. Wenn keine Verbindung besteht, kehren wir einfach zum Hauptprogramm zurück.
#define AUTO_EXIT_NO_USB_MS 1000
Wir kompilieren ... und es wird eine Fehlermeldung angezeigt, dass der Code nicht in den ihm zugewiesenen Bootloader-Speicherplatz passt. Da der Flash-Speicher im Controller sehr klein ist, wird der Bootloader maximal gedrückt, um mehr Platz für das Hauptprogramm zu lassen. Dies lässt sich jedoch leicht in der Datei Makefile.inc beheben, indem Sie den Anweisungen folgen.
Dann habe ich nur die Startadresse des Bootloaders auf eine Seite (64 Byte) reduziert und dadurch den Speicherplatz des Bootloaders vergrößert.
Andernfalls war das Kompilieren und Hochladen des Bootloaders mit dem USBAsp-Programmierer kein Problem.
Fazit
Es war ein sehr interessanter Weg von einem Prototyp auf einem Steckbrett zu einem fertigen Gerät. Es scheint wie ein gewöhnlicher Blinker aus einer Arduino-Lektion, aber tatsächlich musste ich während der Arbeit eine ganze Reihe interessanter Probleme lösen - hier sind der Kampf mit dem Verbrauch, die Wahl der Elementbasis und das Design des Gehäuses und das Erinnern an die Firmware mit dem Bootloader. Ich hoffe aufrichtig, dass meine Erfahrung jemandem nützlich sein wird.
Könnte es einfacher gewesen sein? Natürlich kannst du. Ich denke, alles könnte mit einem Transistor gemacht werden. Leider habe ich
diesen Artikel gelesen, nachdem ich die Platine gelötet habe. Ich würde den Artikel früher sehen - ich würde alles auf dem gleichen beliebten TP4056 machen - es ist einfacher, ihn zu löten. Der DC-DC-Wandler, der sich in diesem Gerät im PT1502 befindet, wird auf keinen Fall benötigt. Eine praktische Untersuchung des PT1502-Mikroschaltkreises ist für mich jedoch nützlich für mein anderes Projekt sowie für die Fähigkeit, Mikroschaltkreise im QFN20-Gehäuse zu löten.
Zum Schluss hier die Links zu meinem Projekt:
Firmware-CodeSchaltung und PlatineGehäuse- und DiffusormodellBereit STL-Modelle zum Drucken