Automatisation des bols chantants tibétains à l'aide de "Arduino". Moteur pas à pas au lieu d'un moine. Programmation sans fil

ET TRANSFERT DE LA VOLONTÉ DIVINE DE SIGNAUX TEMPORELS EXACTS PAR ESP8266.
QUATRIÈME PARTIE



Alors tout a coïncidé. Tout d'abord, j'ai vu un article sur Gytayms sur les rideaux commandés par un moteur pas à pas. Je me suis souvenu que j'avais le même moteur au ralenti pour la deuxième année. Puis mon regard tomba sur le bol chantant, qui accumulait de la poussière sur l'étagère depuis maintenant cinq ans. Et puis diverses pensées intelligentes ont commencé à me venir à l'esprit ...

Non, bien sûr, parfois selon mon humeur, je prenais cette tasse dans mes mains et en extrayais pendant un certain temps différents types de sons envoûtants, mais ce n'était pas tout à fait ce que je voulais. Et je voulais faire quelque chose en parallèle et laisser sonner la tasse à ce moment-là. Il est clair qu'il y a mille ans, cela aurait nécessité un esclave séparé, il y a trois cents ans, ce serait un mécanisme d'horlogerie sophistiqué, et maintenant ... Eh bien, maintenant nous avons un moteur pas à pas et une carte Arduino ProMini et d'autres appareils électroniques non sophistiqués. Il ne reste plus qu'à coder un peu sur le bétail . Et en même temps, assurez-vous que ce ciseau tibétain combat en même temps l'heure exacte - en vain, ou quelque chose qui a produit autant de serveurs de temps exacts. Et laissez ESP8266 communiquer avec eux, elle sait comment.

Alors ...

Il y a un bol chantant avec un battant.



Il est nécessaire de faire frapper le maillet contre le bord du bol. Automatiquement. Également avec possibilité de télécommande (et de reprogrammation!). Et juste pour battre le temps comme une vieille montre, mais avec une précision moderne.

À l'avenir, je montrerai ce qui s'est finalement produit. Regardez mieux avec le son.


Mais commençons dans l'ordre. J'ai d'abord dû comprendre à quoi ressembleraient et fonctionneraient les mécaniciens. Pour l'électronique et les logiciels, j'étais calme - derrière trois articles sur la façon de gérer l'arduinki à distance.

L'élément mobile principal devait être un simple moteur pas à pas 28YBJ-48 et j'avais besoin de comprendre s'il pouvait manipuler le maillet.



La connexion même du dvigun à l'arduino n'est pas difficile, heureusement, il a été vendu avec un pilote ULN2003 prêt à l'emploi. Il était seulement nécessaire de fournir une alimentation séparée pour 5 volts et une réserve de 200-300 mA, car vous n'aurez certainement pas assez de convertisseur sur l'arduino lui-même. Ensuite, sur quatre ports numériques (j'ai pris PB1, PB2, PB3, PB4), nous transférons les blocs-notes suivants au nombre de huit.

PORTB=0b00000010;//     PORTB=0b00000110; PORTB=0b00000100; PORTB=0b00001100; PORTB=0b00001000; PORTB=0b00011000; PORTB=0b00010000; PORTB=0b00010010; 

Pour une rotation en sens inverse, nous transférons les mêmes cahiers, mais dans l'ordre inverse.

  PORTB=0b00010010; PORTB=0b00010000; PORTB=0b00011000; PORTB=0b00001000; PORTB=0b00001100; PORTB=0b00000100; PORTB=0b00000110; PORTB=0b00000010; 

La seule chose qui se pose est la vitesse à laquelle les données sont transmises. Il est clair que plus souvent, plus vite l'arbre moteur tourne, mais à quelle limite? Il y a une mystérieuse fréquence de 100 Hz dans la description, mais qu'est-ce que cela signifie exactement - la période d'un cycle complet ou chaque quartet séparément?

Au cours des expériences, il s'est avéré que, apparemment, la fréquence du changement de tétrades précisément était prévue. Au maximum, j'ai réussi à accélérer cette fréquence à 147 Hz, à laquelle l'arbre du moteur a fait une révolution en une ou deux secondes environ. Je ne l'ai pas mesuré exactement, mais vous pouvez juger par vous-même que ce modèle avec cette boîte de vitesses ne diffère pas par une agilité particulière. Mais pour mon maillet, il semblait, en principe, convenir.

