
Anak saya dengan kuat "terpikat" pada
Magformers konstruktor magnetik . Setelah melihat
serangkaian Fixiks di mana konstruktor yang sama ditampilkan, anak itu bertanya: "Ayah, mengapa fixics memiliki detail yang bersinar, tetapi kami tidak melakukannya?".
Ternyata benar-benar ada "Magformers Neon LED Set", di mana selain blok bangunan biasa, ada juga elemen dengan LED. Karena pada saat ini kami telah mengumpulkan sekotak penuh magnet dengan segala bentuk dan ukuran yang memungkinkan (bagi saya,
magformer China sama sekali tidak kalah dengan aslinya), saya
entah bagaimana tidak mau membeli set lain hanya demi bola lampu. Selain itu, set ini harganya jauh lebih mahal daripada yang sama tanpa lampu latar.
Setelah memperkirakan bahwa hanya ada beberapa dolar dalam komponen, yang sebagian besar sudah saya miliki, saya memutuskan untuk mengumpulkan morgulka saya. Ya, dan dengan efek yang tidak dimiliki aslinya.
Di bawah kucing, Anda akan menemukan opsi tanda bahayanya pada ATTiny85 dan panel LED pada LED WS8212. Saya akan berbicara tentang sirkuit, bagaimana saya menghidupkan semua ini dari baterai, serta masalah-masalah yang tidak saya sadari sepanjang jalan. Saya juga akan berbicara secara rinci tentang komponen perangkat lunak proyek.
Langkah pertama
Tampak bagi saya bahwa cahaya pada LED biasa (bahkan RGB) membosankan dan dangkal. Tetapi merasakan sesuatu seperti WS8212 tampak menarik. Di ebee, baik LED individu maupun matriks berukuran hingga 16x16 ditawarkan. Setelah membeli beberapa modul yang berbeda, saya memutuskan untuk matriks 4x4. Ada banyak LED di dalamnya untuk menikmati berbagai efek visual, sedangkan modul ini sebanding ukurannya dengan jendela blok persegi perancang.

Untuk mengontrol matriks LED, hanya satu pin dari mikrokontroler sudah cukup, sehingga arduino nano terlihat seperti bust (selain itu, tidak akan masuk ke dalam case). Tetapi klon digispark pada pengontrol ATTiny85 ternyata tepat - ia tidak memiliki banyak memori dan pin, tetapi lebih dari cukup untuk lampu tanda LED. Modul ini terintegrasi sempurna dengan IDE Arduino dan memiliki bootloader USB, sehingga pemrograman modul ini sangat sederhana dan nyaman. Saya sudah lama ingin mencobanya.
Dimulai dengan skema paling sederhana.

Dalam formulir ini, dimungkinkan untuk dengan cepat men-debug semua algoritma cahaya / blink (tentang mereka di bawah). Tapi mainan bertenaga kawat bukan itu masalahnya - Anda harus memikirkan daya baterai. Selain itu, agar tidak bangkrut pada baterai jari (yang, apalagi, tidak muat dalam amplop), diputuskan untuk menggunakan lithium. Dan karena ada baterai lithium, Anda perlu memikirkan cara mengisi ulang. Di nampan, kami baru saja menemukan
pengendali muatan "populer" pada chip TP4056 yang dibeli pada kesempatan itu.
Tapi itu tidak berhasil segera. Sirkuit modul Digispark ATTiny85 tidak terlalu dirancang untuk ini - ada daya USB, tetapi daya disuplai langsung ke mikrokontroler (melalui bus +5), atau dari input VIN, tetapi daya mengalir melalui stabilisator linier 7805. Opsi saat modul pengisian daya lithium dimasukkan ke celah antara konektor USB dan mikrokontroler tidak disediakan. Saya harus memodifikasi rangkaian sedikit dan menghapus detail tambahan.

