Otomatisasi mangkuk bernyanyi Tibet dengan bantuan "Arduino". Motor stepper bukan biksu. Pemrograman nirkabel

DAN PENGALIHAN ILAHI AKAN TANDA-TANDA TEPAT WAKTU MELALUI ESP8266.
BAGIAN EMPAT



Jadi semuanya bertepatan. Pertama, saya melihat artikel tentang Gytayms tentang tirai yang dikendalikan oleh motor stepper. Saya ingat bahwa saya memiliki mesin yang sama di sekitar tahun kedua. Lalu tatapanku jatuh pada mangkuk bernyanyi, yang telah mengumpulkan debu di rak selama lima tahun sekarang. Dan berbagai pemikiran cerdas mulai muncul di kepalaku ...

Tidak, tentu saja, kadang-kadang sesuai dengan suasana hati saya, saya mengambil cangkir ini di tangan saya dan untuk beberapa waktu mengeluarkan berbagai jenis suara menyihir darinya, tapi ini tidak seperti yang saya inginkan. Dan saya ingin melakukan sesuatu secara paralel, dan membiarkan cawan itu sendiri berbunyi pada waktu itu. Jelas bahwa seribu tahun yang lalu ini akan membutuhkan seorang budak yang terpisah, tiga ratus tahun yang lalu itu akan menjadi mekanisme jam yang canggih, dan sekarang ... Nah, sekarang kita memiliki motor stepper dan papan Arduino ProMini dan elektronik non- canggih lainnya. Tetap hanya untuk sedikit memberi kode pada ternak . Dan pada saat yang sama pastikan pahat Tibet ini pada saat yang sama bertarung dengan waktu yang tepat - sia-sia, atau sesuatu yang menghasilkan begitu banyak server waktu yang tepat. Dan biarkan ESP8266 berkomunikasi dengan mereka, dia tahu caranya.

Jadi ...

Ada mangkuk bernyanyi dengan genta.



Hal ini diperlukan untuk membuat palu mengenai tepi mangkuk. Secara otomatis. Juga dengan kemungkinan remote control (dan pemrograman ulang!). Dan hanya untuk mengalahkan waktu seperti jam tangan lama, tetapi dengan presisi modern.

Ke depan, saya akan menunjukkan apa yang terjadi pada akhirnya. Tonton lebih baik dengan suara.


Tapi mari kita mulai secara berurutan. Pertama saya harus memahami bagaimana mekanika akan terlihat dan bekerja. Untuk elektronik dan perangkat lunak, saya tenang - di belakang tiga artikel tentang cara menangani arduinki dari kejauhan.

Elemen penggerak utama adalah menjadi motor stepper 28YBJ-48 yang sederhana dan saya perlu mengerti apakah dia bisa menangani palu.



Koneksi dvigun ke arduino tidak sulit, untungnya, itu dijual dengan driver ULN2003 yang sudah jadi. Itu hanya diperlukan untuk menyediakan catu daya terpisah untuk 5 volt dan cadangan 200-300 mA, karena Anda pasti tidak akan memiliki cukup konverter pada Arduino itu sendiri. Kemudian, pada empat port digital (saya mengambil PB1, PB2, PB3, PB4) kami mentransfer notebook bit berikut dalam jumlah delapan buah.

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

Untuk rotasi di arah yang berlawanan, kami mentransfer notebook yang sama, tetapi dalam urutan terbalik.

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

Satu-satunya hal yang muncul adalah kecepatan pengiriman data. Jelas bahwa semakin sering, semakin cepat poros motor akan berputar, tetapi sampai batas berapa? Ada frekuensi misterius 100 Hz dalam deskripsi, tetapi apa sebenarnya artinya - periode siklus penuh atau masing-masing menggigit secara terpisah?

Dalam perjalanan percobaan, ternyata, tampaknya, frekuensi perubahan tetrads tepatnya dimaksudkan. Secara maksimal, saya berhasil mempercepat frekuensi ini menjadi 147 Hz, di mana poros mesin melakukan revolusi dalam sekitar satu atau dua detik. Saya tidak mengukurnya dengan tepat, tetapi Anda dapat menilai sendiri bahwa model dengan gearbox ini tidak berbeda dalam kelincahan khusus. Tetapi bagi palu saya, tampaknya, pada prinsipnya, cocok.

