Etiket arşivi: EFM8BB1

EFM8BB1 LCK ile Geliştirme

LCK muhtmelen “low-cost kit” sözcüklerinin kısaltması.
LCK, 6 dolar gibi bir paraya satılan küçük bir geliştirme kartı. Bizimki gibi ülkelerde 6 birim gavur parası bile yeri geldiğinde üzerinde düşünülmesi gereken bir bütçe olabilir elbette. Ama normal ölçütlerde bu, bu kartın adının hakkını vermesini sağlayan bir fiyat:
En başta, bu paranın 1 dolarını EFM8BB10F8G QSOP24 mcu’ya , 5 dolarını da üstündeki debugger’a verdiğimizi düşünelim. Fena değil.
Sonra, Simplicity programını hemen kullanmaya başlamak ve lisanslı bir Keil C51 derleyicisine erişmek faydalarını da hesaba kattığımızda epey iyi oluyor.
Ben bu board’u, radyo haberleşmeleriyle uğraşırken,bir seri port dönüştürücüsü kullanmadan pc’den giden/gelen radyo paketlerini izlemek için kullandım.
Board’un üstündeki debugger’ı VCOM port olarak kullanmak için iki jumper kısa devre etmemiz gerekiyor:

Artık işlemcinin seri portu debugger’a bağlı. Fakat bilgisayar bunu bir comport olarak görmüyor. Silicon Labs’ın 8 bit işlemciler sayfasından Toolstick Terminal programını indirmeniz gerek. Bu çok basit program zaten kullanmayı planladığınız seri terminal programının yerine geçecek.
PC bağlantısı bana çoğu zaman lazım olduğu üzere, sadece bir aptal terminal olarak değil, bazı işleri otomatize etmek ve veriyle gerçek zamanlı olarak oynamak için gerekiyorsa zaten bildiğimiz yoldan devam etmemiz gerek : Seri dönüştürüc + kendi pc uygulamamız.

Ben, radyo haberleşmesinde, aslında uç nokta olacak (yani bir arayüzü olmayan) cihazların testlerini yapmak için bunu kullanıyorum. Hex veri yollamak için BB1’e bir Send_Byte( ) fonksiyonu yazmak “fazlasıyla” yeterli:

void Send_Byte (char c)
{
  SCON0_TI = 0;
  SBUF0 = c;
  csum += c;
  while(SCON0_TI == 0) ;
}

Fakat, madem terminalden verilere bakacağız desimal sayılar görelim hatta açıklayıcı string’ler yazdıralım falan diyorsak o zaman printf( ) ‘i kullanmalıyız. 8051 çeşitlilik konusunda yeryüzündeki eklembacaklılarla yarışacak kadar türe sahip olduğu için, <stdio.h> include etmenin bu işi çözmesini beklemek yaşadığımız dünyayı hafife almak olur.
printf( ) bir port yazması için putchar fonksiyonunu çağırır ve bu fonksiyon
<program kurulum yeri> \developer\toolchains\keil-8051\9.60\LIB dizininde bir C dosyası olarak yer alıyor. Oraya gidip bunu modifiye edebiliriz. Bakarsanız, orada saçma sapan bir yazdırma kodu olduğunu göreceksiniz.
Veya daha kökten bir çözüm olarak kendiniz, bu işleri yaptığınız C dosyasının içinde aynı adda bir fonksiyon yazarsınız:

char putchar (char c)

Bunu, benim yukarıdaki Send_Byte’ın aynısı yapsanız çalışacaktır. Keil’in kendi dosyalarını değiştirmek zorunda da olmazsınız. printf sizin putchar’ı çağıracaktır (object scope).

Gönderme işlemini belli bir zamanlama (scheduling) içinde firmware’in yapmasını istiyorsanız sprintf kullanıp buffer’ı haberleşme thread’inin yollamasına bırakmak da kullanılabilir.