Jadi, sekarang daya USB disuplai ke pin VIN dan kemudian masuk ke input pengisi daya. Output pengisi daya (pada kenyataannya, baterai terhubung langsung) kembali ke papan melalui kaki 5V. Dan meskipun sebenarnya akan ada dari 3 ke 4.2V (tegangan baterai) ini cukup normal - kisaran tegangan operasi mikrokontroler adalah 1,8-5,5V. Dan bahkan modul LED bekerja secara normal dari 2.7V, meskipun di bawah 3.2V LED biru sedikit kurang dan warna-warna "mengambang" sedikit berwarna kuning.
Untuk menghemat energi, LED D2 yang selalu aktif juga hilang. Skema umum sekarang terlihat seperti ini

Dimungkinkan untuk memberi makan sirkuit melalui konektor USB di pengisi daya, tetapi kemudian kemampuan untuk mengunggah firmware melalui konektor USB pada papan pengontrol akan hilang. Mungkin saja untuk meninggalkan dua konektor USB untuk berbagai keperluan - satu untuk mengisi daya, yang lain untuk firmware, tetapi ini entah bagaimana salah.
Saya membeli baterai ukuran 6x25x35 di ebay, tetapi ternyata cacat, atau saya membunuhnya dengan korsleting atau arus muatan besar (secara default arus muatan diatur ke 1A dan Anda perlu menyolder satu resistor untuk mengurangi arus). Bagaimanapun, ketika beban terhubung, bahkan pada 10 mA, tegangan pada baterai turun menjadi 1V. Pada saat pengujian, saya beralih ke baterai LiPo setengah mati dari quadrocopter kecil. Beberapa saat kemudian saya memesan baterai dari penjual lain dan ternyata bagus.
Pada prinsipnya, mungkin untuk berhenti pada hal ini, menyolder kabel penghubung dan dengan lembut mendorong segala sesuatu ke dalam beberapa jenis perumahan, tetapi saya memutuskan untuk mengukur konsumsi rangkaian. Dan kemudian aku menangis. Nah, dalam kondisi kerja (ketika umbi bersinar penuh) hal ini memakan hingga 130mA, jadi saat istirahat, konsumsi lebih dari 25mA! Yaitu penutup mata ini memakan baterai 600mAh saya dalam waktu kurang dari sehari!
Ternyata sekitar 10 mA mengkonsumsi LED. Bahkan jika mereka tidak menyala, mikrokontroler masih berfungsi di masing-masing dan menunggu perintah. Yaitu Anda harus membuat sirkuit power-down untuk LED.
Sisanya 15 mA dikonsumsi oleh mikrokontroler. Ya, itu bisa diletakkan di tempat tidur dan menurut datasheet, konsumsi akan diukur dengan microamps, tetapi pada kenyataannya itu tidak mungkin untuk mendapatkan kurang dari 1 mA. Saya mematikan ADC dan menerjemahkan pin menjadi input. Tampaknya di suatu tempat di sirkuit ada semacam kebocoran, tetapi pengetahuan sederhana saya tentang elektronik tidak cukup untuk menemukan dan memahaminya.
Kami menyulitkan skema
Kemudian saya ingat bahwa saya membeli chip PT1502 untuk tes. Chip ini adalah pengontrol pengisian daya baterai lithium lengkap dengan catu daya dengan beberapa input kontrol. Satu-satunya kesulitan adalah bahwa microcircuit datang dalam paket QFN20 4x4 mm dan membutuhkan beberapa pengikat. Menyolder ini di rumah memang sulit, tetapi memungkinkan. Biaya sulit untuk LUT reguler dan harus dipesan dari Cina. Tapi kita tidak takut kesulitan, kan?
Dalam beberapa kotak, skema dapat dijelaskan sebagai berikut.

Dalam kondisi mati, daya tidak diberikan ke pengontrol dan LED. Perangkat ini memiliki tombol 'Daya' yang menyalakan blinker (itu juga beralih mode). LED bersinar, katakanlah, satu menit, dan jika tidak ada aktivitas pengguna (tidak ada yang menekan tombol), perangkat mati. Yaitu Ini tidak hanya akan tidur, tetapi mematikan daya dengan sendirinya oleh sinyal Power Hold. Dan itu mematikan semuanya sekaligus - baik mikrokontroler dan LED. Fungsi hidup dan mati diimplementasikan di dalam chip PT1502
Yang tersisa adalah menggambar diagram sirkuit dan membuat papan sirkuit. Sirkuit ini, untuk sebagian besar, dicampur dengan lembar data PT1502, serta modul Digispark ATTiny85. Microcircuit dari pengendali daya PT1502 secara fungsional dibagi menjadi beberapa bagian, oleh karena itu dibagi menjadi beberapa blok dalam rangkaian.

