Robototexnika · Mikrokontrollerlar · 8-sinf
millis() va ko‘p vazifa
Ikki chiroq har xil tezlikda, tugma darhol — nega delay() hammasini muzlatadi?
Vaziyat
Robot mashinada ikki chiroq har xil tezlikda miltillashi, tugma bosilganda esa signal darhol yonishi kerak. Birinchi dastur oson yoziladi: yoq — delay(500) — o‘chir — delay(500). Lekin tugmani bossangiz, ko‘pincha hech narsa bo‘lmaydi: dastur «uxlayapti». Mikrokontroller delay() ichida nima qiladi? Platada soat bormi — millis() qanday ishlaydi va nega u 49,7 kundan keyin 0 ga qaytadi? Ikki chiroq va tugmani bitta loop() da, hech qanday delay siz qanday boshqaramiz?
Mavzu
millis() va ko‘p vazifa: delay() siz dastur. Ikki LED har xil tezlikda miltillaydi, tugma esa darhol javob beradi — vaqtni taqqoslash
Asosiy tushunchalar
- delay() CPU ni to‘xtatadi
- millis() — soat
- Holat mashinasi
Formulalar
delay() bilan: loop() ning davri — barcha delay larning yig‘indisi; tugma faqat bir nuqtada o‘qiladi, javob eng yomon holda butun davr kechikadi, qisqa bosish esa yo‘qoladi.
millis() — plata yoqilganidan (yoki RESET dan) beri o‘tgan millisekundlar: 32 bitli `unsigned long`, Timer0 uzilishida fonda sanaladi, dasturni to‘xtatmaydi.
Holat mashinasi: har vazifa o‘zining «oxirgi marta» vaqtini eslab qoladi; loop() har aylanishda faqat tekshiradi — hech qachon kutmaydi, shuning uchun tugma har aylanishda o‘qiladi.
To‘lib qolish: `unsigned long` 4 294 967 295 dan keyin 0 ga qaytadi — 49 kun 17 soat 2 minut 47 sekunddan so‘ng.
Ayirma to‘g‘ri ishlaydi: unsigned ayirish ham modul bo‘yicha — soat 0 ga qaytgan bo‘lsa ham haqiqiy o‘tgan vaqtni beradi. esa to‘lib qolganda adashadi.
Nazariya
delay() nima qiladi
`delay(500)` — mikrokontroller 500 ms davomida faqat vaqtni sanaydi: na tugmani o‘qiydi, na boshqa chiroqni boshqaradi, na Serial ga yozadi. Shuning uchun delay bilan yozilgan loop() ichida tugma faqat bitta satrda — tekshiruv payti kelganda — o‘qiladi. Namunadagi dasturda loop() 2 sekund davom etadi: tugmani 150 ms bosib qo‘ysangiz, 93 % ehtimol bilan dastur buni ko‘rmaydi. Vaqt jadvalida buni ko‘rasiz: kulrang bloklar — CPU uxlayapti, qizil chiziqchali bosish — sezilmadi.
millis() — platadagi soat
ATmega328P da Timer0 har ≈ 1 ms da uzilish chaqiradi, u esa 32 bitli hisoblagichni bittaga oshiradi — dastur nima qilayotganidan qat’i nazar, hatto delay() ichida ham. `millis()` shu hisoblagichni qaytaradi: plata yoqilganidan beri o‘tgan millisekundlar. U hech narsani kutmaydi — shunchaki «soat necha?» deb so‘raysiz. Turi `unsigned long` (0 dan 4 294 967 295 gacha); `int` ga yozsangiz, 32 sekunddan keyin xato bo‘ladi.
Holat mashinasi: delay siz miltillash
G‘oya: kutmaymiz — tekshiramiz. Har vazifa uchun «oxirgi marta qachon» vaqtini eslab qolamiz: `if (millis() - tRed >= 300) { tRed = millis(); red = !red; digitalWrite(RED, red); }`. loop() sekundiga minglab marta aylanadi: har aylanishda qizil uchun vaqt keldimi, yashil uchun keldimi — va tugma bosilganmi? Shuning uchun ikki chiroq mustaqil davrlarda miltillaydi (300 va 700 ms — delay bilan buni yozish qiyin), tugmaga javob esa bir aylanish ichida — millisekundlar. Bu holat mashinasi: dastur holatni (red, tRed) saqlaydi va vaqtga qarab o‘zgartiradi. Xuddi shu usul servo, sensor va Serial ni bitta loop() da birlashtirishga imkon beradi.
49,7 kun: to‘lib qolish
32 bitli hisoblagich = 4 294 967 296 ms ≈ 49,71 kundan keyin 0 ga qaytadi. Oddiy taqqoslash `if (now >= tRed + 250)` shu paytda adashadi: `tRed + 250` ham to‘lib, kichik songa aylanadi, `now` esa hali katta — shart har aylanishda rost, chiroq delay siz loop tezligida pirillaydi, to soat ham 0 ga qaytguncha. To‘g‘ri yo‘l — ayirma: `now - tRed >= 250`. unsigned ayirish ham modul bo‘yicha: now = 5, tRed = 4 294 967 290 bo‘lsa, 5 − 4 294 967 290 = −4 294 967 285, modul bo‘yicha esa 11 — aynan o‘tgan vaqt. Tajribada «Soat to‘lib qolishga 6 s qolganda boshlansin» rejimi 49 kunni 6 sekundga qisqartiradi.
Tugma: INPUT_PULLUP
`pinMode(BTN, INPUT_PULLUP)` pinni ichki ≈ 20–50 kΩ rezistor orqali 5 V ga «tortadi»: tugma bosilmaganda HIGH, bosilganda (pin GND ga ulanadi) LOW — shuning uchun shart `digitalRead(BTN) == LOW`. Oddiy `INPUT` da pin «osilib» qoladi: antenna kabi shovqin yig‘adi va tasodifiy 0/1 ko‘rsatadi — sariq chiroq o‘z-o‘zidan pirillaydi (sinab ko‘ring). Tugma tebranishi (debounce) va uzilishlar — «Tugma va raqamli kirish» va «Uzilishlar» tajribalarida.
Model haqida
Dastur simulyatsiya soatida ishlaydi: har 8,3 ms li qadamda loop() ko‘p marta aylanadi (sekundiga ≈ 13 ming; haqiqiy Uno da shu dastur uchun bir necha o‘n ming), tugma holati ham shu qadamda yangilanadi — shuning uchun javob 0–8 ms ko‘rinadi (haqiqatda mikrosekundlar). delay() aniq: 500 ms aynan 500 ms. millis() 1 ms qadam bilan sanaladi (haqiqiy platada 1,024 ms li tiklar, kichik tebranish bor). Osilib qolgan kirish tasodifiy ketma-ketlik bilan modellangan. LED toki Ω: qizil ≈ 12,7 mA, yashil ≈ 11,4, sariq ≈ 12,2.
Bilasizmi?
- Windows 95 va 98 da aynan shu xato bor edi: tizim yoqilganidan beri o‘tgan millisekundlarni 32 bitli sonda sanagan va 49,7 kun uzluksiz ishlagandan keyin «osilib» qolgan; Microsoft buni 1999-yilda tuzatgan (KB216641).
- Arduino da millis() ni Timer0 ning har 1,024 ms dagi uzilishi yuritadi (16 MHz / 64 / 256); yadro kodi (wiring.c) yig‘ilib qolgan 0,024 ms larni alohida sanab, har 41–42 tikda bir millisekund qo‘shib turadi — shuning uchun millis() o‘rtacha aniq, lekin ayrim qadamlari 2 ms.
Tajribada nima bor
Haqiqiy plata qo‘lda chizilgan rasmda: uni aylantirasiz, qismlarni joylaysiz, zanjirni ulaysiz, kodni yozib yuklaysiz — tok va kuchlanish o‘sha zahoti hisoblanadi, xato ulashda qism kuyadi. Ichida topshiriqlar, nazariya, test va ekskursiya bor.
Shu bo‘limdagi boshqa tajribalar
- Arduino Uno: birinchi dastur — 7-sinf
- Tugma va raqamli kirish — 7-sinf
- Analog kirish va Serial monitor — 7-sinf
- PWM: yorqinlik va ovoz — 8-sinf