[Giriiş]
Mikrodenetleyici teknolojisi, modern endüstriyel otomasyon, elektronik, elektrik mühendisliği ve Nesnelerin İnterneti'nde (IoT) vazgeçilmez bir ana teknolojidir. Hayatlarımız giderek daha akıllı hale geldikçe mikrodenetleyici teknolojisi, akıllı pirinç pişiriciler, akıllı hoparlörler ve daha fazlası gibi günlük hayatımızın neredeyse her alanına nüfuz etti.
Bunu akılda tutarak, "51 Mikrodenetleyiciyi Yeniden Öğrenmek" makale serisi, yeni başlayanların mikrodenetleyici teknolojisine başlamalarına yardımcı olmayı amaçlamaktadır. En basit görev olan-tek bir LED'i açmakla- başlayacağız ve yavaş yavaş düğme kontrolleri, LCD1602 ekranı, DS18B20 ve DS1302 sıcaklık sensörleri ve iki mikro denetleyici arasındaki iletişim gibi modülleri uygulamaya geçeceğiz. Ayrıca UART, I²C ve SPI gibi donanım iletişim protokollerini de ele alacağız. Bu kavramları C programlama teknikleriyle birleştirerek programlama yaklaşımlarını göstermek için{10}gerçek dünyadaki mühendislik projelerini kullanacağız ve modüler programlama elde etmek için C işaretçilerini ve yapılarını esnek bir şekilde uygulamanıza olanak sağlayacağız.
Şimdi ana konuya dönelim: Bir LED'i kontrol etmek ve nefes alan ışık efekti oluşturmak için 51 mikrodenetleyici kullanmak.
[Solunum Işıkları Nasıl Çalışır]
Öncelikle nefes alan ışık efektinin nasıl çalıştığına bir göz atalım.
Nefes alan bir ışık yavaş yavaş parlıyor ve sonra yavaş yavaş sönüyor, bu döngü nefes almaya benzer bir şekilde tekrarlanıyor. Bununla birlikte, bir mikro denetleyicinin pinleri yalnızca 1 (açık) veya 0 (kapalı) çıkışı verebileceğinden, kademeli bir geçiş efekti nasıl elde edilebilir?
Bunun nedeni ise gözlerimizde görmenin kalıcı olmasıdır. Bir şeye baktığımızda gözlerimizin oluşturduğu görüntü 0,04 saniye boyunca kalıcı olur (bu rakam internette bulunmuştur).
0,04 saniyeye göre hesaplarsak bu 40 ms'ye denk geliyor. Dolayısıyla LED 20 ms süreyle açılıp kapandığında insan gözüne sürekli yanıyormuş gibi görünür.

Bir LED'in 20 ms boyunca yanıp sönmesinin ve 20 ms boyunca sönmesinin etkisi, sürekli açık kalmasıyla aynı mıdır?
Haha, kesinlikle farklı. Işık her 20 milisaniyede bir açılıp kapandığında gördüğümüz etki, sürekli açık kaldığı duruma göre daha sönüktür. Sürekli yanan bir ışığın parlaklığının %100 olduğunu varsayarsak, her 20 milisaniyede bir açılıp kapanan ışığın parlaklığı %50 olur. Buna dayanarak LED'in parlaklığını ayarlayabiliriz.

Bu noktada LED'in parlaklığını (yüksek seviyenin süresini 40 ms'lik döngü içerisinde ayarlayarak) ayarlayabiliriz. Bu, iyi bilinen PWM (Darbe Genişlik Modülasyonu) parlaklık kontrolü yönteminin arkasındaki prensiptir ve yüksek seviyenin süresini ayarlamak, görev döngüsünü ayarlamaya eşdeğerdir (yani, yüksek seviyenin süresi, toplam döngüye bölünür: %20/40=50).
Burada en önemli faktör bu görev döngüsüdür. Örneğin, süre 20 ms ise ve LED 10 ms açık ve 10 ms kapalı arasında değişiyorsa algılanan parlaklık hala %50'dir (yani görev döngüsü %10/20=50'dur).
Şimdi bunun programda nasıl uygulandığını görelim.
[Programın Uygulanması]
Bir LED'i açma
Öncelikle bir LED'i açarak başlayalım, ardından yavaş yavaş nefes alan bir ışık efekti uygulayacağız. Kullanacağımız donanım aşağıdaki gibidir:
| Geliştirme Kurulu | ZeroOne Mikrodenetleyici Eğitimi Geliştirme Kartı |
|---|---|
| Mikrodenetleyici Modeli | STC89C52 |
| LED Arayüzü | P4^4'ü sabitle |

Şematikten LED'in mikro denetleyicinin P4^4 pinine bağlı olduğunu görebiliriz. Mikrodenetleyici 1 çıkışı verdiğinde LED yanar; 0 çıkışı verdiğinde LED kapanır. Bu nedenle, bir LED'i açma programı aşağıda gösterildiği gibi oldukça basittir:

Bir LED'i açma programı oldukça basittir; Eminim herkes bunun nasıl yapılacağını biliyordur.
LED Parlaklığını Ayarlama
Daha sonra, parlaklığı ayarlamamıza (yani görev döngüsünü ayarlamamıza) olanak tanıyan bir işlevi aşağıdaki gibi uygulayacağız:

Görev döngüsünü depolamak için statik bir "görev_döngüsü" değişkeni tanımlayın. 'Bayrak' 1 olduğunda, görev döngüsü kademeli olarak 255'e yükselir, ardından 'bayrak'ı 0'a ayarlayın ve 'görev döngüsü' kademeli olarak 255'ten 0'a düşer. Bu döngüyü tekrarlayın.
Haha, bu noktada solunum ışığının zaten çalıştığını düşünebilirsiniz ama değil. Bana inanmıyorsanız yukarıdaki kodu kendiniz deneyin.
Peki sorun tam olarak nerede?
Sorun, parlaklık-ayar işlevi `set_led_luminance()`'a doğrudan çağrı yapmamızda yatıyor. Bu işlevin bir döngüyü tamamlaması 40 ms sürer; bu, görev döngüsünün bu 40 ms boyunca değiştirilemeyeceği anlamına gelir; aksi takdirde parlaklık ayarı çalışmaz. `breath_led` fonksiyonuna bir kez daha bakalım. Görev döngüsünü ayarlamak için 'set_led_luminance()'a yapılan her çağrıdan sonra, 40 ms beklemeden hemen 'duty_cycle' değerini değiştirir.
Bu noktada `duty_cycle` değerini 40 ms geçtikten sonra güncellemek için bir yazılım zamanlayıcısı eklememiz gerekiyor. Değiştirilen program aşağıdaki gibidir:

Not: Zamanlayıcı süresinin 40 ms'den büyük olması gerekir ("s_breathCounter" değerinin 255'ten büyük olması gerektiği anlamına gelir), ancak bunu döngünün katlarına ayarlamak en iyisidir. Örneğin döngümüz 255 ise (yani 0'dan 255'e kadar 256 değer), onu bu değerin iki katına ayarlayabiliriz: 256 * 2 - 1=511 (yani, 0'dan 511'e kadar 512 değer).
Ve işte,-nefes alan ışığımız tamamlandı! Bu çok basit değil mi? (* ̄︶ ̄)
Sırada bugünün bonus bölümü var.
Nefes alan ışık efektini elde etmiş olsak da, kod yeterince kısa veya zarif değil-bir sürü if ve else ifadesi kullanıyor. Bakalım bunu daha da basitleştirebilecek miyiz?
Öncelikle set_led_luminance() fonksiyonunun bu kısmını aşağıdaki şekilde gösterildiği gibi basitleştirelim.

Bunu basitleştirmeden önce, C: bit düzeyinde AND işlemi hakkında kısa bir ipucu verelim.

Bundan, ister 1 ister 0 olsun, 0 ile bit düzeyinde bir AND işlemi gerçekleştirmenin 0 ile sonuçlanacağını biliyoruz.
İster 1 ister 0 olsun, bit düzeyinde VE işleminin 1 ile gerçekleştirilmesi orijinal değerle sonuçlanır.
Kolaylık olması açısından onaltılı gösterim kullanıyoruz ("0x" ön ekiyle); örneğin 0xff ondalık sayı olarak 255'e karşılık gelir. Öyleyse,
0xff'den küçük veya ona eşit bir sayı 0xff ile ANDlendiğinde, aşağıda gösterildiği gibi sonuç sayının kendisi olur.

0xff ile 0xff'ten büyük bir sayı arasında bit düzeyinde VE işlemi gerçekleştirirseniz ne olur?

Sonuç, bu sayının (0xff + 1)'ye bölümünden kalandır (yani sonuç hala 0 ile 0xff arasındadır).
Bu bitsel AND işlemiyle yukarıdaki kod şu şekilde basitleştirilebilir:

Bu şekilde s_Counter'ın değeri her zaman 0x00 ile 0xff aralığında olacaktır.
Benzer şekilde yukarıdaki Breath_led işlevindeki yazılım zamanlayıcısı da aşağıdaki şekilde basitleştirilebilir:

3. satırda 0x1ff ondalık sayı olarak 511'dir. s_breathCounter'ın değeri (0x1ff+1) veya 512 olduğunda durum doğrudur, çünkü 512 & 0x1ff=0. Önündeki ünlem işareti bit düzeyinde DEĞİL işlemini belirtir (kesin olarak söylemek gerekirse, "s_breathCounter" değeri 512'nin katı olduğunda koşul karşılanır; bu, "s_breathCounter"ı sıfırlama ihtiyacını ortadan kaldırır. Umarım bu açıklama gayet açık-lütfen iyice düşünün). Koşul karşılanırsa görev döngüsü artmaya veya azalmaya başlar.
Ancak görev döngüsü aralığı 0 ile 255 arasında değil mi? Neden 5. satır da 0x1ff (511) oluyor? Endişelenmeyin-8. satıra bakın. Tekrar 0xff'yi çıkarırız, böylece görev döngüsü aralığı 0 ile 255 arasında kalır.
7-10 arasındaki satırlar şu anlama gelir: 'görev_döngüsü > (0xff)', yani 256–511 olduğunda, 0xff'yi çıkarmak, 1'den 255'e çıkarmaya eşdeğerdir, dolayısıyla parlaklık giderek artar.
Ne zaman görev_döngüsü<= 0xff, the duty_cycle increases from 0 to 255, while the set brightness is 255 - duty_cycle. This effectively decreases the brightness from 255 to 0, causing the light to gradually dim. This achieves the breathing light effect.
Haha, sadeleştirmemizin bittiğini mi sandın?
Hayır, hayır, hayır
Aslında 7'den 10'a kadar olan satırlar daha da basitleştirilebilir. Mutlak değer fonksiyonunun kullanışlı olduğu yer burasıdır.
Ne? Mutlak değer fonksiyonunu neden kullanmalıyım?
10. satıra bakın: 0xff - görev_döngüsü, görev_döngüsü - 0xff'ye eşdeğerdir ve ardından mutlak değer alır. Tamam, işte basitleştirilmiş kod:
Mutlak değeri almak için makro fonksiyonu aşağıdaki gibidir
Son olarak basitleştirilmiş kodun tamamını aşağıya ekledim

Ne düşünüyorsun? Çok basit değil mi? (* ̄︶ ̄)