Ini, pada kenyataannya, adalah pengontrol pengisian daya baterai lithium dengan harnessnya sendiri. LED1 menunjukkan status pengisian aktif, kemudian pengisian aktif. Resistor R6 menetapkan arus muatan ke 470mA. Karena saya memiliki baterai 600mAh, pada prinsipnya, Anda dapat meningkatkan arus dan meletakkan resistor pada 780-800 Ohm hingga 600mA. Namun, saya tidak yakin dengan kualitas khusus baterai saya - lebih baik mengisi daya lebih lambat, tetapi akan bertahan lebih lama.
Pertimbangkan rencana daya

Tombol SW1 memulai seluruh sistem - chip PT1502 bangun sendiri dan kemudian mulai semua sumber daya (yang memiliki 3). Ketika daya dipasang, sirkuit mikro akan memulai mikrokontroler dengan melepaskan sinyal RESET. Untuk memudahkan debugging, saya juga menambahkan tombol Reset terpisah.
Sinyal HOLD digunakan untuk mematikan seluruh sistem. Ketika mikrokontroler mulai, itu harus mengatur unit pada baris ini. Ketika tiba saatnya untuk mematikan, mikrokontroler menetapkan nol pada saluran HOLD dan chip daya PT1502 akan menghentikan semua sumber daya.
Mungkin saja masih melacak pengisian baterai rendah menggunakan output BAT_LOW, tetapi dalam artikel ini saya mencetaknya - Anda tidak perlu menyimpan data apa pun dan tidak ada yang akan meledak jika Anda tidak melihat baterai yang mati pada waktunya. Mati begitu mati. Tapi untuk jaga-jaga, dewan memberikan kontak untuk bisnis ini.
Mari kita kembali ke tombol SW1 sebentar. Saya memutuskan untuk tidak membuat 2 tombol terpisah untuk menyalakan dan mengendalikan. Oleh karena itu, tombol yang sama juga terhubung ke ATTiny85 dan selama operasi mengalihkan mode berkedip. Nilai dari pembagi R7-R8 dipilih agar tidak membakar port mikrokontroler PB2. Untuk semua rentang tegangan baterai (3.3 - 4.2V), tegangan akan dipasok ke kaki pengontrol dalam batas lembar data yang ditentukan (0,7 * VCC - VCC + 0,5V)
Pertimbangkan sumber daya

Ini adalah konverter DC-DC berdenyut. Tegangan keluaran diatur oleh resistor R10-R11 dan, sesuai dengan rumus dari datasheet, diatur ke 3.3V. Yang lainnya adalah pengikat sederhana.
Demi kebaikan, sumber daya yang ditipu seperti itu tidak benar-benar dibutuhkan - akan mungkin untuk memberi daya mikrokontroler secara umum langsung dari baterai. Hanya saja sumber ini sudah diimplementasikan dalam chip PT1502 dan dapat dinyalakan / dimatikan saat kita membutuhkannya - mengapa tidak menggunakannya?

Chip ini juga memiliki 2 stabilisator linier, tetapi saya tidak akan menggunakannya. Sayangnya, ternyata, masih perlu untuk memasok tegangan input ke sumber ini, jika tidak, rangkaian mikro berpikir bahwa daya masih belum cukup stabil dan tidak memulai mikrokontroler (pengetahuan ini diberikan kepada saya oleh seminggu menyolder papan tes bolak-balik - saya tidak bisa mengerti mengapa itu tidak bekerja )
Mari kita beralih ke bagian logis.

