Fanlab

Robototexnika · Mikrokontrollerlar · 8-sinf

millis() va ko‘p vazifa

Ikki chiroq har xil tezlikda, tugma darhol — nega delay() hammasini muzlatadi?

Tajribani ochish

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

Tloop=∑𝚍𝚎𝚕𝚊𝚢(…),tjavob≤Tloop

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.

𝚖𝚒𝚕𝚕𝚒𝚜()=t−t0

millis() — plata yoqilganidan (yoki RESET dan) beri o‘tgan millisekundlar: 32 bitli `unsigned long`, Timer0 uzilishida fonda sanaladi, dasturni to‘xtatmaydi.

𝚗𝚘𝚠−toxirgi≥T⇒almashtir,toxirgi=𝚗𝚘𝚠

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.

232 ms=4294967296 ms≈49,71 kun

To‘lib qolish: `unsigned long` 4 294 967 295 dan keyin 0 ga qaytadi — 49 kun 17 soat 2 minut 47 sekunddan so‘ng.

(5−4294967290)mod232=11

Ayirma to‘g‘ri ishlaydi: unsigned ayirish ham modul 232 bo‘yicha — soat 0 ga qaytgan bo‘lsa ham 𝚗𝚘𝚠−t haqiqiy o‘tgan vaqtni beradi. 𝚗𝚘𝚠≥t+T 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 232 = 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 232 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 (5−ULED)/(220+25) Ω: 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