Burada not etmek istediğim bir konu var: Sadece bir printf() çağrısı yapmak için (örneğin bir int değerini desimal olarak göstermek için) programımıza binen ek kod yaklaşık 1k civarındadır).
Bu, güzel bir örnek. Çünkü EFM8BB1 gibi bir mcu’ya 1k ‘lık bir programla hayret edilecek kadar karmaşık işler yaptırabilirsiniz aslında. Yani bu, standart kütüphane fonksiyonları kullanarak low-code programcılık yapmak istemenin neye malolduğunu göstermesi açısından güzel bir örnek.

Yazının ileride ekleyeceğim bölümlerinde bunu bir PC’ye bağlamadan beslemenin yöntemlerinden söz edeceğim.

Radyo Modülü Bağlamak

nRF24L01+ modülünü çalıştırmak için gerekli sinyalleri şu portlara atadım:
* IRQ : P00
* SCK : P06
* MISO : P07
* MOSI : P10
* CSN : P11
* CE : P12

Slave modüller için nRF24L01+ radyosunun çalışması üstüne yapılacak denemeleri bu board üzerinde yapmak, geliştirme aşamasında kullanışlı oldu. nRF24’ün konfigürasyon modelini şu şekilde tanımladım:

nRF24L01+ radyo modülüne EFM8BB1 ile erişmek için gerekli fonksiyonlar:

EFM8BB1 IDLE Mode

Küçük işlemcilerle yaptığımız uygulamaların azımsanmayacak bir kısmı şu tarife uyar: Bunlar “belli bir olay“a tepki vermek için “bekleyen” programlardır.

Örneğin bir butona basıldığında bir işlem yapan bir program düşünelim. Program, butona basılmadığı zaman sadece butona basılmasını bekliyor (aslında belli zamanlarda yaptığı başka işler olması bu hikayenin teorisini çok değiştirmiyor).

İşte bu belli olayı beklediğimiz uzun müddetler boyunca işlemciyi boşa alarak akım tüketimini azaltabiliriz. Beklediğimiz olay için bir kesme kurmamız yeterlidir. EFM8 işlemcilerdeki IDLE mode, bu senaryo için uygundur.
– Idle mode’da iken işlemciyi istediğimiz saat hızında bekletebiliriz ve istediğimiz modülleri açık tutabiliriz.
-Idle mode’dan çıkmak için mevcut kesmelerin herhangi birini ya da bir kaçını kullanabiliriz.
– Çıkış bir interrupt fonksiyonuna atlama kadar hızlı olacağı için olaya tepkimiz hızlı olabilir.
Yani, Idle mode kullanımı basit ve sağladığı tasarruf bizim tercihlerimize bağlı bir çalışma ayarıdır. Bununla beraber, işlemci aslında kapanmadığı için bu en yüksek düzeyde güç tasarrufu sağlayan mod değildir.

Bir uygulama geliştirmek çalışan bir kod elde etmekten çok fazlasıdır. Bir uygulama tasarlamak temelde, seçimler yapmak demektir. Bu seçimler gereksinimlerinize en uygun seçimler olurlarsa başarılı olursunuz.
İşte bu yazının konusu olan Idle mode, sıklıkla ve zamanlamasını tam bilemediğiniz anlarda görev başına geçmesi gereken bir programınız varsa doğru bir seçim olabilir.

Tipik bir kullanımda kod
– Beklerken gerekli olmayan birimleri kapatır,
– Makul bir çalışma frekansına yavaşlar,
– IDLE mode’dan çıkış kaynaklarını ayarlar,
– Ve işlemciyi idle mode’a sokar..


IDLE mode’a giriş:

   TMOD = 0x31;
   TH0 = 0;
   CKCON0 = 0x02;  // tmr0 prescaler SYSCLK/48 olsun
   CLKSEL = 0x40;  // sistem saatini 1,53MHz'e indir..
   TCON = 0x11;    // TMR0 çali$sin, EX0 kenarda tetiklemeli
   IE = 0x83;      // = 0x83 TMR0 ve EX0 kesmeleri devrede
   PCON0 = 0x01;   // IDLE=1
   PCON0 =  PCON0;
   _nop_();