Kabel USB tersusun dari papan Digispark tidak berubah. Ini diperlukan untuk mengoordinasikan tegangan USB (yang menjalankan 3.3V) dan sinyal-sinyal mikrokontroler (yang aslinya didukung oleh 5V). Karena dalam kasus saya mikrokontroler juga ditenagai oleh 3.3V, sirkuit dapat disederhanakan, tetapi untuk berjaga-jaga, saya menceraikan sirkuit asli di papan tulis.

Tidak ada yang menarik dalam pengikatan mikrokontroler.
Sentuhan terakhir adalah konektor

Bahkan, saya mendapatkan papan debugging untuk saya sendiri di ATTiny85 dengan dukungan USB dan pengontrol daya dengan baterai lithium. Karena itu, saya tidak membatasi diri untuk hanya mengeluarkan garis ke LED. Sebagai gantinya, saya membawa semua lini mikrokontroler ke sisir - pada saat yang sama nyaman untuk terhubung ke programmer.
Dan biarkan hampir semua garis terikat secara kaku ke fungsi tertentu (PB1 - Tahan saluran, PB2 - tombol power, PB3 / PB4 - USB, PB5 - Reset) di masa depan akan dimungkinkan untuk melewati beberapa batasan. Misalnya, jangan solder kabel USB dan lepaskan garis PB3 / PB4. Atau, misalnya, menolak reset dan lepaskan PB5. Sementara itu, hanya PB0 yang tetap bebas - dan hubungkan LED kami ke sana.
Kami lolos ke papan. Mengingat keterbatasan pada ukuran papan di 40x40mm, jumlah komponen dan perumahan QFN20 dari chip PT1502, saya bahkan tidak mempertimbangkan membuat papan di rumah. Oleh karena itu, saya segera mulai membiakkan papan dua lapis yang paling ringkas. Itu yang saya dapat

Untuk kemudahan penggunaan, di sisi sebaliknya saya menandatangani semua fungsi output yang mungkin (saya mendapat ide dari papan Digispark)

Saya memesan
board di
JLCPCB . Sejujurnya, saya tidak terlalu puas dengan kualitasnya - jika Anda menyolder chip berkali-kali, maka topeng di dekat kontak kecil PT1502 sedikit mendung. Nah, prasasti kecil sedikit melayang. Namun, jika semuanya disolder pertama kali, maka norma.
Untuk menyolder QFN20 Anda membutuhkan besi solder, yang lainnya dapat disolder dengan besi solder tertentu dengan keterampilan tertentu. Inilah yang terlihat seperti papan solder

Perumahan
Sudah waktunya untuk pindah ke lambung. Saya mencetaknya pada printer 3D. Desain tanpa embel-embel - kotak dan tombol. Kait khusus disediakan pada kotak untuk memasang kunang-kunang dalam modul kotak standar perancang.

Papan utama dan baterai hidup dalam kasing.


Panel LED dipasang pada penutup, yang pada gilirannya disekrup ke kotak utama dengan sekrup
Pada awalnya saya berpikir untuk memasang panel LED pada penutup dengan sekrup, tetapi pada akhirnya saya hanya menempelkannya pada selotip dua sisi. Ternyata begini

Dalam bentuk ini, perangkat sudah dapat digunakan, tetapi masih terlihat jelek - tidak ada cukup diffuser.
Saya mencoba membuat versi pertama dari diffuser menggunakan teknologi penyusutan botol PET dengan pengering rambut konstruksi (mengintip model pesawat terbang).
Jadi, pertama kamu perlu yang kosong. Saya membuatnya dari gypsum, yang saya tuangkan ke dalam bentuk yang saya cetak pada printer 3D. Dalam versi pertama, formulir itu one-piece dan saya tidak pernah bisa mengeluarkan disc dari dalamnya. Karena itu, saya harus membuat bentuk dua potong.