Mais après tout, ce n'est pas seulement la vitesse qui est importante pour nous (ou plutôt, ce n'est même pas très important) mais la force avec laquelle le moteur peut agir sur le fluide de travail. Dans les articles dédiés à ce moteur, il a été affirmé qu'ils ne pouvaient pas s'arrêter avec une main. Il s’est avéré que la tige elle-même, oui, vous ne vous arrêterez pas, mais déjà un petit levier (et j'ai décidé d'utiliser un système de levier) de 10 cm de long, s'arrête et se bloque très facilement même avec un petit impact local.

Par conséquent, l'option la plus simple initiale, lorsque le levier boulonné à l'arbre pousse le batteur sur la suspension, qui par conséquent martèle le bol, n'a pas réussi. Le son était trop faible. J'ai donc décidé d'appeler à la gravité de l'aide (la «chienne sans cœur» selon les mots de Sheldon Cooper). Dans ce mode de réalisation, le levier a tiré un battant jusqu'à un angle d'environ 30 degrés par rapport à la direction du centre de la Terre, puis s'est désengagé de celui-ci et l'a envoyé en route vers le bol. J'ai vraiment aimé le son, à la fois d'en bas et de mes voisins. Le mécanisme de libération a été réalisé sur un aimant monté à l'extrémité du levier. Alors qu'ils montaient, la gravité a vaincu le magnétique et le verrou a été libéré. Ensuite, j'ai fait un arrêt mécanique aidant - une barre transversale avec laquelle le maillet s'est rencontré près du point extrême de montée. Le moteur a continué de tourner, le levier a tiré et a débrayé de force le verrou magnétique. Ici, le moteur était aidé par la gravité, de sorte que l'effort de désengagement était très peu nécessaire.

Le design lui-même a été assemblé sur la base des détails du designer pour enfants de la Tour Eiffel. Je l'ai acheté longtemps et j'utilisais périodiquement ses pièces pour mon artisanat. La tour, bien sûr, ne s'est pas avérée être l'Eiffel, mais à mon avis elle n'est en aucun cas pire :)

Presque Tour Eiffel


Tout fonctionnait parfaitement, mais avec un inconvénient - le son était toujours de la même puissance. C'est normal pour le temps qui bat, mais en mode libre, j'aimerais entendre non seulement différentes pauses dans le temps, mais aussi des sons de différentes forces. Par conséquent, il était nécessaire d'appliquer un électro-aimant, ce qui était également très utile. Les aimants conventionnels étaient également utiles - une colonne de cinq petits aimants que j'ai utilisée comme amortisseur pour apprivoiser les vibrations du batteur après avoir frappé le bol.



Au début, je l'ai installé à l'extrémité du levier, mais la conception était lourde et fragile et peu fiable. Par conséquent, l'électro-aimant s'est déplacé vers un maillet. Il a consommé environ 300 mA et bien sûr, il était impossible de le contrôler depuis le port d'arduino. J'ai dû placer une simple clé à transistor sur une petite planche à pain.



R1 - 560 Ohm, VD1 - 1N4007, VT1 - BD139

J'ai assemblé la partie électronique principale sur le «Arduino ProMini» et le module ESP8266-07, dont le firmware que j'ai réalisé complètement pas à pas selon mon ancien article . En conséquence, comme d'habitude, j'ai eu la possibilité de programmer l'arduino sans fil et de communiquer à distance avec lui, en échangeant des données, que j'ai finalement utilisé avec succès. Le diagramme montre cependant l'Arduino Nano pour des raisons historiques, mais sa connexion n'est pas différente.



Alors, qu'est-ce que je désirais et ensuite incorporé dans le code du programme.

  1. Lorsque vous allumez le système doit passer indépendamment en mode montre.
  2. Il devrait y avoir une application sur l'ordinateur (smartphone) pour changer les modes de fonctionnement et transférer les données nécessaires.
  3. Les modes doivent être simples: horloge, marmonnement aléatoire et contrôle manuel.

J'ai commencé, semble-t-il, par la chose la plus simple: les heures. En effet, toute radio amateur débutante recueille d'abord une sonde, puis une horloge électronique. Et puis, cependant, il se demande pourquoi cette montre est en retard d'une minute par heure - il semble qu'il ait théoriquement tout calculé correctement.

J'avais déjà l'horloge électronique assemblée.



Et leur principale caractéristique utile pour moi maintenant était leur capacité à faire glisser l'heure exacte à partir des serveurs NTP en utilisant le même microcircuit ESP8266, en la personne de sa toute première et la plus simple incarnation.

J'ai même voulu déposer un article sur ce sujet il y a quelques années, mais après avoir vu combien de fois cela avait déjà été fait, j'ai changé d'avis. Ils riront après tout. Mais dans le contexte de cette post-analyse de leur travail, c'est tout à fait approprié. Comme je l'ai mentionné plus tôt dans les articles, j'écris des programmes pour ESP8266 en langage LUA. Il en est ainsi.