Tetapi bagaimanapun juga, bukan hanya kecepatan yang penting bagi kami (atau lebih tepatnya, itu bahkan tidak terlalu penting) tetapi juga kekuatan yang dapat digunakan mesin untuk mempengaruhi fluida kerja. Dalam pos yang didedikasikan untuk mesin ini, dikatakan bahwa mereka tidak dapat berhenti dengan tangan. Ternyata, poros itu sendiri, ya, Anda tidak akan berhenti, tetapi sudah tuas kecil (dan saya memutuskan untuk menggunakan sistem tuas) secara harfiah 10 cm panjang, berhenti dan sangat mudah warung bahkan dengan dampak lokal yang kecil.

Oleh karena itu, opsi paling sederhana awal, ketika tuas yang dibaut ke poros mendorong pengocok pada suspensi, yang sesuai dengan pound mangkuk, tidak lulus. Suara itu terlalu lemah. Jadi saya memutuskan untuk meminta bantuan gravitasi ("perempuan jalang tak berperasaan" dalam kata-kata Sheldon Cooper). Dalam perwujudan ini, tuas menarik genta ke sudut sekitar 30 derajat relatif terhadap arah ke pusat bumi, dan kemudian melepaskannya dan mengirimkannya dalam perjalanan ke mangkuk. Saya sangat menyukai suaranya, baik dari bawah maupun tetangga saya. Mekanisme pelepasan dibuat pada magnet yang dipasang di ujung tuas. Ketika mereka naik, gravitasi mengalahkan yang magnetik dan kunci dilepaskan. Lalu saya membuat berhenti mekanis yang membantu - sebuah palang dengan mana palu bertemu di dekat titik kenaikan ekstrim. Mesin terus berputar, tuas menarik dan melepaskan secara paksa kunci magnetik. Di sini, mesin dibantu oleh gravitasi, sehingga upaya untuk melepaskan diperlukan sangat sedikit.

Desainnya sendiri dirangkai berdasarkan detail perancang anak Menara Eiffel. Saya membelinya untuk waktu yang lama dan secara berkala menggunakan bagian-bagiannya untuk kerajinan saya. Menara, tentu saja, tidak berubah menjadi Eiffel, tetapi menurut saya itu tidak buruk :)

Hampir Menara Eiffel


Semuanya bekerja dengan sempurna, tetapi dengan satu minus - suara selalu memiliki kekuatan yang sama. Ini normal untuk pemukulan waktu, tetapi dalam mode bebas saya ingin mendengar tidak hanya jeda waktu yang berbeda, tetapi juga suara dengan kekuatan yang berbeda. Karena itu, perlu menerapkan elektromagnet, yang juga sangat berguna. Magnet konvensional juga berguna - kolom lima magnet kecil yang saya gunakan sebagai peredam untuk menjinakkan getaran pemukul setelah memukul mangkuk.



Pada awalnya saya menginstalnya di ujung tuas, tetapi desainnya rumit dan tidak dapat diandalkan. Karena itu, elektromagnet pindah ke palu. Dia mengkonsumsi sekitar 300 mA dan tentu saja, tidak mungkin untuk mengendalikannya dari pelabuhan Arduino. Saya harus meletakkan kunci transistor sederhana di atas papan tempat memotong roti kecil.



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

Saya merakit bagian elektronik utama pada "Arduino ProMini" dan modul ESP8266-07, firmware yang saya selesaikan sepenuhnya langkah demi langkah menurut artikel lama saya. Akibatnya, saya, seperti biasa, memiliki kesempatan untuk memprogram Arduino secara nirkabel dan juga berkomunikasi dari jarak jauh dengannya, bertukar data, yang akhirnya berhasil saya gunakan. Diagram menunjukkan, bagaimanapun, Arduino Nano untuk alasan historis, tetapi hubungannya tidak berbeda.