Gagasan metode ini adalah sebagai berikut. Anda meletakkan botol yogurt bayi di tempat kosong dan duduk dengan pengering rambut konstruksi. Berikut ini hanya porting ulang 20 kontainer berbeda dari susu berbeda yang saya tidak pernah berhasil meletakkannya dengan baik, tanpa lipatan dan gelembung. Rupanya Anda perlu pagar semacam instalasi vakum dan kursi plastik. Secara umum, itu ternyata terlalu sulit untuk kerajinan seperti itu.
Setelah menggerutu melalui gophers, saya menemukan sepasang meter plastik Verbatim PET Transparan. Saya memutuskan untuk mencoba diffuser hanya untuk mencetak. Dan meskipun di pintu masuk ke printer, plastik tampak jernih, bagian yang sebenarnya membosankan. Ini mungkin karena struktur internal, seperti lapisan tidak mengisi volume sepenuhnya tetapi tumpang tindih dengan celah dan celah. Selain itu, jika Anda mencoba untuk memproses bagian dengan amplas untuk permukaan yang lebih halus, kami mendapatkan lebih banyak anyaman. Namun, inilah tepatnya yang saya butuhkan.
Saya terlalu malas untuk repot dengan mount untuk diffuser, jadi saya menambahkannya ke lem panas. Jadi desain saya sekarang kondisional dapat dilipat. Saya bisa bingung dengan penemuan semacam kait, tapi saya sudah kehabisan probe plastik transparan. Jadi biarkan panas meleleh.