Par conséquent, le code chargé dans ce module ESP était comme ça.
 uart.setup(0,9600,8,0,1,0) timezone = 3 --  tmr.alarm(1,5000,0,function() -- try once connect to NTP-server sk=net.createUDPSocket() sk:send(123,"130.149.17.21",string.char( 227, 0, 6, 236, 0,0,0,0,0,0,0,0, 49, 78, 49, 52, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)) sk:on("receive", function(sck, payload) ntp = payload:byte(41) * 128 * 256 * 256 + payload:byte(42) * 128 * 256 + payload:byte(43) * 128 + payload:byte(44) /2 + timezone * 1800 hour =ntp % 43200 / 1800 minute = ntp % 1800 / 30 secund = ntp % 60 uart.write(0,hour) uart.write(0,minute) uart.write(0,secund) sk:close() end ) end) 


L'essentiel est simple. Une fois (ou non), la fonction qui configure le client UDP est appelée, qui appelle le serveur d'heure exacte et demande l'heure exacte en conséquence. En réponse, le serveur vide trente-deux octets, d'où il est nécessaire de récupérer les quatre octets de données souhaités. Malheureusement, cette recherche n'est pas des minutes et des heures, mais le nombre de secondes qui se sont écoulées jusqu'à présent depuis le 1er janvier 1900. Par conséquent, vous devrez alors calculer l'heure actuelle à partir des quatre octets de ces secondes avec diverses manipulations complexes.

De plus, tout est plus simple. Démarrez l'émetteur UART et déposez-y le temps calculé de trois octets - heures, minutes et secondes.

Et j'ai à nouveau inséré ce code, déjà dans mon chargeur de démarrage LUA (lien), juste à l'endroit où la connexion au réseau WI-FI a déjà été établie, mais les travaux n'ont pas encore commencé.