Yukarıdaki kod parçası işlemciyi EX0 ve TMR0 kesmeleri ile normal çalışmaya geri dönmek üzere boşa çıkarır.
İşlemcinin kötü bir şans eseri IDLE=1 komutu işlenirken gelecek bir kesme yüzünden hiçbir zaman IDLE durumundan çıkamayacak olması ihtimaline karşılık, bu komutun hemen ardından bir byte’tan büyük opcode’lu bir komut işletilmelidir. PCON0=PCON0 dummy komutu bu yüzdendir.
İşlemcinin normal çalışma moduna dönünce, kendisini uyandıran kesme kodunu işledikten sonra _nop_() ile devam edeceğini de not edelim. Yani uyanma kodu, buradan sonra yer almalı (muhtemelen yapılacak ilk iş çalışma hızını arttırmak olacaktır).
Bu şekilde her şeyi tek düzeyli düz bir while(1) süper döngüsü içinde halledebiliriz.
Son bir not olarak da EFM8BB1 için IDLE mode çalışma akımı vereyim. 1,53MHz için işlemci akımı 550 uA ‘dir. ( -G sıcaklık sınıfındaki çipler için )

alpha-S

decolit alpha-s rgb led lamba modüllerinin çıplak kart halleri

Üç renkli LED elemanlar kullanarak rengarenk aydınlatmalar yapmak son zamanlarda bana da çekici gelmeye başlamıştı. Aslında bunun için hazır pek çok seçenek var ancak ben küçük bir board yapıp, üzerine kendi seçtiğim RGB LED’leri yerleştirip kendi tanımladığım kumanda şemasını yürütebilecek bir şeyler yapmak istedim. Sonrasında bu küçük kartları istediğim yerlere yerleştirip, onları değişken renklerle yakmak niyetindeyim. Noktasal olarak büyük bir ışık yoğunluğunu hedeflemiyor olsam da bir board üstüne 5 tane PLCC kılıf LED koymaktan kaçınmadım.

Kartın boyutları 58mm x 13.5mm oldu.


Deneme amacıyla ürettiğim board’ların üzerine Cree marka CLP6C-FKB model LED’ler taktım. Sonuç gayet güzel.

LED’leri doğrudan kartların besleme hattı üzerinden besliyorum ve her renk için bir pull-down transistör ile sürüyorum. Şemada 100R olarak gözüken seri dirençler besleme gerilimine göre seçilmesi gereken denge dirençleri.

Bu projeyi bir hafta sonu çalışması olarak uğraşmaya değer bulmama neden olan şey bu kartları seri haberleşme ile kumanda edilebilir ve kaskat bağlanabilir yapmam. Bu şekilde iş basit bir renk ayarından çok daha fazlası olabilir.

Aslında arka arkaya bağlanabilir renkli LED deyince akla gelen Neopixel diye bir LED çipi de var. Bunun Çin malı kopyaları ucuza bulunabiliyor. Ama bunlar adreslenebilir modüller değiller. Ben uzun clock katarları üretmek zorunda kalmak yerine doğrudan UART kullanan bir şey yapmak istedim. Bu şekilde tek bir komutla tüm lambalara aynı anda aynı şeyi yaptırabilmek mümkün. Veya istediğiniz belli bir adrese doğrudan komut verebilirsiniz. Eğer hızlı animasyonlar yapmak söz konusu ise bu daisy-chanining’e göre çok büyük bir fark yaratabilir.

Her modülün sol tarafında uart’ının RX ‘i sağ tarafında ise TX çıkışı bulunuyor.

Modülleri arka arkaya (kaskat) bağladığımızda soldan sağa birbirlerini sürmüş olacaklar.Bir modül sol tarafından bir paket aldığında bu paket onu adreslemiyorsa veya bir genel çağrı adresi taşıyorsa o komutu sağ taraftaki TX pininden aktaracaktır. 0-3V gerilim düzeyinde çalışıyor olsak bile bu şekilde alavere yaparak uzun bir lamba zincirini bir uçtan kumanda edebilmeyi umuyorum. Bu arada, 24mA kaynak akımı verebilen bir logic buffer kullanmakla sonraki modül ile aramızdaki kablonun kapasitesinden kaynaklanacak yavaşlamaları azaltmış olacağız. Şemadaki paralel kondansatöre takılmayın. Orada bir komponent yeri olsun istedim sadece.