Firmware
Untuk lampu tanda LED, Anda tidak perlu secara khusus masuk ke pinggiran mikrokontroler - cukup beberapa fungsi untuk bekerja dengan GPIO sudah cukup. Tetapi karena modul ini merapat dengan platform Arduino, lalu mengapa tidak mengambil keuntungan dari ini?
Pertama, beberapa definisi dan konstanta
Ini menentukan jumlah piksel dalam matriks saya, nomor pin dan kecerahan maksimum LED (selama debugging, nyaman untuk mengaturnya pada 50 sehingga tidak akan membutakan mataku)
LED di matriks saya diatur dengan cara yang agak tidak jelas - zigzag. Karena itu, untuk efek yang berbeda, saya harus memberi nomor baru.
Untuk mengontrol LED, saya tidak menemukan kembali roda dan mengambil
perpustakaan yang sudah jadi untuk bekerja dengan LED WS8211 . Antarmuka perpustakaan sedikit dikapur. Beberapa fungsi tambahan (misalnya, mengkonversi HSV ke RGB) juga macet di sana.
Pertama, papan dan perpustakaan WS8211 perlu diinisialisasi.
Langkah pertama adalah mengatur sinyal POWER HOLD ke kesatuan - ini akan menjadi sinyal ke chip PT1502 bahwa mikrokontroler telah berakhir dan berfungsi dengan baik. Microcircuit, pada gilirannya, akan secara teratur memasok listrik ke mikrokontroler dan LED selama sinyal HOLD diatur ke satu.
Selanjutnya, kaki untuk mengontrol LED pada output dan tombol pada input dikonfigurasi. Setelah itu, Anda dapat menginisialisasi perpustakaan WS8211.
Karena ini adalah perangkat yang cukup otonom, tidak mungkin untuk membiarkan mikrokontroler tetap dalam keadaan yang tidak dapat dipahami dan melahap seluruh baterai. Untuk melakukan ini, saya memulai pengawas waktu selama 2 detik. Penghitung waktu akan dimulai kembali dalam loop program utama.
Sekarang Anda perlu mendefinisikan beberapa fungsi tambahan. Perpustakaan WS8211 menyimpan buffer dengan nilai warna setiap LED. Bekerja dengan buffer secara langsung sangat tidak nyaman, karena saya menulis fungsi sederhana untuk menulis nilai RGB ke LED tertentu
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; }
Tetapi dalam kebanyakan kasus, dalam model warna RGB, penghitungan warna tidak terlalu nyaman, atau bahkan tidak mungkin. Misalnya, saat menggambar pelangi apa pun, akan lebih mudah digunakan dengan
model warna HSV . Warna setiap piksel diatur oleh nilai nada warna dan kecerahan. Kejenuhan dihilangkan karena kesederhanaan (maksimum digunakan). Nilai rona dikurangi hingga kisaran 0-255 (bukan standar 0-359).
void setHue(uint8_t led_idx, int hue, int brightness) {
Fungsi ini diambil dari perpustakaan Ai_WS8211 dan sedikit diajukan. Dalam versi asli fungsi ini dari perpustakaan ada beberapa bug karena warna pada pelangi ditampilkan dengan tersentak.
Mari beralih ke implementasi berbagai efek. Setiap fungsi dipanggil dari loop utama untuk menggambar satu "bingkai". Karena setiap efek beroperasi dengan parameter yang berbeda di antara panggilan, mereka disimpan dalam variabel statis.
Ini adalah efek paling sederhana - semua LED diisi dengan satu warna, yang berubah dengan mulus.
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); }
Efek selanjutnya lebih menarik - ini menampilkan pelangi di sepanjang kontur matriks, dan warna-warna dalam pelangi secara bertahap bergeser dalam lingkaran.
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); }
Dan efek ini mengisi seluruh matriks dengan warna acak, yang pertama kali menyala dengan lancar, dan kemudian juga keluar dengan lancar.
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); }
Kelompok efek berikutnya menarik beacon berkedip berbeda. Jadi, misalnya, seorang anak suka membuat buldoser dari magnet dan flasher oranye akan sangat berguna di sana.
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); }
Secara teknis, efeknya terlihat seperti titik terang yang bergerak di sepanjang matriks. Tetapi untuk membuatnya tampak indah, LED tetangga secara bertahap memudar saat Anda menjauh dari titik utama. Oleh karena itu, saya memerlukan fungsi yang menghitung kecerahan yang sama ini.
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 adalah posisi bersyarat tertentu dari titik kecerahan bercahaya, dipetakan ke kisaran loopback 0-255. ledPos adalah posisi LED (ditampilkan pada rentang yang sama) yang kecerahannya perlu Anda hitung. Jika perbedaan posisi lebih besar dari delta, maka LED tidak menyala, dan semakin dekat ke posisi, semakin terang itu bersinar.
Atau, misalnya, lampu berkedip polisi merah-biru
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); }
Karena kita berbicara tentang mobil, maka lampu lalu lintas di sini bukan masalah untuk diterapkan.
Ini adalah fungsi yang mencakup berbagai sinyal lalu lintas di berbagai posisi.
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(); }
Sudah waktunya untuk menghidupkannya kembali. Lampu lalu lintas beroperasi sesuai dengan program khusus yang didefinisikan dalam semacam bytecode. Pelat menggambarkan mode dan waktu mode ini harus dihidupkan.
enum TRAFFIC_LIGHTS { NONE, RED, YELLOW, GREEN }; struct trafficLightState { uint8_t state; uint16_t duration; }; const trafficLightState trafficLightStates[] = { {NONE, 1},
Sebenarnya fungsi itu memproses semuanya
void trafficLights() { static uint8_t curStateIdx = 0; static unsigned long curStateTimeStamp = 0;
Setelah mencapai interval waktu yang ditentukan, mode lampu lalu lintas berikutnya dihidupkan dan hitungan mundur dimulai lagi.
Efek terakhir yang cukup membuat imajinasi saya adalah tanda bintang. 5 LED acak menyala pada kecerahan acak dan kemudian mati dengan lancar. Jika satu bintang padam, maka bintang lain di tempat acak akan menyala.
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); }
Di suatu tempat di sini serangga jahat merayap masuk. Terkadang bintang-bintang menyala dengan tajam, atau sebaliknya tiba-tiba padam. Tapi jujur saja, saya terlalu malas untuk mengetahuinya - itu terlihat sangat normal.
Sudah waktunya untuk berpikir tentang menghemat baterai. Saya sudah memberikan nilai konsumsi dari semua ini. Jika Anda tidak berpikir untuk mematikan daya, LED akan memakan baterai dalam beberapa jam. Fungsi ini bertanggung jawab untuk mematikan daya setelah 90 detik tidak aktif. Awalnya, itu 60 detik, tetapi dengan permainan nyata ini tidak cukup, dan 2 menit entah bagaimana panjang.
void shutdownOnTimeOut(bool resetTimer = false) { static unsigned long periodStartTime = 0; if(periodStartTime == 0 || resetTimer) { periodStartTime = millis(); return; } if(millis() - periodStartTime >= 90000UL) { periodStartTime = 0; shutDown(); } }
Sebenarnya matikan terjadi sebagai berikut.
void shutDown() { clearPixels(); ws2811.sendLedData(); wdt_disable(); digitalWrite(POWER_EN_PIN, LOW);
Jika pengguna menekan tombol, timer diset ulang. Setelah waktu yang disetel berlalu, fungsi ini mengatur sinyal HOLD ke nol, yang merupakan perintah PT1502 untuk mematikan daya. Watchdog, omong-omong, juga perlu dihentikan, jika tidak setelah 2 detik akan membangunkan sistem dan menyalakan daya lagi.
Akhirnya, loop utama yang memulai semuanya
Menekan tombol akan mengubah mode dan mengatur ulang timer mati otomatis. Tergantung pada mode saat ini, salah satu fungsi efek dari daftar Mode diluncurkan. Pada setiap siklus, anjing penjaga juga diatur ulang.
Jika seorang anak, katakanlah, sedang bermain mobil polisi dan setelah 1,5 menit lampu darurat dimatikan, maka kemungkinan besar setelah giliran kedua pada anak itu akan ingin terus bermain mobil polisi. Untuk melakukan ini, mode yang dipilih disimpan dalam EEPROM (nomor sel 10 dipilih dari bulldozer).
Berikut adalah video yang menunjukkan cara kerjanya.
Bootloader
Hampir semuanya siap. Tetapi ada satu hal lagi yang perlu diajukan - bootloader. Faktanya adalah bootloader standar tidak cocok untuk kita.
Pertama, ketika Anda menghidupkan daya, ia menunggu selama 6 detik - mungkin firmware akan mulai mengalir ke dalamnya. Hanya setelah kontrol ini ditransfer ke firmware utama. Ini nyaman pada tahap pengembangan, tetapi akan mengganggu pada perangkat yang sudah selesai.
Dan kedua, bootloader standar tidak tahu apa-apa tentang chip PT1502, yang akan lebih baik untuk memberikan sinyal TAHAN. Tanpa sinyal ini, rangkaian mikro berpikir bahwa mikrokontroler tidak memulai, atau, sebaliknya, ingin mematikan. Dan jika demikian, maka setelah beberapa milidetik, PT1502 akan memutus daya ke seluruh rangkaian.
Manfaat memperbaiki kedua masalah itu tidak sulit. Digispark ATTiny85 menggunakan
mikronukleus bootloader . Bootloader ini cukup mudah untuk memenuhi kebutuhan kita. Hanya perlu untuk memperbaiki definisi yang sesuai dalam file konfigurasi.
Pertama-tama, saya menyalin konfigurasi firmware standar \ konfigurasi \ t85_default ke direktori saya sendiri dan sudah membuat semua perubahan di dalamnya. Jadi kalau-kalau mudah untuk kembali ke bootloader asli.
Dalam file bootloaderconfig.h, ada pilihan cara untuk masuk ke bootloader. Dari apa yang ditawarkan di luar kotak, tidak ada yang cocok dengan kami, tetapi opsi terdekat adalah ENTRY_JUMPER. Dalam opsi ini, bootloader diakses hanya jika level tertentu muncul pada pin tertentu (jumper ditutup di papan).
#define ENTRYMODE ENTRY_JUMPER
Kami tidak memiliki jumper, tetapi ada tombol di kaki PB2. Biarkan bootloader masuk jika tombol ditahan selama 5-7 detik saat daya dihidupkan. Tetapi jika ditekan dan dilepaskan, maka transisi ke firmware utama terjadi segera.
Kita perlu mendefinisikan 3 fungsi - inisialisasi, deinisialisasi, dan benar-benar memeriksa apakah sudah waktunya untuk memasuki bootloader. Dalam aslinya, semuanya sederhana dan diimplementasikan dengan makro. Hanya 2 yang pertama akan sederhana
#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 () mengkonfigurasi pin tombol (JUMPER_PIN) ke input dan mematikan suspender di atasnya. Kami sudah memiliki pull-up di papan tulis, dan ke tanah, dan ketika Anda menekan tombol pada pin, sebaliknya, akan ada satu. Pada saat yang sama, Anda dapat segera mengkonfigurasi sinyal TAHAN untuk output dan mengatur unit untuk itu ...
Untuk penjelasan tentang aritmatika bit, misalnya, buka di
sini , dan pemahaman tentang register pengaturan GPIO di pengontrol AVR dapat diperoleh, misalnya,
dari sini .
Fungsi bootLoaderExit () kosong karena konfigurasi yang terbuka cukup cocok untuk transisi selanjutnya ke firmware utama
Fungsi bootLoaderStartCondition (), yang bertanggung jawab untuk memasukkan bootloader dalam format makro, belum cocok, dan karenanya telah menjadi fungsi penuh
#ifndef __ASSEMBLER__
Fungsi dalam beberapa detik (bahkan sekitar 6-7) memeriksa status tombol. Jika tombol ini dirilis sebelumnya, maka kita tidak perlu masuk ke bootloader. Pasien dan gigih diizinkan masuk ke bootloader.
Ternyata, file bootloaderconfig.h terlibat dalam kompilasi file assembler dan kode dalam file ini menyebabkan kesalahan. Saya harus meletakkan fungsi di blok #ifndef __ASSEMBLER__
Parameter lain yang saya sesuaikan memberitahu bootloader apa yang harus dilakukan jika tidak terhubung ke USB - keluar setelah satu detik. Faktanya adalah selama break-in, sang putra sering menekan tombol dan secara tidak sengaja masuk ke bootloader. Saya tidak tahu betapa ajaibnya, tetapi jika bootloader tidak melihat koneksi USB, itu bisa secara tidak sengaja menimpa beberapa halaman memori. Karena itu, jika tidak ada koneksi, kita cukup keluar dari program utama.
#define AUTO_EXIT_NO_USB_MS 1000
Kami mengkompilasi ... dan kami mendapatkan kesalahan bahwa kode tidak sesuai dengan ruang bootloader yang dialokasikan untuk itu. Karena memori flash di controller sangat kecil, bootloader ditekan hingga maksimum untuk memberikan lebih banyak ruang untuk program utama. Tapi ini mudah diperbaiki di file Makefile.inc dengan mengikuti instruksi.
Kemudian saya hanya mengurangi alamat mulai bootloader menjadi satu halaman (64 byte), sehingga menambah ruang bootloader.
Jika tidak, mengkompilasi dan mengunggah bootloader menggunakan programmer USBAsp bukanlah masalah.
Kesimpulan
Itu cara yang sangat menarik dari prototipe di papan tempat memotong roti ke perangkat jadi. Tampaknya seperti tanda bahayanya biasa dari pelajaran arduino, tetapi pada kenyataannya, dalam proses kerja, saya harus menyelesaikan sejumlah masalah menarik - ini adalah perjuangan dengan konsumsi, pilihan basis elemen, dan desain kasing, dan membawa firmware dengan bootloader ke pikiran. Saya sangat berharap bahwa pengalaman saya akan bermanfaat bagi seseorang.
Mungkinkah ini lebih mudah? Tentu saja bisa. Saya pikir semuanya bisa dilakukan dengan transistor. Sayangnya
, saya membaca
artikel ini setelah saya menyolder papan. Saya akan melihat artikel sebelumnya - saya akan melakukan segalanya pada TP4056 populer yang sama - lebih mudah untuk menyoldernya. Bagaimanapun, konverter DC-DC, yang ada di dalam PT1502 pada perangkat ini, untuk kebaikan, tidak diperlukan. Namun, sebuah studi praktis tentang rangkaian mikro PT1502 berguna bagi saya untuk proyek saya yang lain, serta kemampuan untuk menyolder rangkaian mikro dalam paket QFN20.
Akhirnya, inilah tautan ke proyek saya:
Kode firmwareSirkuit dan papanModel housing dan diffuserModel STL siap untuk dicetak