Jadi, apa yang saya inginkan dan kemudian diwujudkan dalam kode program.

  1. Ketika Anda menghidupkan sistem harus secara independen masuk ke mode menonton.
  2. Seharusnya ada aplikasi pada komputer (smartphone) untuk mengubah mode operasi dan mentransfer data yang diperlukan.
  3. Mode harus sederhana - jam, bergumam acak dan kontrol manual.

Saya mulai, sepertinya, dengan hal yang paling sederhana - jam. Memang, setiap radio amatir pemula mengumpulkan probe terlebih dahulu, dan kemudian jam elektronik. Dan kemudian, dia bertanya-tanya mengapa arloji ini ketinggalan satu menit per jam - sepertinya dia secara teoritis menghitung semuanya dengan benar.

Saya sudah memiliki jam elektronik rakitan.



Dan fitur utama mereka yang berguna bagi saya sekarang adalah kemampuan mereka untuk menyeret waktu yang tepat dari server NTP menggunakan mikrosirkuit ESP8266 yang sama, sebagai pribadi dari inkarnasi pertama dan paling sederhana.

Saya bahkan ingin mengajukan artikel tentang hal ini beberapa tahun yang lalu, tetapi setelah melihat berapa kali ini sudah dilakukan, saya berubah pikiran. Mereka akan tertawa. Namun dalam konteks postingan ini analisis pekerjaan mereka cukup tepat. Seperti yang saya sebutkan sebelumnya dalam artikel, saya menulis program untuk ESP8266 dalam bahasa LUA. Itu terjadi begitu saja.

Karena itu, kode yang dimuat ke dalam modul ESP seperti itu.
 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) 


Intinya sederhana. Sekali (atau tidak), fungsi yang mengatur klien UDP dipanggil, yang memanggil server waktu yang tepat dan menanyakan waktu yang tepat. Sebagai tanggapan, server membuang tiga puluh dua byte, dari mana perlu untuk mengambil empat byte data yang diinginkan. Sayangnya, ini dicari bukan menit dan jam, tetapi jumlah detik yang telah berlalu sejauh ini sejak 1 Januari 1900. Karena itu, maka Anda harus menghitung waktu saat ini dari empat byte detik ini dengan berbagai manipulasi kompleks.

Selanjutnya, semuanya lebih sederhana. Mulai pemancar UART dan masukkan waktu yang dihitung ke dalamnya tiga byte - jam, menit, dan detik.

Dan saya kembali memasukkan kode ini, sudah ada di LUA bootloader (tautan) saya, tepat di tempat koneksi ke jaringan WI-FI telah dibuat, tetapi pekerjaan lebih lanjut belum dimulai.

Dalam tampilan penuh, terlihat seperti ini.
 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) 


Tentu saja, ini bertentangan dengan konsep saya, di mana ESP8266 adalah jembatan nirkabel yang bersih, dan mikrokontroler ATMEL melakukan sisanya, tetapi seperti yang mereka katakan: "sekali, bukan ...".

Jadi, kami mendapatkan waktu yang tepat awal (langsung dari server NTP atau secara tidak langsung melalui aplikasi di komputer, tidak masalah), maka kami ingin mempertimbangkan waktu sendiri. Pertama, tidak ada yang memuat jaringan, dan kedua, ATMEL secara teoritis memungkinkan Anda menghitung detik dengan akurasi yang baik. Secara teoritis, ya. Namun dalam praktiknya, ada jebakan.

Penyimpangan kecil tentang jam waktu nyata pada AVR.

Secara teori, tidak ada yang rumit dalam membangun arloji pada mikrokontroler AVR. Desainer paling fanatik bahkan mendorong kuarsa jam di 32768 Hz ke sirkuit untuk ini. Tetapi pada kenyataannya, ini tidak perlu. Bahkan, kuarsa jam diperlukan untuk membentuk kelipatan interupsi sedetik dan membangunkan mikrokontroler yang sedang tidur (catatan). Jika perangkat Anda bekerja terus-menerus, dan jam biasanya bekerja, maka letakkan kuarsa tambahan ke yang sudah ada dan gunakan dua kaki input-output di bawahnya dengan gegabah. Dimungkinkan untuk menggunakan resonator kuarsa, yang sudah ada di sana, delapan atau enam belas megahertz di sana. Akurasi kuantisasi cukup untuk mata Anda, dan menghitung satu detik sebagai penghitung waktu juga akan mudah.