Benim “adreslenebilir” bir lamba modülünden beklediğim kumanda işlevleri her kanal için ayrı ayrı olmak üzere;
* Açma ve kapama denetimi,
* Çıkış gücünü ayarlama,
* Rampalı açma/kapama,
* Çıkış gücünü rampalı değiştirme,
* Rampa sürelerini değiştirebilme
* Çıkış gücünü çıkışa yansıtmaksızın değiştirebilme (komut verildiğinde uygulanmak üzere)
* Tüm modüllere eşzamanlı komut verebilme

Bu fonksiyonlar basit seri komutlarla kumanda edilebildiğinde ana kontrolcünün karmaşık efektleri yürütmesi kolayca mümkündür.

Firmware Yapısı

Ana program, dört ayrı thread’den oluşur:
+ Sürekli olarak çalışan ve her aşaması belirli bir zaman aşımı denetimi ile korunan bir seri haberleşme thread’i..
+ Zaman çoğullamalı olarak çalışan ve her biri bir renk sürücüsüne çıkış veren üç özdeş paralel renk kontrol thread’i.

Her renk kontrol thread’i 4 farklı duruma dair task’ler çalıştırır. Her task’in o renk için bir “durum” olduğunu düşünün. Bunlar;
OFF
ON
RAMPUP
RAMPDOWN
task’leridir. OFF ve ON task’leri kalıcı durumlardır. Bir komut alınmadığı sürece denetim bu task’i çalıştırmayı sürdürür. RAMP task’leri ise belli bir süre boyunca çalışan geçiş durumlarıdır. Set değeri değiştirildiğinde (kademeli geçiş komutu verilmişse) veya kademeli açma kapama komutları verildiğinde yeni set değerine ulaşılıncaya dek bu adımlarda kalınır.

Çıkış kontrol task’lerinin zaman bölümlemesi 10,7ms ‘dir. Yani her 10,7ms’de bir sıradaki renk kanalının geçerli durumuna dair task çalışır. Ve bu çevrim art arda sürekli devam eder. İki rezerve zaman slotunun da eklenmesiyle, bir tam çevrim 5×10,7 = 53,5ms ‘de tamamlanır. Bu demektir ki, çıkış güncellemeleri 53,5ms çözünürlükle yapılmaktadır. Durağan adımlarda bu önemli olmamakla beraber kademeli geçiş adımlarında bu süre değişim hızını tanımlar.

PWM

İşlemcinin üzerindeki 3 PWM kanalını da kullanıyoruz. PWM çözünürlüğü 8 bittir. PWM frekansı da 7,98 kHz’dir.
Bu projenin donanımını tasarlarken EFM8BB1 işlemcisini kullanmıştım. Firmware’e de bu işlemci üzerinde çalışarak başladım. Sonra iş deneme aşamasına gelince prototip montajları için bana yardımcı olan arkadaşımın elimdeki board’lara BB1 yerine yanlışlıkla SB1 takmış olduğunu fark ettim.
Sleepy-Bee ile Busy-Bee “hemen hemen” pin uyumludur. Ancak firmware’de oldukça yaşamsal farklar vardır. Ne yazık ki buna PCA modülü de dahil, ki bu board’da PWM’i bununla üretiyorum. O yüzden, elde olan asıl olandır mantığıyla benim firmware’i SB1 üzerinde çalışacak şekilde değiştirdim. Ancak asıl üretimim BB1 ile olacak. Çünkü bunda daha çok özellik var.
Bu işlemcilerde PWM çıkışı aktif duty cycle’da 0, pasif durumda da 1 oluyor. Ben BB1’de PCA0POL register’ı ile bu evirme işlemini donanımsal olarak halledebiliyordum. Ancak SB1’de POL register’ı yok. Bu durumda çıkış gücü 0 yazarsak en büyük 255 yazarsak en küçük olmuş olur. Ayrıca, kanalı kapatmak istediğimizde önce çıkışa 255 yazmalı sonra da _ECOM bitini sıfırlamalıyız.
8 bit PWM’de set değerini PCA0CPHx register’ına yazmak gerek. Daha yüksek çözünürlüklü PWM ayarlarında kullanılabilen auto-reload register’larının kullanım seçimi bu modda kullanılmıyor.
Çalışma sırasında PCA modülü ve sayacı sürekli devrede. Bir kanalın kapalı olması gerektiğinde
PCA0CPMx = 0x02; yazarak o çıkışın toggle olmasını engelliyorum. (PCA0CPHx = 255)
PCA0CPMx = 0x42; yazdığımda da PWM çalışmaya devam ediyor.