En pleine vue, cela ressemble à ceci.
 function InstrProgrammingEnable () -- instruction for MC "enable programming" p=0 while p<31 do p=p+1 pin=8 gpio.write(pin, gpio.LOW) spi.send(1, 0xAC,0x53) read = spi.recv( 1, 8) spi.send(1,0,0) gpio.write(pin, gpio.HIGH) if (string.byte(read)== 83) then --print("connection established") p=33 if(p==31) then --print("no connection") end end end end function ProgrammingDisable () pin=2--END OF ESET FOR MK GPIO4 gpio.mode(pin, gpio.INPUT) pin=8 gpio.mode(pin, gpio.INPUT) -- CE chip enable not used GPIO15 pin=5--CLK MASTER for SPI GPIO14 used gpio.mode(pin, gpio.INPUT) pin=6--MISO MASTER for SPI GPIO 12 may not used gpio.mode(pin, gpio.INPUT) pin=7--MOSI MASTER for SPI //GPIO13 used gpio.mode(pin, gpio.INPUT) end --PROGRAMMING ENABLE function ProgrammingEnable () pin=2-- RESET FOR MK gpio.mode(pin, gpio.OUTPUT) gpio.write(pin, gpio.LOW) pin=2--POZITIV FOR 4MSEC RESET FOR MK gpio.mode(pin, gpio.OUTPUT) gpio.write(pin, gpio.HIGH) tmr.delay(4) gpio.mode(pin, gpio.OUTPUT) gpio.write(pin, gpio.LOW) tmr.delay(25000) end function InstrFlashErase() --FFFFFFFFFFFFFFFFFF pin=8 gpio.write(pin, gpio.LOW) spi.send(1,0xAC,0x80,0,0) gpio.write(pin, gpio.HIGH) tmr.delay(15000) pin=2--RESET FOR MK gpio.mode(pin, gpio.OUTPUT) gpio.write(pin, gpio.HIGH) tmr.delay(20000) gpio.write(pin, gpio.LOW) --print( "FLASH is erased") InstrProgrammingEnable () end function InstrStorePAGE(H, address, data) pin=8 gpio.write(pin, gpio.LOW) spi.send(1,H,0,address,data) gpio.write(pin, gpio.HIGH) tmr.delay(500) end function InstrWriteFLASH(page_address_low,page_address_high) pin=8 gpio.write(pin, gpio.LOW) spi.send(1,0x4C,page_address_high,page_address_low,0) gpio.write(pin, gpio.HIGH) tmr.delay(5000)--        end function Programming (payload) pin=8--CS MASTER for SPI gpio.mode(pin, gpio.OUTPUT, gpio.PULLUP) pin=4--LED LIGHTS ON LOW gpio.mode(pin, gpio.OUTPUT) gpio.write(pin, gpio.LOW) --print(string.len(payload)) page_count = 7 --  1  for k =0 ,page_count ,1 do--quantity of pages for i=0 , 127, 2 do-- -1 address = i/2 data=payload:byte(i+1+128*k) if data == nil then data = 0xff end InstrStorePAGE(0x40,address,data) -- tmr.delay(100)-- otherwise not in time write data =payload:byte(i+1+1+128*k) if data == nil then data = 0xff end InstrStorePAGE(0x48,address,data) -- tmr.delay(100) end page_address_low=bit.band(k ,3)*64 -- 3   11 page_address_high=k/4+frame1024*2 tmr.delay(1000) InstrWriteFLASH(page_address_low,page_address_high) tmr.wdclr() end pin=4--LED gpio.mode(pin, gpio.OUTPUT) gpio.write(pin, gpio.HIGH) end --MAIN BLOCK wifi.setmode(wifi.STATION) --wifi.sta.config("mixa","M1sh8111") -- set SSID and password of your access point station_cfg={} tmr.delay(30000) station_cfg.ssid="mixa" tmr.delay(30000) station_cfg.pwd="M1sh8111" tmr.delay(30000) wifi.sta.config(station_cfg) tmr.delay(30000) wifi.sta.connect() tmr.delay(1000000) --print(wifi.sta.status()) --print(wifi.sta.getip()) while ( wifi.sta.status()~=1 ) do if( wifi.sta.status()==5) then break end end uart.setup(0,9600,8,0,1,0) --     NTP      AVR timezone = 3 --  tmr.alarm(1,5000,0,function() -- try once connect to NTP-server sk=net.createUDPSocket() sk:send(123,"130.149.17.21",string.char( 227, 0, 6, 236, 0,0,0,0,0,0,0,0, 49, 78, 49, 52, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)) sk:on("receive", function(sck, payload) ntp = payload:byte(41) * 128 * 256 * 256 + payload:byte(42) * 128 * 256 + payload:byte(43) * 128 + payload:byte(44) /2 + timezone * 1800 hour =ntp % 43200 / 1800 minute = ntp % 1800 / 30 secund = ntp % 60 uart.write(0,100)-- AVR    uart.write(0,hour) uart.write(0,minute) uart.write(0,secund) sk:close() end ) end) prog_address=""; sv=net.createServer(net.TCP,30) tmr.delay(100) --print("SERVER READY") sv:listen(40000,function(c)-- ,   c:on("receive", function(c, payload) --print(payload) if (payload =="program\r\n") then c:send("ready\r\n") --print("ready for program\r\n") tmr.wdclr() spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8,80,spi.FULLDUPLEX) --  SPI 320  115 000  ProgrammingEnable ()---------------------------------------------------------------------  80    1  tmr.delay(100) InstrProgrammingEnable () tmr.delay(100) InstrFlashErase() tmr.delay(100) frame1024=0--   st=net.createServer(net.TCP,30)--         AWR,   stop program st:listen(40001,function(c) c:on("receive", function(c, payload) tmr.wdclr() Programming (payload) frame1024=frame1024+1 end) end) end if (payload =="data\r\n") then tmr.wdclr() c:send("ready\r\n") -- print("ready for data\r\n") c:on("receive", function(c, prog_address_payload) prog_address=prog_address_payload-- IP  UDP       -- print(prog_address) c:send(prog_address) srv=net.createUDPSocket()--     ,   data stop srv:listen(50000) -- uart.setup(0,9600,8,0,1,0) srv:on("receive", function(srv, pl) --      UDP pl=pl*1 -- print(pl) uart.write(0,pl) --    UART  AVR end) uart.on("data", 1, function(data) --    UART  AVR srv:send(50000,prog_address,data) --    UDP   end, 0) tmr.wdclr() end) end if (payload =="stop data\r\n") --      then ready = false if(srv~=nil) then srv:close() -- print("stop data") end collectgarbage() end if (payload =="stop program\r\n") then if(st~=nil) then st:close() frame1024=0 ProgrammingDisable () -- print("stop program") end collectgarbage() end end) end) 


Bien sûr, cela va à l'encontre de mon concept, où l'ESP8266 est un pont sans fil propre, et le microcontrôleur ATMEL fait le reste, mais comme on dit: "une fois, pas ...".

Donc, nous avons obtenu l'heure exacte initiale (directement à partir du serveur NTP ou indirectement via l'application sur l'ordinateur, cela n'a pas d'importance), puis nous aimerions considérer l'heure nous-mêmes. Premièrement, il n'y a rien pour charger le réseau, et deuxièmement, ATMEL vous permet théoriquement de compter les secondes avec une bonne précision. Théoriquement, oui. Mais dans la pratique, il y a des pièges.

Une petite digression sur l'horloge en temps réel de l'AVR.

En théorie, il n'y a rien de compliqué à construire une montre sur le microcontrôleur AVR. Les concepteurs les plus enragés ont même poussé une horloge à quartz à 32768 Hz dans le circuit pour cela. Mais en fait, ce n'est pas nécessaire. En effet, une horloge à quartz est nécessaire pour former un multiple d'interruption d'une seconde et réveiller un microcontrôleur endormi . Si votre appareil fonctionne constamment, et l'horloge le fait généralement, il est imprudent de mettre du quartz supplémentaire sur celui existant et de prendre deux jambes d'entrée-sortie. Il est possible d'utiliser un résonateur à quartz, qui est déjà là, huit ou seize mégahertz là-bas. Sa précision de quantification est suffisante pour vos yeux, et compter une seconde comme compteur de minuterie sera également facile.

En fait, le microcontrôleur AVR a déjà tout pour cela. Comme vous le savez, le signal d'horloge d'entrée (par exemple, 8 MHz) arrive à l'intérieur de la puce (par exemple, AVRmega328P comme le plus populaire pour Arduino) sur le soi-disant pré-séparateur, où il peut être divisé davantage (généralement par 8, 64, 256, 1024). Et puis il arrive à une sorte de compteur de minuterie (par exemple T1), qui commence immédiatement à augmenter.

Alors, prenons 8 MHz et divisons par 256. On obtient respectivement la fréquence d'horloge du compteur 31250 Hz. En conséquence, puisque le compteur T1 est à seize chiffres et peut compter en conséquence jusqu'à 65535, il aura juste le temps de compter jusqu'à 31250 en une seconde. Ce dont nous avons besoin. De plus, notre minuteur possède un autre registre de comparaison très utile. Si nous y inscrivons le nombre 31250, dans certaines conditions, il sera constamment comparé au contenu du compteur T1, et enfin, lorsqu'il est égal, le compteur générera un signal d'interruption, disons, gardez votre deuxième.

Il s'avère que cela est pratique, mais malheureusement pas tout à fait exact. Car notre compteur sera compté avec une erreur de quantification de 256/8 000 000, ce qui donne une erreur assez importante dans le calcul d'une seconde en autant que 32 microsecondes. Et cela conduit à une erreur de 2,8 secondes par jour (0,000032 * 3600 * 24).
Mais si nous divisons les 8 MHz d'origine par une plus petite quantité, par exemple par 64, la précision de quantification augmentera de 4 fois à 8 μs et réduira l'erreur résultante à 0,33 seconde par jour. Mais, malheureusement, dans ce cas, le compteur devra être compté jusqu'à 125 000, et un tel nombre dans le registre à seize bits n'entrera pas. Nous devrons écrire un plus petit nombre dans le registre de comparaison (62500 peut toujours tenir)) et ajouter une boucle dans le programme lui-même, où une seconde sera comptée non pas par une mais par deux interruptions.

Mais nous avons pris un boîtier idéal, et un véritable résonateur à quartz, spécialement installé sur une carte «made in China», peut vous apporter bien des surprises. Non, en général, si vous regardez du quartz standard sur des fiches techniques , alors théoriquement tout n'est pas si mauvais.

Comme nous pouvons le voir, le quartz de milieu de gamme se comporte plutôt bien. Il a une instabilité de son propre réglage à 25 ppm (ou en d'autres termes 25 ppm), c'est-à-dire qu'il résonnera à une fréquence non de 8 MHz, mais, par exemple, à une fréquence de 8 0002 MHz, ce qui nous donnera jusqu'à 2,1 secondes d'erreur par jour. Mais c'est une erreur constante et elle peut être prise en compte. Ce quartz peut également flotter à une température de 5 à 10 ppm par degré, mais dans les conditions de fonctionnement de la pièce de l'appareil, l'erreur sera également faible. Il existe toujours un facteur tel que le vieillissement, mais il est très rare et change les caractéristiques du quartz dans un état d'au moins une certaine perceptibilité, enfin, peut-être cinq ans. Ou dix.

Et ici, nous sommes joyeux de prendre un clone chinois d'arduino, par exemple ARDUINO UNO.



Nous y exécutons un programme de test pour compter le temps et le lancer. Arriéré par heure pendant une minute? C'est facile! Deuxième carte Arduino UNO? Pas mieux.

Prenez l'Arduino ProMini.



Et ici, c'est mieux, oui. L'erreur a diminué à vingt secondes par heure. Eh bien, déjà comparable à une montre mécanique à coucou.

La dernière carte que j'avais sous la main était l'Arduino Nano.



Et elle était la seule à avoir donné des résultats plus ou moins sains d'esprit.

Mais même avec un tel tableau, utilisant uniquement des constructions théoriques, vous comprenez vous-même que vous ne ferez pas une montre exacte. La carte doit être configurée et moi, avec un soupir, je suis monté derrière l'oscilloscope.

Il s'est avéré que les cartes Arduino ont une caractéristique désagréable - la sortie à laquelle le résonateur à quartz est connecté n'a pas de sortie sur le peigne à broches, bien qu'elle corresponde au port PB7. Comme, comme le port est occupé par du quartz, vous ne vous y accrochez pas. Et juste au pied du microcontrôleur, il est très difficile de ramasser la sonde de l'oscilloscope, pour un montage en surface et un pas de 0,5 mm entre les bornes. Mais même rejoindre la jambe droite ne m'a rien donné. Soit parce que j'ai piqué incorrectement, soit piqué au mauvais endroit, parce que la sortie du résonateur à quartz, peut-être pas la sortie du générateur d'horloge, et en général, c'est à l'intérieur du microcontrôleur lui-même. Par conséquent, j'ai dû contourner les solutions de contournement - mettre le pré-détartreur sur le coefficient de division minimum - un, écrire zéro dans le registre de comparaison afin que l'interruption se déclenche immédiatement et entrer dans le microcontrôleur dans un mode spécial dans lequel la jambe de port PB1 change son état logique à chaque interruption.
Logiquement, lorsque vous allumez la carte Arduino Nano 16 MHz, un méandre de 8 MHz devrait apparaître à la sortie de ce port.

C'est ce qui s'est passé. L'oscilloscope a montré une fréquence de 8. 002 31 MHz. De plus, la dernière décharge a vécu sa propre vie et je ne comprenais toujours pas si la précision de l'oscilloscope faisait défaut, ou si la fréquence de l'oscillateur à cristal flotte comme ça. Plus comme une seconde.

Là aussi, une bonne stabilité thermique ne sentait pas. Si vous respirez sur la planche (peut-être, d'ailleurs, les conteneurs viennent encore de l'humidité?) Ou si vous apportez un fer à souder (de loin), le quartz pourrait se déplacer immédiatement de cinquante hertz. Et ces mesures sont encore grossièrement doublées, car la fréquence initiale est de 16 MHz.

Ainsi, dans les cartes Arduino (au moins celles d'origine chinoise), il est impossible d'atteindre une précision supérieure à 200 Hz à une fréquence d'horloge de 16 MHz. Ce qui nous donne la précision ultime des montres assemblées sur de telles planches pas plus d'une seconde par jour. Et c'est bien.

Parce qu'il y a des clones chinois d'Arduino UNO, déjà mentionnés par moi plus tôt, avec lesquels, en général, tout va mal. Et ils sont très courants, car ils sont bon marché et pratiques.

Ainsi, leur fréquence peut différer de celle déclarée de plus d'une centaine de kilohertz! Ce qui est même inhabituel même pour le pire quartz chinois.

L'énigme commence par le fait que 12 MHz est écrit sur le quartz lui-même! Et dans les descriptions des vendeurs aussi.



Mais il n'y a pas 12 MHz, c'est sûr. Si vous activez le port série UART sur la carte, vous verrez par vous-même. Puisque l'UART est réglé sur cette fréquence, vous ne travaillerez pas. Et réglé sur une fréquence de 16 MHz - sera. De plus, j'ai personnellement regardé les formes d'onde sur mes deux cartes Arduino Uno. La première carte avait une fréquence de générateur de 15,8784 MHz et la seconde 15,8661 MHz.

Mais soudain, il s'est avéré que le quartz 12 MHz n'est pas directement lié au microcontrôleur AVR, mais est conçu pour faire fonctionner le port série avec un ordinateur via USB (pour télécharger des croquis). Par conséquent, l'hypothèse qu'il n'y avait pas de quartz à l'intérieur, mais une chaîne RC mal réglée, ne s'est pas matérialisée. Et le quartz dont nous avons besoin est beaucoup plus petit et se trouve à côté de la puce du microcontrôleur. Mais il est très petit et il n'y a aucune inscription dessus.

En conséquence, je ne pouvais toujours pas comprendre comment et où trouver des résonateurs à quartz d'une si mauvaise qualité. Mais apparemment, tout est possible en Chine. Et en quelque sorte, j'ai pensé aux casse-cou qui utilisent arduinki pour les affaires sérieuses. Eh bien, les logiciels peuvent et doivent être écrits vous-même, mais que faire de la qualité des modules eux-mêmes? Apparemment, à partir des composants électroniques, les Chinois y poussent tous les moins chers et les plus rejetés.

Le programme Singing Bowl pour AVR.

Au final, après avoir surmonté toutes les difficultés avec un timing précis, j'ai écrit le code suivant pour mon Arduino ProMini

Programme C pour le microcontrôleur AVRmega328P
 /* * Tibetian_Bowl.c * * Created: 07.06.2018 0:29:57 * Author: User */ #define F_CPU 8000000 #include <avr/io.h> #include <avr/interrupt.h> #include <stdint.h>//    #include <math.h> //  #include <stdio.h> // - #include <avr/eeprom.h> #include <stdbool.h> #include <setjmp.h> #include <stdlib.h> volatile bool change_mode = false; volatile bool boom =false; volatile bool go_ahead=true; volatile bool go_back=false; volatile bool gerkon=false; volatile uint8_t latency=2;//     latency = 1 volatile uint8_t hour=12; volatile uint8_t hour24=12;//       12 volatile uint8_t minute=0; volatile uint8_t secund=0; volatile uint8_t power=0; volatile uint8_t pause_between_boom=0; volatile uint8_t first_byte=0; volatile uint8_t second_byte=0; volatile uint8_t third_byte=0; volatile uint8_t firth_byte=0; volatile uint8_t fifth_byte=0; volatile uint8_t cSREG; ISR(USART_RX_vect) { //     ,  //   –  ,    . if (first_byte==0) { first_byte=UDR0; change_mode=true; goto ret; } if (second_byte==0) { second_byte=UDR0; goto ret; } if (third_byte==0) { third_byte=UDR0; goto ret; } if (firth_byte==0) { firth_byte=UDR0; goto ret; } if (fifth_byte==0) { fifth_byte=UDR0; goto ret; } cSREG=UDR0; ret: return; } ISR(PCINT1_vect )//PC2 int 10 //    { if (go_ahead) { UDR0=44; //      44 } if (go_back) { gerkon=true; } } ISR(TIMER1_COMPA_vect) { //        secund++; if (secund ==60) { secund=0; minute++; if(minute==60) { minute=0; hour++; if(hour==12) { hour=1;//     12  } hour24++; if(hour24==24) { hour24=1; } boom=true; } } } void time_delay(long dell)//       { long i; dell=dell*796;//  8  for(i=0;i<dell;i++){;;}; sei();//    ,  -    .WTF ?????????????????????? } void turn_onkward()//       { uint8_t legnth=170;//    ( 0  170) for(uint16_t i =0;i<=legnth;i++) { go_ahead=true; PORTB=0b00000010;//       time_delay(latency); PORTB=0b00000110; time_delay(latency); PORTB=0b00000100; time_delay(latency); PORTB=0b00001100; time_delay(latency); PORTB=0b00001000; time_delay(latency); PORTB=0b00011000; time_delay(latency); PORTB=0b00010000; time_delay(latency); PORTB=0b00010010; time_delay(latency); if (i>140) { PORTD |=(1<<PORTD2);//     , 1 -   } } time_delay(100); go_ahead=false; } void turn_backward(uint8_t pause, uint8_t force_of_sound)//     // //       { uint8_t legnth=170;//       ( 0  170) for(uint16_t i =0;i<=legnth;i++) { go_back=true; PORTB=0b00010010; time_delay(latency); PORTB=0b00010000; time_delay(latency); PORTB=0b00011000; time_delay(latency); PORTB=0b00001000; time_delay(latency); PORTB=0b00001100; time_delay(latency); PORTB=0b00000100; time_delay(latency); PORTB=0b00000110; time_delay(latency); PORTB=0b00000010;//16 ms   ,  latency = 2 time_delay(latency); if (i==force_of_sound*17) { PORTD &=~(1<<PORTD2);//     , 0 -   } if (gerkon) { gerkon=false; break; } } time_delay(50); time_delay(pause*1000);//       go_back=false; } void sound(uint8_t force,uint8_t pause) //       1  10           { turn_onkward(); turn_backward(pause,force); } int main(void) { sei(); // UART  9600    8  time_delay(2000);//  , esp     -  UCSR0A=0; UCSR0B=0b10011000;// a UART UCSR0C=0b00000110; UBRR0L=51;// 8  9600  UART UBRR0H=0; //   INT0   2   10 //        PCICR|=(1<<PCIE1);//   14-8 PCMSK1|=(1<<PCINT10);//    INT10 DDRC&=~(1<<PORTC2); DDRB=0b00111110;//PB1-PB4    , PB5      DDRD=0b00000100; // PD2      //SET INTERRUPT FROM TIMER1 AND SET TIMER1 GTCCR=0;//RESET PRESCALER TCCR1A=0;//I/O NORMAL WORK TCCR1C=0; TCCR1B=0B00001100;//1/256 PRESCALING AND CTC MODE TCNT1H=0;//RESET TIMER1 TCNT1L=0; TIMSK1=0B00000010;//SET COMPARE A INTERRUPT ENABLED OCR1AH=0x79;//SET TIME CONSTANT IN COMPARE REGISTER OCR1AL=0xa7;// 31143    7 972 608  TCCR0B=0b00000010;// 8        0  255 while (1) { begining: time_delay(1000); if (first_byte!=0) { UDR0=first_byte;//      .     (100,101,102)    } if (first_byte==100)//   (     NTP  { hour=second_byte;//  if (hour>12)//      12  (24  ) { hour=hour-12; } if (hour==0) { hour=12; } minute=third_byte;//  secund=firth_byte;//  power=fifth_byte;//   first_byte=0;//   second_byte=0; third_byte=0; firth_byte=0; fifth_byte=0; change_mode=false; goto clock_mode; } if (first_byte==101)//   { power=second_byte; pause_between_boom=third_byte; first_byte=0; second_byte=0; third_byte=0; firth_byte=0; fifth_byte=0; change_mode=false; goto random_mode; } if (first_byte==102)//  { power=second_byte; first_byte=0; second_byte=0; third_byte=0; firth_byte=0; fifth_byte=0; change_mode=false; goto hand_mode; } //     ,      first_byte=0; second_byte=0; third_byte=0; firth_byte=0; fifth_byte=0; goto begining; clock_mode: while(change_mode==false) { if (boom)//   { for(uint8_t i =0;i<hour;i++) { if ((hour24>21)|(hour24<10))//  { sound(3,0);//   10 (),  0  boom=false; } else { sound(power,0);//   10 (),  0  boom=false; } } } } goto begining; random_mode: while(change_mode==false) { uint8_t random_power = TCNT0;//      1 uint8_t random_pause = TCNT1L;//      1 random_pause=TCNT0;//      1 random_power=random_power/25; if (random_power<5) { random_power=random_power+2;//      } random_pause=(random_pause/25)+pause_between_boom; UDR0=random_pause; time_delay(100); sound(random_power,random_pause); } goto begining; hand_mode: sound(power,0); goto begining; } } 


Tout fonctionne simplement. Après avoir initialisé les périphériques, le microcontrôleur entre dans une boucle sans fin, attendant une commande UART. Les codes de commande sont les suivants:

100
mode horloge 101
mode aléatoire 102 mode manuel.

Étant donné que l'AVR n'a pas d'importance d'où vient la commande, la première commande de ESP8266 est déclenchée. Comme déjà mentionné, le module ESP s'accroche au réseau, fait glisser l'heure exacte du serveur NTP et l'envoie au microcontrôleur. Ainsi, au début, l'arduinka entre en mode de battement d'horloge. En interrompant le compteur horaire T1, les secondes, les minutes et les heures sont comptées et, si nécessaire, des fonctions sont appelées pour régler le moteur pas à pas dans les deux sens afin de battre le temps.

L'interruption de l'interrupteur à lames fixe le même point zéro, si au fil du temps le levier tirant le clapet commence à se déplacer par rapport à l'arbre du moteur.

L'application pour l'ordinateur.

Il est tout de même basé sur les mêmes anciens programmes , seule la représentation visuelle change ici.



Tout de même, le canal de communication avec AVR monte via les connexions HTTP et UDP. Ensuite, si nécessaire, la commande de contrôle nécessaire et les données associées sont envoyées sous forme de paquets UDP. Bien sûr, il serait plus correct de séparer le contrôle et les données sur différents canaux, mais, premièrement, pour cela, vous devez modifier le code LUA dans le chargeur de démarrage, et deuxièmement, cela ne sert à rien, car le microcontrôleur et les commandes et données sont reçus un par un et le même UART. Et pourtant oui, parfois (rarement) AVR les confond. Mais ce n'est pas effrayant, car si le microcontrôleur ne reconnaît pas la commande, il ne l'exécutera pas, et il s'en emparera également sur l'application sur l'ordinateur, ce qui vous invitera à répéter l'entrée.

Le code est disponible sur Github.

PS

En général, les moines tibétains ne battent pas seulement avec des battants dans des bols chantants. Si vous conduisez soigneusement le maillet juste le long du bord du bol, alors sans aucun coup, un son merveilleux naîtra, ayant en dessous la nature divine de la résonance. Mais c'est un défi vraiment sérieux pour Arduino.

Source: https://habr.com/ru/post/fr415629/


All Articles