Bahkan, mikrokontroler AVR sudah memiliki segalanya untuk ini. Seperti yang Anda ketahui, sinyal clock input (misalnya, 8 MHz) tiba di dalam chip (misalnya, AVRmega328P sebagai yang paling umum untuk arduino) pada apa yang disebut pre-splitter, di mana ia dapat dibagi lebih jauh dengan keinginan programmer (biasanya oleh 8, 64, 256, 1024). Dan kemudian dia tiba di semacam penghitung waktu (katakanlah T1), yang segera mulai bertambah.

Jadi, mari kita ambil 8 MHz dan bagi 256. Kita masing-masing mendapatkan frekuensi clock dari penghitung 31250 Hz. Oleh karena itu, karena penghitung T1 adalah enam belas digit dan dapat menghitung hingga 65535, maka hanya akan memiliki waktu untuk menghitung hingga 31250 dalam satu detik. Apa yang kita butuhkan Selain itu, timer kami memiliki register perbandingan yang sangat berguna. Jika kita menulis angka 31250 di sana, maka dalam kondisi tertentu itu akan terus-menerus dibandingkan dengan konten penghitung T1, dan akhirnya, ketika itu sama, penghitung akan memberikan sinyal interupsi, katakanlah, sekarang, pegang yang kedua.

Ternyata nyaman, tetapi, sayangnya, tidak sepenuhnya akurat. Untuk penghitung kami akan dihitung dengan kesalahan kuantisasi 256 / 8.000.000, yang memberikan kesalahan yang agak besar dalam menghitung satu detik dalam sebanyak 32 mikrodetik. Dan ini menyebabkan kesalahan 2,8 detik per hari (0,000032 * 3600 * 24).
Tetapi jika kita membagi 8 MHz asli dengan jumlah yang lebih kecil, misalnya, dengan 64, maka akurasi kuantisasi akan meningkat 4 kali menjadi 8 Ξs dan mengurangi kesalahan yang dihasilkan menjadi 0,33 detik per hari. Tetapi, sayangnya, dalam hal ini penghitung akan perlu dihitung hingga 125.000, dan angka seperti itu dalam register enam belas bit tidak akan masuk. Kami harus menulis angka yang lebih kecil ke dalam register perbandingan (62500 masih bisa muat)) dan menambahkan satu loop dalam program itu sendiri, di mana satu detik akan dihitung bukan dengan satu tetapi dengan dua interupsi.

Tapi kami mengambil kasus yang ideal, dan resonator kuarsa nyata, terutama dipasang di papan "buatan Cina", dapat memberi Anda banyak kejutan. Tidak, secara umum, jika Anda melihat kuarsa standar pada lembar data , maka secara teoritis tidak semuanya buruk.

Seperti yang bisa kita lihat, kuarsa rentang menengah berperilaku cukup baik. Dia memiliki ketidakstabilan tuning sendiri pada 25 ppm (atau dengan kata lain 25 ppm), yaitu, dia akan beresonansi pada frekuensi bukan 8 MHz, tetapi, misalnya, pada frekuensi 8, 0002 MHz, yang akan memberi kita kesalahan 2,1 detik per hari. Tetapi ini adalah kesalahan yang konstan dan dapat diperhitungkan. Kuarsa semacam itu juga dapat mengambang pada suhu 5-10 ppm per derajat, tetapi di bawah kondisi ruang operasi perangkat, kesalahannya juga kecil. Masih ada faktor seperti penuaan, tetapi sangat sedikit dan mengubah karakteristik kuarsa ke keadaan setidaknya beberapa terlihat, baik, mungkin lima tahun. Atau sepuluh.

Dan di sini kita dengan gembira mengambil beberapa klon arduino Cina, misalnya ARDUINO UNO.



Kami menjalankan program pengujian untuk menghitung waktu dan memulainya. Backlog per jam selama satu menit? Mudah! Papan Arduino UNO kedua? Tidak ada yang lebih baik.

Ambil Arduino ProMini.



Dan di sini lebih baik, ya. Kesalahan berkurang menjadi dua puluh detik per jam. Yah, sudah sebanding dengan arloji mekanik kukuk.

Papan terakhir yang saya miliki adalah Arduino Nano.



Dan dia adalah satu-satunya yang menunjukkan hasil yang lebih atau kurang waras.

Tetapi bahkan dengan dewan seperti itu, hanya menggunakan konstruksi teoretis, Anda sendiri mengerti bahwa Anda tidak akan membuat jam yang tepat. Papan perlu dikonfigurasi dan aku, sambil mendesah, naik ke belakang osiloskop.

Ternyata, papan Arduino memiliki fitur yang tidak menyenangkan - output yang terhubung resonator kuarsa tidak memiliki output ke sisir pin, meskipun sesuai dengan port PB7. Seperti, karena port ditempati oleh kuarsa, maka Anda tidak melekat padanya. Dan tepat di kaki mikrokontroler, sangat sulit untuk mengambil probe osiloskop, untuk pemasangan di permukaan dan jarak 0,5 mm antara terminal. Tetapi bahkan bergabung dengan kaki kanan tidak memberi saya apa pun. Entah karena saya menusuk salah, atau menusuk di tempat yang salah, karena output resonator kuarsa, mungkin bukan output dari generator jam sama sekali, dan secara umum, itu ada di dalam mikrokontroler itu sendiri. Oleh karena itu, saya harus berkeliling mencari solusi - meletakkan prescaler pada koefisien divisi minimum - satu, tulis nol di register perbandingan sehingga interupsi tersentak segera dan masukkan mikrokontroler dalam mode khusus di mana kaki port PB1 mengubah keadaan logisnya dengan setiap interupsi tersebut.
Logikanya, ketika Anda menghidupkan papan Arduino Nano 16 MHz, sebuah jalan berliku 8 MHz akan muncul di output port ini.

Itulah yang terjadi. Osiloskop menunjukkan frekuensi 8. 002 31 MHz. Selain itu, debit terakhir menjalani hidupnya sendiri dan saya masih tidak mengerti apakah keakuratan osiloskop kurang, atau apakah frekuensi osilator kristal mengambang seperti ini. Lebih seperti sedetik.

Stabilitas termal yang baik di sana, juga, tidak berbau. Jika Anda bernapas di papan (mungkin, omong-omong, wadah masih berasal dari kelembaban?) Atau membawa besi solder (dari jauh), maka kuarsa dapat bergerak dengan segera oleh lima puluh hertz. Dan pengukuran ini masih sekitar dua kali lipat, karena frekuensi awal adalah 16 MHz.

Dengan demikian, dalam papan Arduino (setidaknya yang berasal dari Cina) tidak mungkin untuk mencapai akurasi lebih dari 200 Hz pada frekuensi clock 16 MHz. Yang memberi kita keakuratan tertinggi dari arloji yang dipasang di papan seperti itu tidak lebih dari satu detik per hari. Dan itu bagus.

Karena ada klon Cina dari Arduino UNO, yang sudah saya sebutkan sebelumnya, yang secara umum semuanya buruk. Dan mereka sangat umum, karena mereka murah dan nyaman.

Jadi, frekuensi mereka mungkin berbeda dari yang dinyatakan oleh lebih dari seratus kilohertz! Yang bahkan tidak biasa bahkan untuk kuarsa Cina terburuk.

Teka-teki dimulai dengan fakta bahwa 12 MHz ditulis pada kuarsa itu sendiri! Dan dalam deskripsi penjual juga.



Tapi tidak ada 12 MHz, itu sudah pasti. Jika Anda mengaktifkan port serial UART di papan tulis, Anda akan melihatnya sendiri. Karena UART disetel ke frekuensi ini, Anda tidak akan berfungsi. Dan disetel ke frekuensi 16 MHz - akan. Selain itu, saya pribadi menyaksikan bentuk gelombang di kedua papan Arduino Uno saya. Papan pertama memiliki frekuensi generator 15.8784 MHz, dan yang kedua 15.8661 MHz.

Tapi kemudian tiba-tiba ternyata 12 MHz kuarsa tidak berhubungan langsung dengan mikrokontroler AVR, tetapi dirancang untuk mengoperasikan port serial dengan komputer melalui USB (untuk mengunduh sketsa). Oleh karena itu, asumsi bahwa tidak ada kuarsa di dalamnya, tetapi rantai RC yang tidak disetel dengan benar, tidak terwujud. Dan kuarsa yang kita butuhkan jauh lebih kecil dalam ukuran dan terletak di sebelah chip mikrokontroler. Tapi itu sangat kecil dan tidak ada tulisan di atasnya.

Akibatnya, saya masih tidak dapat memahami bagaimana dan di mana menemukan resonator kuarsa dengan kualitas mengerikan. Namun ternyata, semuanya mungkin di China. Dan entah bagaimana saya memikirkan para pemberani menggunakan arduinki untuk bisnis yang serius. Ya, perangkat lunak dapat dan harus ditulis sendiri, tetapi apa yang harus dilakukan dengan kualitas modul itu sendiri? Rupanya, dari komponen elektronik, Cina mendorong semua yang termurah dan ditolak ke dalamnya.

Program Singing Bowl untuk AVR.

Pada akhirnya, setelah mengalahkan semua kesulitan dengan pengaturan waktu yang akurat, saya menulis kode berikut untuk Arduino ProMini saya

Program C untuk mikrokontroler 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; } } 


Semuanya bekerja dengan sederhana. Setelah menginisialisasi periferal, mikrokontroler masuk ke loop tanpa akhir, menunggu perintah UART. Kode perintah adalah sebagai berikut:

100
mode jam 101
mode acak 102 mode manual.

Karena AVR tidak masalah dari mana perintah itu berasal, perintah pertama dari ESP8266 dipicu. Seperti yang telah disebutkan, modul ESP melekat pada jaringan, menyeret waktu yang tepat dari server NTP dan mengirimkannya ke mikrokontroler. Jadi, pada awalnya arduinka memasuki mode beat jam. Dengan mengganggu penghitung waktu T1, detik, menit dan jam dihitung dan, jika perlu, fungsi dipanggil untuk mengatur motor stepper bolak-balik untuk mengalahkan waktu.

Gangguan dari saklar buluh menetapkan titik nol yang sama, jika seiring waktu tuas yang menarik genta mulai bergerak relatif terhadap poros motor.

Aplikasi untuk komputer.

Semua didasarkan pada program lama yang sama , hanya representasi visual yang berubah di sini.



Semua sama, saluran komunikasi dengan AVR naik melalui koneksi HTTP dan UDP. Kemudian, jika perlu, perintah kontrol yang diperlukan dan data terkait dikirim dalam bentuk paket UDP. Tentu saja, akan lebih tepat untuk memisahkan kontrol dan data pada saluran yang berbeda, tetapi, pertama, untuk ini Anda perlu mengedit kode LUA di bootloader, dan kedua, tidak ada gunanya, karena mikrokontroler dan perintah serta data diterima satu per satu dan UART yang sama. Namun ya, kadang-kadang (jarang) AVR membingungkan mereka. Tapi ini tidak menakutkan, karena jika mikrokontroler tidak mengenali perintah, maka ia tidak akan mengeksekusinya, dan itu juga akan menangkap ini pada aplikasi komputer, yang pada gilirannya akan meminta Anda untuk mengulangi input.

Kode tersedia di Github.

PS

Secara umum, para biksu Tibet tidak hanya memukul dengan genta dalam mangkuk bernyanyi. Jika Anda dengan hati-hati menggerakkan palu di sepanjang tepi mangkuk, maka tanpa ketukan pun akan lahir suara yang indah, karena di bawahnya terdapat sifat ilahi resonansi. Tapi ini adalah tantangan yang sangat serius bagi Arduino.

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


All Articles