BB1 kullanan versiyonda işler biraz daha değişik. Burada o kanalın POL bitini = 0 yapıp _ECOM=0 yapmam çıkışı kapatmaya yetiyor. Duty cycle’ı değiştirmeme gerek yok.
Ayrıca, BB1 ‘de PWM’i center-aligned kullanıyorum.

Kontrolcü, komutlar tamamlanmadan yeni komutu işleme almaz. Ancak rampa komutları icra edilirken seri haberleşmeden yeni bir komut alınırsa komut kuyruğa eklenir ve geçerli komut tamamlanınca işleme alınır. Ani açma / kapama /set değeri değiştirme komutlarında bunun olma olasılığı yoktur. Rampa işlemlerinde komutun tamamlanması rampa ayarına bağlı olarak uzun sürebileceği için bu olasılık vardır.

Üç renk kanalı birbirinden tamamen bağımsız olarak çalışabilir ve kumanda edilebilirler. Ancak tamamen eşzamanlı olarak da kumanda edilebilirler.

Bir zincirde 128 adet modül arka arkaya bağlı olabilir. Her modülün adresi sabittir. Bir ağda aynı adresli iki modül olması çalışmayı bozmaz, sadece bu modüller ayrı ayrı ayarlanamamış olurlar. 0x81 adresi tüm modüllerin ortak çağrı adresidir.

Seri portun haberleşme hızı 38400bps’dir. Yazdığım ilk versiyonda modül aldığı paketin adresi kendi adresi değil ise paketi tamamlanmasını beklemeden iletmeye başlıyordu. Ancak sonra, kesme kodunu kısa tutma takıntım yüzünden bu özellikten vazgeçtim. Bu durumda, her modül için paket aktarım gecikmesi yaklaşık 1,6ms’dir.

Haberleşme Yapısı

Paket boyu tüm komutlar için sabittir ve 6 byte’tır. Her paket sync byte’ı (0xAA) ve hedef node adresi ile başlar (0x01..0x81). 0x81 genel çağrı adresidir.
Ardından gelen cmd byte’ı paketi tanımlar. Geçerli komutlar ve kullandıkları parametrelerin anlamı şöyledir:

node: 1..128 arası, hedef adresi. 129 tüm modülleri adresler.
cmd_X : kanal komutları. anlamları şunlar:
0: Bu kanala ait komut yok.
1: forced-on : Kanal kapalıysa hemen açılmasını sağlar. Eğer açıksa out_X’te o an yazılı olan set değerini çıkışa yansıtır.
2: turn_off : Kanalı hemen kapatır.
3: ramp-up: Çıkış gücünü kademeli olarak out_X ‘te yazılmış değere yükseltir. Kanal kapalı idiyse açılır ve set değerine kademeli yükselir.
4: ramp-down: Çıkış gücünü kademeli olarak out_X’te yazılmış değere azaltır.
5: rampdown-off: Çıkış gücünü sıfır değerine kadar kademeli azaltır ve sonra kanalı kapatır.
NOT: Ramp-up ve ramp-down komutları, o anki çıkış oranı, out_X set değerinden sırasıyla yüksekse ve düşükse tek seferde yeni set değerine gelir.

Bu projede önemli bileşenin lambaların kontrolcüsü olduğunu düşünüyorum. Firmware’i tamamlayıp denemelere başladığımda işin bu yönünün geliştirilmeye oldukça açık olduğunu gördüm. Yukarıdaki komutların çalışmasını hızlı biçimde deneyebilmek için basit bir PC programı hazırladım. Aşağıda onun görüntüsünü görüyorsunuz. Üzerine tıklayarak programı indirebilir ve deneyebilirsiniz: