
Mon fils s'est fermement «accroché» au
constructeur magnétique Magformers .
Après avoir parcouru une
série de Fixiks où le même constructeur était en vedette, l' enfant a demandé: «Papa, pourquoi les fixics ont-ils des détails qui brillent, mais pas nous?».
Il s'est avéré qu'il y a vraiment un «Magformers Neon LED Set», où en plus des blocs de construction habituels, il y a aussi un élément avec une LED. Étant donné qu'à cette époque, nous avions déjà rassemblé toute une boîte d'aimants de toutes les formes et tailles possibles (quant à moi, les
magformers chinois ne sont pas inférieurs à l'original du tout), je ne voulais pas acheter un autre ensemble juste pour le plaisir d'une ampoule. De plus, cet ensemble coûte beaucoup plus cher qu'un similaire sans rétro-éclairage.
Ayant estimé qu'il n'y avait que quelques dollars dans les composants, dont la plupart étaient déjà en place, j'ai décidé de récupérer ma morgulka. Oui, et avec des effets que l'original n'avait pas.
Sous le chat, vous trouverez l'option d'un clignotant sur l'ATTiny85 et le panneau LED sur les LED WS8212. Je vais parler des circuits, de la façon dont j'ai alimenté tout cela à partir de la batterie, ainsi que des problèmes évidents que j'ai ratissés en cours de route. Je parlerai également en détail de la composante logicielle du projet.
Premiers pas
Il me semblait que la lueur sur une LED régulière (même RGB) est ennuyeuse et banale. Mais ressentir quelque chose comme WS8212 semblait intéressant. Sur ebee, des LED individuelles et des matrices jusqu'à 16x16 ont été proposées. Après avoir acheté plusieurs modules différents, j'ai opté pour une matrice 4x4. Il y a beaucoup de LED pour se livrer à divers effets visuels, tandis que le module est de taille comparable à la fenêtre du bloc carré du concepteur.

Pour contrôler la matrice LED, une seule broche du microcontrôleur suffit, donc même l'arduino nano ressemble à un buste (en plus, il ne rentrera pas dans le boîtier). Mais le clone digispark sur le contrôleur ATTiny85 s'est avéré être juste - il n'a pas beaucoup de mémoire et de broches, mais plus que suffisant pour le clignotant LED. Le module s'intègre parfaitement avec l'IDE Arduino et dispose d'un chargeur de démarrage USB à bord, donc la programmation de ce module est très simple et confortable. J'ai longtemps voulu l'essayer.
Commencé avec le schéma le plus simple.

Sous cette forme, il a été possible de déboguer rapidement tous les algorithmes de lueur / clignotement (à leur sujet ci-dessous). Mais un jouet filaire n'est pas le cas - vous devez penser à la puissance de la batterie. De plus, afin de ne pas faire faillite sur les piles à doigts (qui d'ailleurs ne rentrent pas dans l'enveloppe), il a été décidé d'utiliser du lithium. Et comme il existe une batterie au lithium, vous devez réfléchir à la manière de la charger. Dans les poubelles, nous venons de trouver un
contrôleur de charge «populaire» sur la puce TP4056 achetée à l'occasion.
Mais cela n'a pas fonctionné tout de suite. Le circuit du module Digispark ATTiny85 n'est pas très conçu pour cela - il y a soit une alimentation USB, mais ensuite l'alimentation est fournie directement au microcontrôleur (via le bus +5), ou à partir de l'entrée VIN, mais ensuite l'alimentation passe par le stabilisateur linéaire 7805. Option lorsque le module de charge au lithium inséré dans l'espace entre le connecteur USB et le microcontrôleur n'est pas fourni. J'ai dû modifier un peu le circuit et supprimer les détails supplémentaires.

Ainsi, l'alimentation USB est maintenant fournie à la broche VIN, puis passe à l'entrée du chargeur. La sortie du chargeur (en fait, la batterie est connectée directement) revient dans la carte via le pied 5V. Et bien qu'il y ait en fait de 3 à 4,2 V (tension de la batterie), cela est tout à fait normal - la plage de tension de fonctionnement du microcontrôleur est de 1,8 à 5,5 V. Et même le module LED fonctionne normalement à partir de 2,7 V, bien qu'en dessous de 3,2 V, la LED bleue manque un peu et les couleurs «flottent» un peu en jaune.
Afin d'économiser de l'énergie, la LED D2 toujours allumée a également disparu. Le schéma général ressemble maintenant à ceci

Il serait possible d'alimenter le circuit via le connecteur USB du chargeur, mais la possibilité de télécharger le firmware via le connecteur USB de la carte contrôleur serait perdue. Il serait possible de laisser deux connecteurs USB à des fins diverses - l'un pour la charge, l'autre pour le firmware, mais c'est en quelque sorte faux.
J'ai acheté une batterie de taille 6x25x35 sur ebay, mais elle s'est avérée soit défectueuse, soit je l'ai tuée avec un court-circuit ou un courant de charge important (par défaut, le courant de charge est réglé sur 1A et vous devez souder une résistance pour réduire le courant). Dans tous les cas, lorsque la charge était connectée, même à 10 mA, la tension sur la batterie est tombée à 1V. Au moment des tests, je suis passé à une batterie LiPo à moitié morte d'un petit quadricoptère. Un peu plus tard, j'ai commandé la batterie à un autre vendeur et elle s'est avérée bonne.
En principe, il serait possible de s'arrêter à cela, de souder les fils de connexion et de pousser doucement tout dans une sorte de boîtier, mais j'ai décidé de mesurer la consommation du circuit. Et puis j'ai pleuré. Eh bien, en état de marche (lorsque les ampoules brillent au maximum), cette chose mange jusqu'à 130mA, donc au repos, la consommation est supérieure à 25mA! C'est-à-dire ce clignotant mange ma batterie 600mAh en moins d'une journée!
Il s'est avéré qu'environ 10 mA consomment des LED. Même s'ils ne s'allument pas, un microcontrôleur fonctionne toujours dans chacun d'eux et attend une commande. C'est-à-dire Vous devez trouver un circuit de mise hors tension pour les LED.
Les 15 mA restants sont consommés par le microcontrôleur. Oui, il peut être mis au lit et selon la fiche technique, la consommation sera mesurée par des microampères, mais en fait il n'a pas été possible d'obtenir moins de 1 mA. J'ai éteint l'ADC et traduit les broches en entrée. Il semble que quelque part dans le circuit il y ait une sorte de fuite, mais ma modeste connaissance de l'électronique ne suffit pas pour la trouver et la comprendre.
Nous compliquons le schéma
Ensuite, je me suis souvenu d'avoir acheté une puce PT1502 pour un test. Cette puce est un contrôleur de charge de batterie au lithium complet avec une alimentation avec plusieurs entrées de contrôle. La seule difficulté est que le microcircuit est livré dans un boîtier QFN20 4x4 mm et nécessite un cerclage. Souder cela à la maison est difficile, mais possible. Les frais sont difficiles pour un LUT régulier et doivent être commandés auprès des Chinois. Mais nous n'avons pas peur des difficultés, non?
Dans plusieurs encadrés, le schéma peut être décrit comme suit.

À l'état éteint, le contrôleur et les voyants ne sont pas alimentés. L'appareil dispose d'un bouton `` Power '' qui allume le clignotant (il change également de mode). La LED brille, disons, une minute, et s'il n'y a pas d'activité de l'utilisateur (personne n'appuie sur un bouton), l'appareil s'éteint. C'est-à-dire Il ne va pas simplement s'endormir, mais il se coupe automatiquement par le signal Power Hold. Et il éteint tout à la fois - à la fois le microcontrôleur et les LED. La fonctionnalité de mise sous tension et hors tension est implémentée à l'intérieur de la puce PT1502
Il ne reste plus qu'à dessiner un schéma de circuit et à faire un circuit imprimé. Les circuits sont, pour la plupart, liés à la fiche technique PT1502, ainsi qu'au module Digispark ATTiny85. Le microcircuit du contrôleur de puissance PT1502 est fonctionnellement divisé en plusieurs parties, il est donc divisé en blocs dans le circuit.

Il s'agit en fait d'un contrôleur de charge de batterie au lithium avec son propre faisceau. La LED1 indique l'état de charge - allumée, puis la charge est allumée. La résistance R6 règle le courant de charge à 470mA. Puisque j'ai une batterie de 600mAh, en principe, vous pouvez augmenter le courant et mettre une résistance à 780-800 Ohms jusqu'à 600mA. Cependant, je ne suis pas sûr de la qualité spéciale de ma batterie - il vaut mieux charger plus lentement, mais cela durera plus longtemps.
Envisagez un plan d'alimentation

Le bouton SW1 démarre tout le système - la puce PT1502 se réveille puis démarre toutes les sources d'alimentation (dont elle en a 3). Lorsque l'alimentation est installée, le microcircuit démarre le microcontrôleur en libérant le signal RESET. Pour faciliter le débogage, j'ai également ajouté un bouton de réinitialisation distinct.
Le signal HOLD est utilisé pour éteindre l'ensemble du système. Lorsque le microcontrôleur démarre, il doit régler l'unité sur cette ligne. Lorsqu'il est temps d'arrondir, le microcontrôleur met à zéro sur la ligne HOLD et la puce d'alimentation PT1502 arrête toutes les sources d'alimentation.
Il serait possible de suivre la charge faible de la batterie en utilisant la sortie BAT_LOW, mais dans cet article, je l'ai noté - vous n'avez pas besoin d'enregistrer de données et rien n'explosera si vous ne remarquez pas une batterie morte à temps. Meurt tellement meurt. Mais juste au cas où, le conseil d'administration a fourni le contact pour cette entreprise.
Revenons au bouton SW1 pendant une seconde. J'ai décidé de ne pas faire 2 boutons séparés pour allumer et contrôler. Par conséquent, le même bouton est également connecté à l'ATTiny85 et pendant le fonctionnement, il commute les modes de clignotement. Les valeurs du diviseur R7-R8 sont sélectionnées de manière à ne pas brûler le port du microcontrôleur PB2. Pour toutes les plages de tension de batterie (3,3 - 4,2 V), la tension sera fournie au pied du contrôleur dans les limites spécifiées de la fiche technique (0,7 * VCC - VCC + 0,5 V)
Considérez une source d'alimentation

Il s'agit d'un convertisseur DC-DC pulsé. La tension de sortie est réglée par les résistances R10-R11 et, selon la formule de la fiche technique, est réglée sur 3,3 V. Tout le reste est un simple cerclage.
Pour de bon, une telle source d'alimentation trompée n'est pas vraiment nécessaire - il serait possible d'alimenter le microcontrôleur en général directement à partir de la batterie. C'est juste que cette source est déjà implémentée dans la puce PT1502 et qu'elle peut s'allumer / s'éteindre lorsque nous en avons besoin - pourquoi ne pas l'utiliser?

La puce possède également 2 stabilisateurs linéaires, mais je ne les utiliserai pas. Malheureusement, comme il s'est avéré, il est toujours nécessaire de fournir la tension d'entrée à cette source, sinon le microcircuit pense que la puissance n'est pas encore suffisamment stable et ne démarre pas le microcontrôleur (cette connaissance m'a été donnée par une semaine de soudure de la carte de test dans les deux sens - je ne pouvais pas comprendre pourquoi cela ne fonctionnait pas) )
Passons à la partie logique.

Le câble USB est rodé de la carte Digispark inchangé. Cela est nécessaire pour coordonner la tension USB (qui fonctionne à 3,3 V) et les signaux du microcontrôleur (qui, dans l'original, est alimenté par 5 V). Étant donné que dans mon cas, le microcontrôleur est également alimenté par 3,3 V, le circuit pourrait être simplifié, mais juste au cas où, j'ai divorcé du circuit d'origine sur la carte.

Il n'y a rien d'intéressant dans la liaison du microcontrôleur.
La touche finale est le connecteur

En fait, je me suis procuré une telle carte de débogage sur ATTiny85 avec prise en charge USB et un contrôleur d'alimentation avec une batterie au lithium. Par conséquent, je ne me suis pas limité à la sortie de la ligne sur la LED. Au lieu de cela, j'ai apporté toutes les lignes du microcontrôleur au peigne - en même temps, il est pratique de se connecter au programmateur.
Et que presque toutes les lignes soient liées de manière rigide à une certaine fonctionnalité (PB1 - Ligne de maintien, PB2 - bouton d'alimentation, PB3 / PB4 - USB, PB5 - Réinitialisation) à l'avenir, il sera possible de contourner certaines limites. Par exemple, ne soudez pas le câble USB et ne libérez pas les lignes PB3 / PB4. Ou, par exemple, refusez une réinitialisation et relâchez PB5. En attendant, seul PB0 reste libre - et connectez-y notre LED.
Nous passons au conseil d'administration. Étant donné les limites de la taille de la carte en 40x40 mm, le nombre de composants et le boîtier QFN20 de la puce PT1502, je n'ai même pas envisagé la fabrication de la carte à la maison. Par conséquent, j'ai immédiatement commencé à reproduire la planche à deux couches la plus compacte. Voilà ce que j'ai

Pour la facilité d'utilisation, au verso j'ai signé toutes les fonctions de sortie possibles (j'ai eu l'idée de la carte Digispark)

J'ai commandé la
carte sur
JLCPCB . Pour être honnête, je ne suis pas très satisfait de la qualité - si vous soudez la puce plusieurs fois, le masque près des petits contacts du PT1502 est un peu trouble. Eh bien, de petites inscriptions flottaient un peu. Cependant, si tout est soudé la première fois, alors les normes.
Pour souder QFN20, vous avez besoin d'un fer à souder, tout le reste peut être soudé avec un certain fer à souder avec une certaine compétence. Voici à quoi ressemble la carte soudée

Logement
Il est temps de passer à la coque. Je l'ai imprimé sur une imprimante 3D. Conception sans fioritures - boîte et bouton. Des crochets spéciaux sont fournis sur la boîte pour installer la luciole dans le module carré standard du concepteur.

La carte principale et la batterie vivent dans le boîtier.


Le panneau LED est monté sur le couvercle, qui à son tour est vissé sur la boîte principale avec des vis
Au début, j'ai pensé à visser le panneau LED au couvercle avec des vis, mais à la fin je l'ai simplement collé sur un ruban adhésif double face. Il s'est avéré comme ça

Sous cette forme, l'appareil peut déjà être utilisé, mais il a toujours l'air laid - il n'y a pas assez de diffuseur.
J'ai essayé de faire la première version du diffuseur en utilisant la technologie de rétrécissement des bouteilles en PET avec un sèche-cheveux de construction (aperçu des modèles d'avion).
Donc, vous avez d'abord besoin d'un blanc. Je l'ai fabriqué à partir de gypse, que j'ai versé sous une forme que j'ai imprimée sur une imprimante 3D. Dans la première version, la forme était monobloc et je n'ai jamais pu en retirer le disque moulé. Par conséquent, j'ai dû créer un formulaire en deux parties.

L'idée de la méthode est la suivante. Vous mettez une bouteille de yaourt pour bébé sur un blanc et vous l’assoyez avec un sèche-cheveux de construction. Voici juste le rapatriement de 20 récipients différents sous un lait différent. Je n'ai jamais réussi à mettre cette chose joliment, sans plis ni bulles. Apparemment, vous devez clôturer une sorte d'installation sous vide et asseoir une feuille de plastique. En général, cela s'est avéré trop difficile pour un tel engin.
Après avoir grommelé à travers les gophers, j'ai trouvé une paire de mètres de sonde en plastique transparent Verbatim PET. J'ai décidé d'essayer le diffuseur juste pour imprimer. Et bien qu'à l'entrée de l'imprimante, le plastique semble limpide, la vraie partie est terne. Cela est probablement dû à la structure interne, comme les calques ne remplissent pas complètement le volume mais se chevauchent avec des lacunes et des lacunes. De plus, si vous essayez de traiter la pièce avec du papier de verre pour une surface plus lisse, nous obtenons encore plus de nattes. Cependant, c'est exactement ce dont j'avais besoin.
J'étais trop paresseux pour me soucier du support pour le diffuseur, alors je l'ai ajouté à la colle chaude. Donc, ma conception est maintenant conditionnellement pliable. Je pourrais être confondu avec l'invention d'une sorte de loquets, mais j'ai déjà manqué de sonde en plastique transparent. Alors laissez-le fondre.


Firmware
Pour les clignotants LED, vous n'avez pas besoin de plonger particulièrement dans la périphérie du microcontrôleur - quelques fonctions pour travailler avec GPIO suffisent. Mais puisque le module est ancré à la plate-forme Arduino, alors pourquoi ne pas en profiter?
Tout d'abord, quelques définitions et constantes
Cela détermine le nombre de pixels dans ma matrice, les numéros de broches et la luminosité maximale des LED (lors du débogage, il était pratique de le régler à 50 afin qu'il ne me aveugle pas les yeux)
Les LED de ma matrice sont disposées d'une manière plutôt non évidente - un zigzag. Par conséquent, pour différents effets, j'ai dû renuméroter.
Pour contrôler les LED, je n'ai pas réinventé la roue et j'ai pris une
bibliothèque prête à l'emploi pour travailler avec les LED WS8211 . L'interface de la bibliothèque est légèrement blanchie. Certaines fonctions auxiliaires (par exemple, la conversion de HSV en RVB) sont également restées bloquées.
Tout d'abord, la carte et la bibliothèque WS8211 doivent être initialisées.
Tout d'abord, vous devez régler le signal POWER HOLD sur l'unité - ce sera un signal à la puce PT1502 que le microcontrôleur s'est enroulé et fonctionne correctement. Le microcircuit, à son tour, fournira régulièrement de l'électricité au microcontrôleur et aux DEL tant que le signal HOLD est réglé sur l'unité.
Ensuite, les pieds pour contrôler la LED sur la sortie et les boutons sur l'entrée sont configurés. Après cela, vous pouvez initialiser la bibliothèque WS8211.
Comme il s'agit d'un appareil assez autonome, on ne peut pas permettre au microcontrôleur de rester dans un état incompréhensible et d'avaler toute la batterie. Pour ce faire, je lance la minuterie de surveillance pendant 2 secondes. La minuterie redémarrera dans la boucle de programme principale.
Vous devez maintenant définir quelques fonctions auxiliaires. La bibliothèque WS8211 stocke un tampon avec les valeurs de couleur de chaque LED. Travailler directement avec le tampon n'est pas très pratique, car j'ai écrit une fonction simple pour écrire des valeurs RVB sur une LED spécifique
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; }
Mais dans la plupart des cas, dans le modèle de couleurs RVB, le comptage des couleurs n'est pas très pratique, voire impossible. Par exemple, lorsque vous dessinez tout type d'arc-en-ciel, il est plus pratique de travailler avec
le modèle de couleur HSV . La couleur de chaque pixel est définie par la valeur du ton de couleur et de la luminosité. La saturation est omise pour plus de simplicité (le maximum est utilisé). Les valeurs de teinte sont réduites à une plage de 0 à 255 (au lieu de la norme de 0 à 359).
void setHue(uint8_t led_idx, int hue, int brightness) {
La fonction est tirée de la bibliothèque Ai_WS8211 et légèrement archivée. Dans la version originale de cette fonction de la bibliothèque, il y avait quelques bugs à cause desquels la couleur sur les arcs-en-ciel montrait avec des secousses.
Passons à l'implémentation de divers effets. Chaque fonction est appelée à partir de la boucle principale pour dessiner un «cadre». Étant donné que chaque effet fonctionne avec différents paramètres entre les appels, ils sont stockés dans des variables statiques.
C'est l'effet le plus simple - toutes les LED sont remplies d'une seule couleur, qui change en douceur.
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); }
L'effet suivant est plus intéressant - il affiche un arc-en-ciel le long du contour de la matrice, et les couleurs de l'arc-en-ciel se déplacent progressivement dans un cercle.
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); }
Et cet effet remplit toute la matrice d'une couleur aléatoire, qui s'allume d'abord en douceur, puis s'éteint également en douceur.
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); }
Le groupe d'effets suivant dessine différentes balises clignotantes. Ainsi, par exemple, un enfant aime construire un bulldozer avec des aimants et un clignotant orange y sera très utile.
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); }
Techniquement, l'effet ressemble à un point brillant qui se déplace le long de la matrice. Mais pour la rendre belle, les LED voisines s'estompent progressivement à mesure que vous vous éloignez du point principal. Par conséquent, j'avais besoin d'une fonction qui calcule cette même luminosité.
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 est une certaine position conditionnelle du point lumineux de luminosité, mappée sur une plage de bouclage de 0 à 255. ledPos est la position de la LED (affichée sur la même plage) dont vous devez calculer la luminosité. Si la différence de position est supérieure à delta, la LED ne s'allume pas et plus la position est proche, plus elle brille.
Ou, par exemple, une lumière clignotante rouge-bleu de la police
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); }
Puisque nous parlons de voitures, le feu de circulation n'est pas un problème à mettre en place.
Ce sont des fonctions qui incluent divers feux de circulation à différentes positions.
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(); }
Il est temps de le faire revivre. Le feu de circulation fonctionne selon un programme spécial défini dans une sorte de bytecode. La plaque décrit le mode et la durée pendant laquelle ce mode doit être activé.
enum TRAFFIC_LIGHTS { NONE, RED, YELLOW, GREEN }; struct trafficLightState { uint8_t state; uint16_t duration; }; const trafficLightState trafficLightStates[] = { {NONE, 1},
En fait, la fonction qui traite tout
void trafficLights() { static uint8_t curStateIdx = 0; static unsigned long curStateTimeStamp = 0;
Après avoir atteint l'intervalle de temps spécifié, le prochain mode de feu de circulation est allumé et le compte à rebours recommence.
Le dernier effet sur lequel mon imagination était suffisante, ce sont les astérisques. 5 LED aléatoires s'allument à une luminosité aléatoire, puis s'éteignent en douceur. Si une étoile s'éteint, une autre dans un endroit aléatoire s'allume.
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); }
Quelque part ici, un insecte maléfique s'est introduit. Parfois, les étoiles s'illuminent fortement, ou vice versa s'éteignent brusquement. Mais pour être honnête, j'étais trop paresseux pour le comprendre - cela semble tout à fait normal.
Il est temps de penser à économiser la batterie. J'ai déjà donné les valeurs de consommation de tout cela. Si vous ne pensez pas à couper l'alimentation, les LED mangeront la batterie en quelques heures. Cette fonction est responsable de la mise hors tension après 90 secondes d'inactivité. Au départ, c'était 60 secondes, mais avec un vrai jeu, cela ne suffisait pas et 2 minutes étaient en quelque sorte longues.
void shutdownOnTimeOut(bool resetTimer = false) { static unsigned long periodStartTime = 0; if(periodStartTime == 0 || resetTimer) { periodStartTime = millis(); return; } if(millis() - periodStartTime >= 90000UL) { periodStartTime = 0; shutDown(); } }
En fait, la mise hors tension se produit comme suit.
void shutDown() { clearPixels(); ws2811.sendLedData(); wdt_disable(); digitalWrite(POWER_EN_PIN, LOW);
Si l'utilisateur appuie sur les boutons, la minuterie est réinitialisée. Une fois le temps défini écoulé, la fonction remet le signal HOLD à zéro, ce qui correspond à une commande PT1502 pour couper l'alimentation. Le chien de garde, en passant, doit également être arrêté, sinon après 2 secondes, il réveillera le système et remettra sous tension.
Enfin, la boucle principale qui démarre tout
Appuyez sur le bouton pour changer de mode et réinitialiser la minuterie d'arrêt automatique. Selon le mode actuel, l'une des fonctions d'effet de la liste Modes est lancée. À chaque cycle, le chien de garde est également réinitialisé.
Si un enfant, par exemple, jouait dans une voiture de police et qu'après 1,5 minute le voyant d'urgence s'est éteint, il est fort probable qu'après un deuxième allumage, le fils voudra continuer à jouer dans la voiture de police. Pour ce faire, le mode sélectionné est enregistré en EEPROM (la cellule numéro 10 est sélectionnée dans le bulldozer).
Voici une vidéo qui montre comment tout cela fonctionne.
Bootloader
Presque tout est prêt. Mais il y a encore une chose qui doit être déposée - un chargeur de démarrage. Le fait est que le chargeur de démarrage standard ne nous convient pas.
Tout d'abord, lorsque vous allumez l'appareil, il attend aussi longtemps que 6 secondes - peut-être que le firmware commencera à couler dedans. Ce n'est qu'après le transfert de cette commande vers le firmware principal. Ceci est pratique au stade du développement, mais sera gênant pour le périphérique fini.
Et deuxièmement, le chargeur de démarrage standard ne sait rien de la puce PT1502, ce qui serait bien de donner un signal HOLD. Sans ce signal, le microcircuit pense que le microcontrôleur n'a pas démarré ou, au contraire, veut s'éteindre. Et si c'est le cas, après quelques millisecondes, le PT1502 coupera l'alimentation de tout le circuit.
L'avantage de résoudre les deux problèmes n'est pas difficile. Le digispark ATTiny85 utilise
le chargeur de démarrage du micronoyau . Ce chargeur de démarrage est assez facile à classer pour nos besoins. Il suffit de corriger les définitions correspondantes dans le fichier de configuration.
Tout d'abord, j'ai copié la configuration standard du micrologiciel \ configuration \ t85_default dans mon propre répertoire et y ai déjà apporté toutes les modifications. Ce sera donc le cas pour lequel il est facile de revenir au chargeur de démarrage d'origine.
Dans le fichier bootloaderconfig.h, vous avez le choix de la manière d'entrer dans le chargeur de démarrage. D'après ce qui est proposé, rien ne nous convient, mais l'option la plus proche est ENTRY_JUMPER. Dans cette option, le chargeur de démarrage n'est accessible que si un certain niveau apparaît sur une broche spécifique (le cavalier est fermé sur la carte).
#define ENTRYMODE ENTRY_JUMPER
Nous n'avons pas de cavalier, mais il y a un bouton au pied du PB2. Laissez le chargeur de démarrage entrer si le bouton est maintenu enfoncé pendant 5-7 secondes à la mise sous tension. Mais si enfoncé et relâché, la transition vers le firmware principal se produit immédiatement.
Nous devons définir 3 fonctions - initialisation, désinitialisation et vérifier s'il est temps d'entrer dans le chargeur de démarrage. Dans l'original, ils sont tous simples et mis en œuvre avec des macros. Seuls les 2 premiers seront simples
#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 () configure la broche du bouton (JUMPER_PIN) à l'entrée et désactive la suspension. Nous avons déjà un pull-up sur la planche, et au sol, et lorsque vous appuyez sur un bouton de la broche, au contraire, il y en aura un. Dans le même temps, vous pouvez immédiatement configurer le signal HOLD pour qu'il soit émis et régler l'unité sur celui-ci ...
Pour une explication de l'arithmétique des bits, par exemple, allez
ici , et une compréhension des registres de configuration GPIO dans les contrôleurs AVR peut être glanée, par exemple, à
partir d'ici .
La fonction bootLoaderExit () est vide car la configuration exposée est tout à fait appropriée pour la transition ultérieure vers le firmware principal
La fonction bootLoaderStartCondition (), qui est responsable de la saisie du chargeur de démarrage au format macro, n'a pas sa place et est donc devenue une fonction à part entière
#ifndef __ASSEMBLER__
La fonction en quelques secondes (en fait environ 6-7) vérifie l'état du bouton. Si le bouton a été relâché plus tôt, nous n'avons pas besoin d'entrer dans le chargeur de démarrage. Patient et persistant sont autorisés sur le chargeur de démarrage.
Il s'est avéré que le fichier bootloaderconfig.h est impliqué dans la compilation des fichiers assembleur et le code de ce fichier provoque des erreurs. J'ai dû mettre la fonction dans le bloc #ifndef __ASSEMBLER__
Un autre paramètre que j'ai modifié indique au chargeur de démarrage quoi faire s'il n'est pas connecté à l'USB - quittez après une seconde. Le fait est que lors de l'effraction, le fils a souvent appuyé sur le bouton et est accidentellement entré dans le chargeur de démarrage. Je ne sais pas comment c'est miraculeux, mais si le chargeur de démarrage n'a pas vu la connexion USB, il pourrait accidentellement écraser certaines pages de la mémoire. Par conséquent, s'il n'y a pas de connexion, nous quitterons simplement le programme principal.
#define AUTO_EXIT_NO_USB_MS 1000
Nous compilons ... et nous obtenons une erreur indiquant que le code ne rentre pas dans l'espace du chargeur de démarrage qui lui est alloué. Comme la mémoire flash dans le contrôleur est très petite, le chargeur de démarrage est serré au maximum afin de laisser plus d'espace pour le programme principal. Mais cela est facilement résolu dans le fichier Makefile.inc en suivant les instructions.
Ensuite, je viens de réduire l'adresse de démarrage du chargeur de démarrage à une page (64 octets), augmentant ainsi l'espace du chargeur de démarrage.
Sinon, la compilation et le téléchargement du chargeur de démarrage à l'aide du programmateur USBAsp n'étaient pas un problème.
Conclusion
C'était un moyen très intéressant de passer d'un prototype sur une maquette à un appareil fini. Cela ressemble à un clignotant ordinaire d'une leçon d'arduino, mais en fait, dans le processus de travail, j'ai dû résoudre tout un tas de problèmes intéressants - voici la lutte avec la consommation, le choix de la base de l'élément et la conception de l'étui, et en pensant au firmware avec le chargeur de démarrage. J'espère sincèrement que mon expérience sera utile à quelqu'un.
Cela aurait-il pu être plus facile? Bien sûr que vous le pouvez. Je pense que tout pourrait être fait avec un transistor. Malheureusement
, j'ai lu
cet article après avoir soudé la carte. Je verrais l'article plus tôt - je ferais tout sur le même TP4056 populaire - il est plus facile de le souder. Quoi qu'il en soit, le convertisseur DC-DC, qui est à l'intérieur du PT1502 dans cet appareil, pour de bon, n'est pas nécessaire. Cependant, une étude pratique du microcircuit PT1502 m'est utile pour mon autre projet, ainsi que la capacité de souder des microcircuits dans le boîtier QFN20.
Enfin, voici les liens vers mon projet:
Code du micrologicielCircuit et carteModèle de boîtier et diffuseurModèles STL prêts pour l'impression