USART Modülü İle Seri İletişim
Tüm mikrodenetleyici ve mikroişlemci temelli donanımlarda ve haberleşme sistemlerinde seri iletişim yaygın olarak kullanılmaktadır. Seri iletişim verinin seri bitler halinde sıralı şekilde gönderilmesini ifade eder. Her bir bitin bir uçtan bir uca iletimi bir saat darbesi çevrimi içinde gerçekleşir. Paralel iletişimde ise çok sayıda bit tek bir çevrim süresinde aynı anda iletilir. Ancak buna karşın seri iletişim daha yüksek frekanslarda veri iletimi sağladığından çok daha hızlıdır.
PIC mikrodenetleyicilerinin de çoğunda farklı hızlarda ve protokollerde seri iletişim yapmayı sağlayan modüller bulunur. Bu modüllerin en önemlilerinden birisi; RS-485, CAN bus ve LIN protokollerinin de kullandığı USART (Universal Synchronous Asynchronous Receiver Transmitter – Evrensel Senkron Asenkron Alıcı-Vericisi) modülüdür.
USART aşağıdaki modlarda ayarlanabilen bir seri iletişim donanımıdır:
- Asenkron (full-duplex): Cihazlar aynı anda iletebilir ve alabilir. Verici ve alıcı cihazların her ikisi de aynı saat darbesi hızına ayarlanmalıdır.
- Senkron-master (half-duplex): Saat darbesi hızının iletim modunda olan cihaz tarafından dâhili olarak üretildiği moddur. Bu durumda aynı anda yalnızca tek yönde iletim vardır. Bir cihaz verici konumundayken diğerleri alıcıdır. Karşılıklı olarak roller değişebilir.
- Senkron-slave (half-duplex): Bu modun “master” modundan farkı, cihazın uyku modunda çalışması istenen durumlarda tercih edilmesidir. Cihaz “slave” moduna alındığında, saat darbesi iletimi için kullanılan pine dışardan saat darbesi uygulanır.
- Simplex: Yalnızca tek yönlü iletimi ifade eder. Cihazlardan biri vericiyken diğerleri alıcı durumundadır. Roller değişmez.
Kişisel bilgisayar gibi donanımlarla, USART modülünü kullanarak iletişim kurmak için tam çift yönlü (full-duplex) asenkron modu, ya da A/D, D/A, entegre devreler, seri EEPROM’lar gibi çevresel aygıtlarla iletişim kurmak için yarım çift yönlü (half-duplex) senkron modu kullanılabilir.
Bünyesinde USART ya da E-USART (Enhanced USART – Güçlendirilmiş USART) bulunduran tüm PIC mikrodenetleyicilerinde genel olarak seri iletişimi düzenleyen TXSTA ve RCSTA kaydedicileri ve bu iş için tahsis edilmiş TX/RX pinleri yer alır. Bazı gelişmiş 8-bit PIC mikrodenetleyicilerinde 2 ya da daha fazla adet E USART modülü ve söz konusu kaydedicilerden de modül sayısınca bulunur. Şekil 9.1’de örnek olarak iki mikrodenetleyici arasında asenkron modda seri iletişimin ilkesel şeması gösterilmiştir. Dikkat edileceği üzere 8-bitlik veri paketinin başında ve sonunda başlangıç ve bitiş bitleri bulunmaktadır. Ayrıca en düşük değerlikli bit (LSB) ilk önce iletilmektedir. İletim zaman ekseninde gösterildiğinden, t=0 anında hat yüksek seviyededir. Lojik-0 ile (t=0 anından sonra) iletim başlar.
Şekil 9.1 USART Asenkron Modunun çalışma ilkesi
Gerçekte USART ve UART olmak üzere iki tür bulunmaktadır:
- UART – Universal Asynchronous Receiver/Transmitter
- USART – Universal Synchronous/Asynchronous Receiver/Transmitter
Senkron tür vericiler saat darbesi de üretir ve eşzamanlı olarak alıcıya iletirler. Dolayısıyla senkron iletimde mikrodenetleyicinin seri iletişim pinlerinden birisi veri hattı diğeri saat darbesi hattı olarak kullanılır. PIC mikrodenetleyicileriyle senkron seri iletişim modunda; RX pini veri hattı (DT-Data), TX pini saat darbesi (CK-Clock) hattı olarak kullanılır. Verinin hızını veriyi ileten aygıt belirler.
Diğer taraftan, asenkron tür vericiler saat darbesini dâhili olarak üretirler. Dolayısıyla gelen bir seri saat darbesi sinyali yoktur. Ancak bu durumda her iki cihazın da aynı hızda (baud rate) çalışması gerekir. Baud Rate saniyede iletilen bit hızını (bps – bit per second) ifade eden ve seri iletişimde yaygın olarak kullanılan bir kavramdır. Asenkron iletişimde her iki cihazın RX/TX pinleri çaprazlama olarak birbirine bağlanır. Bu durumda RX pini veri alma hattı, TX pini veri iletme hattı olarak kullanılır.
Tablo 9.1’de birbirine sıklıkla karıştırılan UART ve USART türlerinin farkları gösterilmiştir.
Tablo 9. 1 UART ve USART türlerinin karşılaştırılması
UART | USART |
Bağımsız iletişim protokolüdür. | RS-485, LIN, CAN Bus gibi çok sayıda protokolü destekler. |
Genellikle düşük hızlı uygulamalar için kullanılır. | Yüksek hızlı uygulamalar için daha uygundur. |
Saat darbesi dâhili olarak üretilir ve her iki cihaz da aynı hızda çalışacak şekilde ayarlanır. | Saat darbesi sinyali verici tarafından üretilir ve veri iletimi sırasında alıcıya gönderilir. |
Alıcının baud rate değeri bilinmelidir. | Alıcı aygıt vericinin baud rate değerine göre çalıştığından veri iletim hızının bilinmesi şart değildir. |
Eğer adresli iletişim yapılmıyorsa UART seri iletişim yolu yalnızca 2 cihazın haberleşmesine olanak verir. Bu durumda iki cihazın çapraz bağlantısı Şekil 9.2’de gösterildiği gibi olur.
Şekil 9.2 Verici ve alıcı aygıtların çapraz olarak birbirine bağlanması
UART seri iletişimde gönderilen ve alınan veri paketi özel bloklardan oluşur. Boşta (idle) durumdayken UART hatları lojik-1 (yüksek) seviyesine çekilir. Bu durum alıcının, iletişim kanalının diğer ucunda bir verici olduğunu algılamasını sağlar. UART paketleri genellikle “Start Bit” olarak adlandırılan bir açılış bitiyle başlar. Açılış biti lojik-0’dır.
USART modülü asenkron iletimde standart NRZ (non-return-to-zero) formatını (bir START bit, sekiz ya da dokuz veri biti, bir STOP biti) kullanır. USART veriyolunda iletim ve alma formatı Şekil 9.3’te gösterildiği gibidir.
Şekil 9.3 USART veriyolunda sinyal iletimi ve alımının ilkesel gösterimi
USART veriyolu boşta (idle) durumdayken hattın lojik-0’a çekilmesiyle adres/veri sözcüğünün iletimi ya da alımı başlar. Sözcük 8-bit ya da 9-bit uzunluğunda olabilmektedir. Sözcük bitlerinin ilgili kaydedicilere örneklenmesi ilgili bitin ortasında gerçekleşir.
USART haberleşme donanımı ile birlikte karşımıza çıkan bazı terimler bulunmaktadır. Bunları kısaca şu şekilde açıklayabiliriz:
Başlama Biti (Start Bit): UART veri iletim hattı (TX) boşta durumdayken lojik-1 seviyesinde tutulur. İletim yapılacağı zaman hat bir saat çevrimi süresince lojik-0’a çekilir. UART alıcı modülü hattaki bu değişimi algıladığında vericinin hızıyla eş hızda okuma yapmaya başlar.
Durma Biti (Stop Bit): Gönderilen veri paketinin iletiminin sonlandığını bildirmek için kullanılan bittir. Verici UART donanımı veri hattını en az bir bit süresince lojik-1 seviyesine çeker. İlgili kaydediciler ile kaç bitlik iletim yapılacağı belirlenir. Durma biti bu ayarlanan değere göre algılanır.
Eşlik Biti (Parity Bit - PB): Parite biti ya da eşlik biti alıcının aldığı bilginin doğruluğunu kontrol etmesinde kullanılan düşük seviyeli bir hata izleme mekanizmasıdır. Çift parite ve tek parite olmak üzere iki türü vardır. UART alıcısı veri paketini okuduktan sonra 1 bitlerinin sayısını sayar ve toplam değerinin çift ya da tek olduğuna bakar. Eğer parite biti 0 ise (çift parite), veri paketindeki 1 bitlerinin sayısı çift olmalıdır. Eğer parite biti 1 ise (tek parite), veri bloğundaki 1 bitlerinin sayısı tek sayıya karşılık gelmelidir. Eşlik biti uygun şekilde eşleşirse alıcı iletimin başarılı olduğunu değerlendirir. 9 bitlik iletim/alma işlemi yapıldığında 9. bit eşlik biti olarak kullanılabilir.
Baud Rate: Yukarıda da belirtildiği üzere gönderilen verinin ne kadar hızda gönderileceğini ifade eden bit cinsinden iletim hızıdır. Baud rate değeri olarak donanım sınırları içinde istenilen değer ayarlanabilir. Ancak yaygın olarak kullanılan ve endüstriyel standart hız değerleri diye nitelendirilen iletim hızları vardır. Özellikle ileride de göreceğimiz üzere çeşitli donanımlarla haberleşmeyi sağlayan uygulamalarda en yaygın tercih edilen hız değeri 9600’dür. Diğer standart iletim hızları şunlardır: 1200, 2400, 4800, 19200, 38400, 57600 ve 115200. Açıkça, baud rate 300’ün katlarıdır. 115200 değerinin üzerinde de iletişim mümkündür ancak bu durumda veri kaybı yaşanacaktır. PIC mikrodenetleyicilerinde baud rate hızını ayarlamayı sağlayan kaydediciler vardır ve bu kaydedicilerle elde edilebilecek hız değerleri sınırlıdır.
USART Modülü Kaydedicileri
Bu başlıkta genel olarak tüm USART ya da E-USART modülüne sahip PIC mikrodenetleyicilerinde ortak olan kaydedicilerden bahsedilecektir. Eğer mikroC platformunda hazır kütüphaneler kullanmadan, USART ile seri iletişim yapılması isteniyorsa aşağıdaki kaydedicilerin kullanılması gerekmektedir:
-
TXSTA (TRANSMIT STATUS AND CONTROL) Kaydedicisi: Seri iletişimin modunu ayarlayan ve iletişimi kontrol eden kaydedicidir.
-
RCSTA (RECEIVE STATUS AND CONTROL) KAYDEDİCİSİ: Alıcı tarafında seri bilginin alınmasıyla ilgili işlemleri kontrol eden kaydedicidir.
-
BAUDCTL (BAUD RATE CONTROL) KAYDEDİCİSİ: Veri hızıyla ilgili birtakım ayarlamaların yapıldığı kaydedicidir.
-
TXREG: İletilen bilginin tutulduğu kaydedicidir.
-
RCREG: Alınan bilginin kaydedildiği kaydedicidir.
-
SPBRG: Veri hızı üretecinin (Baud Rate Generator) hızının belirlenmesinde kullanılan kaydedicidir.
-
SPBRGH: Veri hızı üretecinin 16-bit modunda ayarlanması durumunda kullanılan kaydedicidir.
Bu kaydedicilerin nasıl çalıştığını anlamak için USART verici ve alıcı modüllerine bakmakta fayda vardır. Şekil 9.4’te PIC16F887 için E-USART verici modülünün blok şeması gösterilmiştir. E-USART verici modunda çalışırken, veri baytı veriyolu üzerinden 8-bitlik TXREG kaydedicisine yazılır. Ardından, TSR (Transmit Shift Register) kaydedicisine aktarılır. Bilgi paketinin 9-bitlik olması durumunda, TXSTA kaydedicisinin TX9D isimli biti TSR kaydedicisine MSb biti olarak eklenir. Veri asenkron modda, RC6/TX/CK fiziksel pini üzerinden “baud rate” üreteciyle elde edilen hız oranında dış ortama iletilir. Veri hızı programcı tarafından yazılım içinde SPBRG/H kaydedicisi ayarlanarak belirlenir.
Şekil 9.4 PIC16F887 için USART verici modülünün blok şeması
TSR kaydedicisi bir önceki yükleme işleminden Stop biti gelene kadar yüklenmez. Stop biti iletildiği gibi TSR kaydedicisi TXREG kaydedicisinden yeni bilgiyle (eğer mevcutsa) yüklenir. TXREG kaydedicisi TSR kaydedicisine veriyi yüklediğinde (bir TCYsüresi ya da FOSC/4 hızı) hafızası boşaltılır ve PIR1 kaydedicisinin TXIF kesme bağrayı kurulur. Ancak kesme sinyalinin oluşabilmesi için, PIE1 kaydedicisindeki TXIE bitinin de etkinleştirilmiş olması gerekir.
TXIF bayrağı TXIE etkinleştirme bitinin durumundan bağımsız olarak kurulur (TXIE bitinin etkinleştirilmesi, uyku modundan uyanma gibi kesme sinyaline ihtiyaç duyulan haller için önemlidir) ve yazılım içinde temizlenemez. Yalnızca TXREG kaydedicisine yeni bir değer yüklendiğinde resetlenir. TXIF bayrağı TXREG kaydedicisinin durumunu izlememizi sağlarken, TRMT biti TSR kaydedicisinin durumunu gösterir. TRMT biti salt okunurdur ve TSR kaydedicisi boşken kurulur. Bu bite kesme lojik devresi bağlı olmadığından, programcı TSR kaydedicisinin boş olup olmadığını belirlemek için TRMT bitini yoklamalıdır. Şekil 9.5’te Asenkron modda tek iletimin ve ard arda iletimin sinyalizasyonu bayrak sinyalleriyle birlikte ayrıntılı olarak gösterilmiştir.
Şekil 9.5 Asenkron iletimde sinyalizasyon düzeni
Şekil 9.6’da PIC16F887 için E-USART alıcı modülünün blok şeması gösterilmiştir. Asenkron mod seçildiğinde, RCSTA kaydedicisinin CREN bitinin etkinleştirilmesiyle veri RC7/RX/DT pini üzerinden alınır ve veri iyileştirme (data recovery) bloğuna gönderilir. Veri iyileştirme bloğuna gelen veri buradan RSR (Receive Shift Register) kaydedicisine gönderilir. Stop bitinin örneklenmesiyle birlikte RSR kaydedicisindeki veri, iki sözcüklü çifte tamponlu FIFO (First-In-First-Out) özellikli RCREG kaydedicisine iletilir (eğer boşsa). RSR kaydedicisi bit hızında (FOSC/4) çalışırken, veri iyileştirme bloğu gerçekte “baud rate” hızının 16 katı hızında çalışan yüksek hızlı bir kaydırmalı kaydedicidir. Gelen sözcüğün 8 ya da 9 bitinin tümü içeri alındığında hemen RCREG kaydedicisine gönderilir. FIFO arabelleklemesi (buffering), yazılım E USART alıcısından hizmet almaya başlamadan önce iki tam sözcüğün alınmasına ve üçüncü bir sözcüğün başlamasına izin verir. FIFO ve RSR kaydedicilerine doğrudan erişilemez. Alınan veriye erişim RCREG kaydedicisi yoluyla gerçekleşir.
Şekil 9.6 PIC16F887 için USART alıcı modülünün blok şeması
FIFO tamponundaki her sözcüğe karşılık gelen bir çerçeveleme hatası durum biti vardır. Çerçeveleme hatası bir Stop bitinin beklenen sürede alınmadığını ifade eder. Çerçeveleme hata durumuna RCSTA kaydedicisinin FERR biti üzerinden erişilir. FERR biti alma FIFO’sunda yer alan üstteki okunmamış sözcüğün durumunu gösterir. Bu nedenle, RCREG kaydedicisini okumadan önce FERR biti okunmalıdır.
FERR biti salt okunur bittir ve yalnızca alma FIFO’sunun okunmamış üst sözcüğüne başvuru yapar. Çerçeveleme hatası (FERR = 1) ilave sözcüklerin alımını önlemez. FERR bitini temizlemeye gerek yoktur. FIFO tamponundan bir sonraki sözcüğün okunması, FIFO’yu sonraki sözcüğe ve karşılık gelen çerçeveleme hatasına ilerletir. RCSTA kaydedicisinin E-USART modülünü resetlemesini sağlayan SPEN biti temizlenerek FERR biti sıfırlanabilir. RCSTA kaydedicisinin CREN bitinin temizlenmesi FERR bitini etkilemez. Bir çerçeveleme hatası kendi başına kesme üretmez.
RCREG kaydedicisine veri iletimi tamamlandığında PIR1 kaydedicisinin RCIF bayrağı kurulur. Eğer veri iletimi tamamlandığında kesme oluşması isteniyorsa PIE1 kaydedicisinin RCIE bitinin de kurulması gerekmektedir. RCIF bayrağı salt okunurdur ve yalnızca donanım tarafından temizlenebilir. RCREG kaydedicisi okunduğunda ve boşaldığında RCIF bayrağı temizlenir.
Aşağıda TXSTA, RCSTA ve BAUDCTL kaydedicilerinin her bir bitinin ne işe yaradığı açıklanmıştır.
TXSTA: (ADRES: 98h – PIC16F887, 98h – PIC16F628A, 19Eh – PIC12F1840)
bit 7 CRSC: Saat darbesi kaynağı seçme biti
Asenkron modu:
Dikkate alınmaz
Senkron modu:
1 = Master modu (saat darbesi kaynağı dâhili olarak “Baud Rate Generator - BRG” ile üretilir)
0 = Slave modu (saat darbesi kaynağı harici bir kaynaktan gelir)
bit 6 TX9: 9-bitlik veri iletimini etkinleştirme biti
1 = İletim etkin
0 = İletim pasif
bit 5 TXEN: Veri iletimi etkinleştirme biti
1 = Alınan ya da iletilen son baytın veri olduğunu işaret eder.
0 = Alınan ya da iletilen son baytın adres olduğunu işaret eder.
Not: Senkron modda SREN/CREN bitleri TXEN bitini geçersiz kılar.
bit 4 SYNC: EUSART modu tercih biti
1 = Senkron modu
Asenkron modu
bit 3 SENDB: Mola (Break) karakteri gönderme biti (LIN iletişiminde kullanılır)
Asenkron modu:
1 = Bir sonraki iletimde Senkron Molası (Sync Break) biti gönderir (tamamlandıktan sonra donanım tarafından temizlenir)
0 = Senkron molası iletimi tamamlandı
Senkron modu:
Dikkate alınmaz
bit 2 BRGH: Yüksek “Baud Rate” seçme biti
Asenkron modu:
1 = Yüksek hız
0 = Düşük hız
Senkron modu:
Bu modda kullanılmaz
bit 1 TRMT: İletim kaydırmalı kaydedicisi durum (Transmit Shift Register Status) biti
1 = TSR boş (TSR, TXREG kaydedicisinden paralel olarak gelen bilginin çıkış pinine gönderilmek üzere tutulduğu seri iletim kaydedicisidir)
0 = TSR dolu
bit 0 TX9D: İletim verisinin 9.biti
Bu bit bir adres/veri biti ya da parite biti olabilir (9 bitlik iletimde kullanılır)
RCSTA: (ADRES: 18h – PIC16F887, 18h – PIC16F628A, 19Dh – PIC12F1840)
bit 7 SPEN: Seri port etkinleştirme biti
1 = Seri port etkin (RX/DT ve TX/CK pinlerini seri port pinleri olarak konfigüre eder)
0 = Seri port kapalı
bit 6 RX9: 9-bit alma etkinleştirme biti
1 = 9-bitlik almayı seçer.
0 = 8-bitlik almayı seçer.
bit 5 SREN: Tek seferli seri almayı etkinleştirme biti
Asenkron modu:
Dikkate alınmaz
Senkron modu - Master:
1 = Tek almayı etkinleştirir.
0 = Tek almayı kapatır.
Bu bit alma işlemi tamamlandıktan sonra temizlenir.
Senkron modu - Slave:
Dikkate alınmaz
bit 4 CREN: Sürekli alma etkinleştirme biti
Asenkron modu:
1 = Alıcıyı etkinleştirir.
0 = Alıcıyı kapatır.
Senkron modu:
1 = Etkinleştirme biti olan CREN biti temizlenene kadar sürekli almayı etkinleştirir (CREN SREN’inin üzerine yazar).
0 = Sürekli almayı kapatır.
bit 3 ADDEN: Adres algılama etkinleştirme biti
Asenkron modu 9-bit (RX9 = 1):
1 = Adres algılamayı etkinleştirir, kesmeyi etkinleştirir ve RSR<8> kurulduğunda alma tamponunu yükler.
0 = Adres algılamayı kapatır, tüm baytlar alınır ve 9’uncu bit parite biti olarak kullanılabilir.
Asenkron modu 8-bit (RX9 = 0) :
Dikkate alınmaz
bit 2 FERR: Çerçeveleme hatası biti
1 = Çerçeveleme hatası var (RCREG kaydedicisinin okunmasıyla güncellenebilir ve bir sonraki geçerli baytı alır).
0 = Çerçeveleme hatası yok
bit 1 OERR: Aşma hatası biti
1 = Aşma hatası var (CREN bitinin temizlenmesi suretiyle temizlenebilir)
0 = Aşma hatası yok
Not: FIFO özellikli tampon iki sözcük tutabilir. FIFO’ya erişimden önce 3’üncü bir söcüğün gelmesi durumunda aşma hatası oluşur. Hata temizlenene kadar yeni sözcük alımı gerçekleşmez.
bit 0 RX9D: Alınan verinin dokuzuncu biti
Bu bir adres/veri biti ya da bir parite biti olabilir ve kullanıcı yazılımı tarafından hesaplanmalıdır.
BAUDCTL: (ADRES: 187h – PIC16F887, BAUDCON: 19Fh – PIC12F1840)
bit 7 ABDOVF: Otomatik baud algılaması taşma biti
Asenkron modu:
1 = Otomatik baud zamanlayıcısı taşması gerçekleşti
0 = Otomatik baud zamanlayıcısı taşmadı
Senkron modu:
Dikkate alınmaz
bit 6 RCIDL: Alıcı boşta bayrak biti
Asenkron modu:
1 = Alıcı boşta
0 = Start biti alındı ve alıcı alıyor
Senkron modu:
Dikkate alınmaz
bit 5 Kullanılmıyor: ‘0’ olarak okunur.
bit 4 SCKP: Senkron saat darbesi polarite seçme biti
Asenkron modu:
1 = Terslenmiş veriyi RC7/TX/CK pinine ilet
0 = Terslenmemiş veriyi RC7/TX/CK pinine ilet
Senkron modu:
1 = Veri saat darbesinin yükselen kenarında ulaştırılır.
0 = Veri saat darbesinin düşen kenarında ulaştırılır.
bit 3 BRG16: 16-bitlik veri hızı üreteci (Baud Rate Generator)
1 = 16-bitlik üreteç kullanılır.
0 = 16-bitlik üreteç kullanılmaz
bit 2 Kullanılmıyor: ‘0’ olarak okunur.
bit 1 WUE: Uyandırma etkinleştirme biti
Asenkron modu:
1 = Alıcı bir düşen kenar için bekler. Sözcük alımı gerçekleştiğinde ve RCIF bayrağı kurulduğunda WUE biti otomatik olarak temizlenir.
0 = Alıcı normal olarak çalışır.
bit 0 ABDEN: Otomatik baud algılama etkinleştirme biti
Asenkron modu:
1 = Otomatik baud algılama modu etkin (otomatik baud tamamlandığında temizlenir)
0 = Otomatik baud algılama modu pasif
Senkron modu:
Dikkate alınmaz
USART Modülüyle İki Mikrodenetleyici Arası İletişim
Kütüphane kullanmadan mikrodenetleyicilerle USART modülü üzerinden haberleşme yapabilmek için öncelikle BRG katının uygun veri hızı için ayarlanması gerekir. Veri hızı üreteci katı, hem asenkron hem de senkron mod için 8-bitlik ve 16-bitlik zamanlayıcılara sahiptir (üst seviye mikrodenetleyiciler için). Varsayılan olarak 8-bitlik zamanlayıcıyla hız ayarı yapılır. Eğer BAUDCTL kaydedicisinin BRG16 biti kurulursa 16-bitlik zamanlayıcı kullanılarak daha hassas hız ayarlaması yapılabilir. Düşünülen hız için gerekli değer atamaları yapıldıktan sonra ilgili kaydedicilerin bitleri uygun iletişim modu için ayarlanır.
Asenkron bir iletim yapacağımızı varsayarsak, E-USART verici modülünün UART ayarlaması aşağıdaki adımlarda açıklandığı gibi olacaktır.
PIC16F887 İÇİN UART VERİCİSİ AYARLARI
SPBRGH ve SPBRG kaydedici çiftine değer ataması yapılır. TXSTA kaydedicisinin BRGH biti ve BAUDCTL kaydedicisinin BRG16 biti uygun şekilde ayarlanır. Yapılandırma sözcüklerinin ve bitlerinin değerine göre “Baud Rate” değeri hesaplanır. Eğer “Baud Rate” değerini biliyorsak yapılandırma sözcüklerine yüklememiz gereken değeri hesaplayabiliriz. Bunun için Denklem 9.1’den yararlanılır. Ayrıca Tablo 9.2’ye bakılarak yapılandırma bitlerinin aldıkları değere göre değişen denklem katsayısı dikkate alınır.
|
Denklem 9.1 |
Burada,
FOSC: Mikrodenetleyicinin osilatör frekansıdır.
X: Yapılandırma bitleri ve 8-bit/16-bit iletim moduna göre değişen katsayı. 64-16-4 değerlerinden birini alır.
N: SPBRGH:SPBRG kaydedici ikilisinden oluşan 16 bitlik desimal sayı değeridir.
Kaydedici | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
SPBRG | BRG7 | BRG6 | BRG5 | BRG4 | BRG3 | BRG2 | BRG1 | BRG0 |
SPBRGH | BRG15 | BRG14 | BRG13 | BRG12 | BRG11 | BRG10 | BRG9 | BRG8 |
Yüksek hız baud rate değeri düşük osilatör frekanslarında bile daha düşük hata oranlarında çalışılmasını sağlayacaktır. Bunun için TXSTA kaydedicisinin BRGH biti lojik-1 yapılır. Çeşitli baud rate değerleri için 8-bit/16-bit Asenkron iletim modunda yapılandırma değerlerine göre elde edilen hata oranları için Tablo 9.3 – 9.6’da yer alan verileri inceleyebilirsiniz.
Örnek olarak, mikrodenetleyicimizin osilatör frekansının dâhili 8 MHz olduğunu, vericimizin 9600 “Baud Rate” değerinde iletim yapacağını, 8-bit veri hızı üreteci kullanacağımızı ve yüksek hızda iletim yapacağımızı düşünelim:
Bu durumda SPBRG kaydedicisine yüklememiz gereken değeri Denklem 9.1’de verilen değerleri yerine koyarak hesaplayabiliriz.
FOSC = 20 MHz, X = 16 ve “Baud Rate” değeri 9600 denklemde yerine yazılırsa,
SPBRG kaydedicisine yüklenmesi gereken en yakın tamsayı değeri 129 olarak hesaplanır. Bu durumda mikroC proje dosyasında yazacağınız kod aşağıdaki gibi olur;
TXSTA.BRGH = 1;
SPBRG = 51;
Asenkron seri portun ayarlanması için TXSTA kaydedicisinin SYNC biti ve RCSTA kaydedicisinin SPEN biti ayarlanır.
TXSTA.SYNC = 0;
RCSTA.SPEN = 1;
-
TX ve RX hatlarına denk gelen port pinleri TRIS komutuyla giriş moduna kurulmalıdır. PIC16F887 için TRISC<7:6> pinleri kurulur.
TRISC = 0XC0;
-
Eğer 9-bitlik iletim isteniyorsa TX9 kontrol biti kurulur. Alıcının adres algılama moduna ayarlanması durumunda, 9’uncu bitin kurulması diğer 8 bitin adres verisi olacağı anlamına gelir.
-
Seri iletişimin başlaması için TXSTA kaydedicisinin TXEN biti etkinleştirilir. Böylece TXIF kesme bitinin etkinleşecek olmasının yolu açılır.
TXSTA.TXEN = 1;
-
Eğer uygulamanızda seri iletişim kesmesini de kullanacaksanız, PIE1 kaydedicisinin TXIE biti kurulmalıdır. Ayrıca INTCON kaydedicisinin evrensel kesme biti GIE ve çevresel donanım kesmesi PEIE biti de kurulmalıdır. Ancak kesmeleri kurduğunuzda kesme alt yordamı içinde bunu kontrol etmeniz gerekir. Aksi durumda “stack overflow” hatası alınır. Bu durum kesme öncesi yığın belleğine gönderilen program adreslerinin aşırı yığılmasından oluşur. İleride gösterilecek uygulama devresinde verici kısmı için kesme alt yordamı kullanılmayacağından kesmeler kapalı tutulacaktır.
INTCON.GIE = 1;
INTCON.PEIE = 1;
PIE1.TXIE = 1;
-
Eğer 9-bitlik iletim seçildiyse, 9’uncu bit TX9D veri bitine yüklenmelidir.
-
Son aşamada iletilecek 8-bitlik veri TXREG kaydedicisine yüklenir. Bu durum iletimi başlatacaktır.
char veri = 255;
while(!TRMT){} //TSR kaydedicisi dolu oldugu surece (lojik-0) döner ve
TXREG = veri; //yeni bilgi yazilmaz
Bir PIC mikrodenetleyicisinin alıcı olarak çalışması için yukarıda açıklanan işlem basamaklarına benzer şekilde birtakım ayarlamalar yapmak gerekecektir.
PIC16F887 İÇİN UART ALICISI AYARLARI
SPBRG kaydedicisi uygun “baud rate” için ayarlanır. Eğer yüksek hızlı “baud rate” isteniyorsa BRGH biti kurulur. Bilindiği gibi asenkron iletimde birbiriyle haberleşen tüm donanımlar bağımsız olarak aynı baud rate değerine ayarlanmalıydı. İşlem basamakları sonunda iki eş PIC mikrodenetleyicisinin birbiriyle haberleşmesi uygulaması yapılacaktır. Bu nedenle verici için yapılan yapılandırma değerleri alıcı için de uygulanmalıdır.
TXSTA.BRGH = 1;
SPBRG = 51;
Asenkron seri portun ayarlanması için TXSTA kaydedicisinin SYNC biti ve RCSTA kaydedicisinin SPEN biti ayarlanır.
TXSTA.SYNC = 0;
RCSTA.SPEN = 1;
TX ve RX hatlarına denk gelen port pinleri TRIS komutuyla giriş moduna kurulmalıdır. PIC16F887 için TRISC<7:6> pinleri kurulur.
TRISC = 0XC0;
Eğer uygulamanızda seri iletişim kesmesini de kullanacaksanız, PIE1 kaydedicisinin RCIE biti kurulmalıdır. Ayrıca INTCON kaydedicisinin evrensel kesme biti GIE ve çevresel donanım kesmesi PEIE biti de kurulmalıdır.
INTCON.GIE = 1;
INTCON.PEIE = 1;
PIE1.RCIE = 1;
Eğer 9 bitlik alma isteniyorsa RX9 biti kurulur.
UART veri alımının etkinleştirilmesi için CREN biti kurulur.
RCSTA.CREN = 1;
Alma işlemi tamamlandığında RCIF bayrağı kurulur ve eğer RCIE biti kurulmuşsa bir kesme sinyali üretilir.
Hata bayraklarını ve eğer etkinleştirilmişse 9’uncu biti almak için RCSTA kaydedicisi okunur.
RCREG kaydedicisi okunarak gelen 8-bitlik veri elde edilir. Verinin kaydediciden okunması için iki yol vardır.
char veri;
while(!RCIF);//RCREG kaydedicisine veri iletimi tamamlanana kadar döner
veri = RCREG;
İkinci yöntem kesme servis yordamı (ISR - Interrupt Service Routine) kullanılarak yapılır.
char veri;
void interrupt(void)//Kesme yordamı
{
if (RCIF == 1)//Veri iletimi tamamlandıysa
{
veri = RCREG; // Veri tamponu okunur
PORTD = veri; // Okunan verinin içeriği PORTD’ye gönderilir
}
}
Herhangi bir hata (ör: overrun hatası) olduğunda, CREN etkinleştirme biti temizlenerek hata uyarısı sıfırlanır.
Tablo 9.2 Baud Rate üreteci formülleri
Tablo 9.3 Asenkron Mod için standart Baud Rate değerlerine göre kaydedici değerleri - I
Tablo 9.4 Asenkron Mod için standart Baud Rate değerlerine göre kaydedici değerleri- II
Tablo 9.5 Asenkron Mod için standart Baud Rate değerlerine göre kaydedici değerleri - III
Tablo 9.6 Asenkron Mod için standart Baud Rate değerlerine göre kaydedici değerleri - IV
Tüm bu anlatılanlar birleştirildiğinde örnek olarak iki mikrodenetleyici arasında asenkron seri iletişim gerçekleştirebiliriz. Bu kapsamda mikrodenetleyicilerden biri aracılığıyla 0-255 arasında değişen 1 baytlık sayı değeri diğer mikrodenetleyiciye gönderilecek ve ikinci mikrodenetleyiciye bağlı LED grubu üzerinden bu verinin değişimi izlenecektir. Birinci mikrodenetleyiciye bağlı butonlar yardımıyla verinin 0-255 arasında değiştirilmesi istenmektedir. Böyle bir uygulamanın devre şeması Şekil 9.7’de gösterilmiştir.
Şekil 9.7 İki PIC arasında UART iletişim uygulama devresi
“ARTTIR” ve “AZALT” isimli butonlar ile char veri türünde 1 baytlık “veri” isimli değişkenin desimal değeri değiştirilmektedir. Sonuç ikilik olarak 1’inci MCU’ya bağlı LED grubu üzerinden izlenir. “GONDER” isimli butona basıldığında 1’inci MCU’da izlenen verinin aynısı 2’inci MCU’ya da gönderilir. Tablo 9.7’de UART vericisinin, Tablo 9.8’de ise UART alıcısının kaynak kodları verilmiştir.
Tablo 9.7 İki PIC arasında UART iletişim uygulaması VERİCİ devresi için mikroC kaynak kodu
#define ARTTIR PORTB.RB0
#define AZALT PORTB.RB1
#define GONDER PORTB.RB2
char veri = 0;
//Fonksiyon deklerasyonlari
void UART_TX_Init(void);
void UART_Yaz(char);
void port_Init(void);
//Ana fonksiyon
void main(void) {
port_Init();
UART_TX_Init();
while(1) {
if(!ARTTIR){
veri++;
Delay_ms(250);
}
if(!AZALT){
veri--;
Delay_ms(250);
}
if(!GONDER){
UART_Yaz(veri);
Delay_ms(250);
}
PORTD = veri;
}
}
//UART modülü 9600 verici ayari yapiliyor
void UART_TX_Init(void){
BAUDCTL = 0;
TXSTA.BRGH = 1;
SPBRG = 51;
SPBRGH = 0;
TXSTA.SYNC = 0;
RCSTA.SPEN = 1;
TXSTA.TXEN = 1;
}
//Veri TX hattina yazdiriliyor
void UART_yaz(char veri){
while(!TRMT){} //TSR kaydedicisi dolu oldugu surece (lojik-0) doner ve
TXREG = veri; //yeni bilgi yazilmaz.
}
//Portlar ve kesmeler ayarlaniyor
void port_Init(void){
OSCCON = 0X70;
// INTCON.GIE = 1;
// INTCON.PEIE = 1;
// PIE1.TXIE = 1;
ANSEL = 0; //tüm portlar dijital I/O
ANSELH = 0;
CM1CON0 = 0; //karsilastiricilar kapali
CM2CON0 = 0;
TRISA = 0;
PORTA = 0;
TRISB = 0X07; //ilk üç pin giris
PORTB = 0;
TRISC = 0xC0;
PORTC = 0;
TRISD = 0;
PORTD = 0;
TRISE = 0;
PORTE = 0;
OPTION_REG = 0;
WPUB = 0X07; //ilk üç pin için weak pull-up lar aktif
}
İki PIC arasında UART iletişim uygulaması ALICI devresi için mikroC kaynak kodu
char veri = 0;
//Fonksiyon deklerasyonlari
void UART_RX_Init(void);
void port_Init(void);
//Ana fonksiyon
void main(void) {
port_Init();
UART_RX_Init();
while(1) {
//islemler kesme yordami içinde yapiliyor
}
}
//UART modülü 9600 alici ayari yapiliyor
void UART_RX_Init(void){
BAUDCTL = 0;
TXSTA.BRGH = 1;
SPBRG = 51;
TXSTA.SYNC = 0;
RCSTA.SPEN = 1;
RCSTA.CREN = 1;
}
//Portlar ve kesmeler ayarlaniyor
void port_Init(void){
OSCCON = 0X70;
INTCON.GIE = 1;
INTCON.PEIE = 1;
PIE1.RCIE = 1;
ANSEL = 0; //tüm portlar dijital I/O
ANSELH = 0;
CM1CON0 = 0; //karsilastiricilar kapali
CM2CON0 = 0;
TRISA = 0;
PORTA = 0;
TRISB = 0;
PORTB = 0;
TRISC = 0xC0;
PORTC = 0;
TRISD = 0;
PORTD = 0;
TRISE = 0;
PORTE = 0;
}
void interrupt(void){
if(PIR1.RCIF){
veri = RCREG;
PORTD = veri;
}
}
Her ne kadar TXEN ve CREN bitlerinin kurulmasıyla PIC mikrodenetleyicisinin TX/RX pinleri seri iletişim için tahsis edilmiş olsa da, TRISC<7:6> bitleri giriş modunda ayarlanmalıdır. Aksi taktirde iki mikrodenetleyici arasında lojik çakışması sorunu oluşacaktır.
Tüm haberleşme sistemlerinde en büyük sorun mesafedir. Asenkron iletişimde “baud rate” değeri hesaplanırken, kullanılan osilatör frekansı ve diğer yapılandırma ayarlarına bağlı olarak kaydedicilere yüklenecek sayıda yuvarlama yapmak gerekebilir. Bu durum “baud rate” sinyalinin hesaplanan değerinde bir miktar hata (error) oluşmasına neden olur. Düşük yoğunluklu bilgi iletiminde bu hata sorun yaratmaz. Hata oranının hesaplanması Denklem 9.2’de gösterildiği gibidir:
|
Denklem 9.2 |
Örnek olarak MCU frekansının 8 MHz, İstenen baud rate değerinin yüksek hız (BRGH=1) 2400 ve 16 bit BRG ile işlem yapılacağı varsayılsın:
Denklem 9-1’de değerler yerine konursa takip eden eşitlik elde edilir.
9600=8.000.000/(4(SPBRGH:SPBRG+1))
Hesaplanan Baud Rate=8.000.000/(4(832+1)) = 2401
SPBRGH:SPBRG = 832,333 832 olarak elde edilir. Bu değer Denklem 9.1’de yerine konup gerçek “baud rate” değeri hesaplanır. Bu durumda:
Hesaplanan değer Denklem 9.2’de yerine konursa,
Hata=(2401-2400)/2400 x100 = %0,04
hata değeri elde edilir.
UART Dönüştürücülerle İletişim
PIC mikrodenetleyicilerinin USART ya da E-USART modülleri kullanılarak çeşitli haberleşme donanımlarıyla denetleyicinizi haberleştirmeniz mümkündür. Farklı haberleşme cihazlarının UART dönüşümünü gerçekleştiren modülleri vardır. USB-UART, GSM-UART, Wifi-UART, Bluetooth-UART dönüştürme cihazlarıyla mikrodenetleyicinizi bir haberleşme üssüne çevirmeniz mümkündür.
18F serisi ve dâhili USB modülüne sahip mikrodenetleyicilerden farklı olarak, PIC16F628A gibi oldukça sıradan bir mikrodenetleyicinizi bile bu dönüştürme donanımları sayesinde bilgisayara, GSM kitine, Wifi ağına, vb. donanımlara bağlayabilirsiniz.
Şekil 9.8 Çeşitli arabirim ve UART dönüştürücüleri
MikroC programı USART donanımı olmayan MCU’lar için yazılımsal (Software) UART (Universal Asynchronous Receive And Transmit – Full Duplex) ve donanımsal USART modülü olanlar için de UART kütüphanesine sahiptir. Daha önce de söylendiği gibi 8-bit mikrodenetleyicilerde genel olarak 1 adet USART modülü mevcutken, PIC18FXXJ94 gibi mikrodenetleyicilerde bu sayı 4 adet Enhanced (güçlendirilmiş) USART modülüne kadar çıkmaktadır.
Asenkron mod genel olarak RS-232 sistemlerle haberleşirken kullanılır. Çok sayıda alıcının RS-485 sistemlerinde olduğu gibi aynı iletim hattını kullandığı durumlar için özel bir adres algılama modu da mevcuttur.
MikroC UART kütüphanesi komutları ve açıklamaları aşağıdaki gibidir:
UARTx_Init : UART modülünü konfigüre eder ve çalışmaya hazır hale getirir. Dâhili modül şu şekilde konfigüre edilir:
-
Alıcı etkin
-
Verici etkin
-
Bilgi paketleri (frame) 8 bit
-
1 STOP bit
-
Parite modu devre dışı
-
Asenkron (full dublex – karşılıklı alma ve gönderme) etkin
Kullanımı : UART1_Init(9600);
Açıklama : 1 numaralı UART modülü 9600 baud oranında haberleşmeye hazır hale getirilir.
UARTx_Data_Ready : Alma tamponundaki bilginin almaya hazır olup olmadığını kontrol için kullanılır. Lojik-1 ise veri okumaya hazırdır. Lojik-0 ise veri kaydedicisinde alınacak veri yok demektir.
Kullanımı : |
if (UART1_Data_Ready() == 1) {
receive = UART1_Read();
}
|
Açıklama : UART1 modülü veri alma tamponunda bilgi varsa bilgi okunur ve “receive” isimli değişkene kaydedilir.
UARTx_Tx_Idle : Veri gönderme kaydırmalı kaydedicisinin (shift register) boş olup olmaması durumu kontrol edilir. Veri gönderildiyse lojik-1, aksi durumda lojik-0 olur.
Kullanımı : |
if (UART1_Tx_Idle() == 1) {
UART1_Write(data);
}
|
Açıklama : Eğer önceki veri seri olarak iletildiyse “data” isimli sonraki veriyi gönderir.
UARTx_Read : UART alma tamponundaki bilgiyi alır.
Kullanımı : UARTx_Data_Ready rutini uygulamasına bakınız.
Açıklama : UARTx_Data_Ready rutini uygulamasına bakınız.
UARTx_Read_Text : UART aracılığıyla alınan karakterleri okur. Alma işlemi rutin içinde belirtilen karaktere ya da karakter dizisine ulaşana kadar devam eder. Okunan dizi rutin içinde belirtilen değişkene kaydedilir.
Kullanımı : |
UART1_Init(4800); //hazırla
UART1 module
Delay_ms(100);
while (1) {
if (UART1_Data_Ready() == 1) { // veri alındıysa
UART1_Read_Text(output, "BUL", 20);
/* ‘BUL’ bulunana kadar metin okunur ve
output karakter dizi değişkenine kaydedilir. */
UART1_Write_Text(output);
/* İçinde ‘BUL’ dizisi bulunan metin geri gönderilir */
}
}
|
Açıklama : Hatta veri varsa “BUL” bilgisi bulunana kadar 20 karakterlik metin dizisi okunur. Ne kadar uzunlukta bir dizinin okunacağı maksimum 255 olarak girilecek sayı değerince belirtilir. Eğer 255 değeri girilirse aranan veri dizisi bulunana kadar döngü yordamı sürekli olarak işletilecektir. Burada “output” isimli değişken 255 karakterlik bir char değişkendir.
UARTx_Write : UART modülü yardımıyla 1 byte bilgi gönderilir.
Kullanımı : |
unsigned char data = 0x1E;
...
UART1_Write(data);
|
Açıklama : 0x1E hekzadesimal verisi gönderilir.
UARTx_Write_Text : UART modülü yardımıyla 1 byte bilgi gönderilir.
Kullanımı : |
UARTx_Read_Text rutini uygulamasına bakınız. |
Açıklama : İçinde aranan karakter dizisi bulunan bilgi geri gönderilmektedir.
UART_Set_Active : Birden fazla UART modülü olan mikrodenetleyicilerde o an için aktif kılınacak UART modülünü ayarlamak için kullanılır.
Kullanımı : |
Örnek olarak PIC18F65J90 mikrodenetleyicisinde 2 adet E-USART modülü vardır. Program içinde bunlardan hangisinin kullanılacağı aşağıdaki uygulamada olduğu gibi belirlenir.
UART1_Init(9600); // UART1 modülü hazırlanır
UART2_Init(9600); // UART2 modülü hazırlanır
RS485Master_Init(); // MCU Master olarak ayarlanır
UART_Set_Active(&UART1_Read, &UART1_Write,
&UART1_Data_Ready, &UART1_Tx_Idle); // UART1 aktif
RS485Master_Send(data,1,160); // mesajı UART1 ile gönder
UART_Set_Active(&UART2_Read, &UART2_Write,
&UART2_Data_Ready, &UART2_Tx_Idle); // UART2 aktif
RS485Master_Send(data,1,160); // mesajı UART2 ile gönder
|
Açıklama : Örnekte RS485 seri iletişimi protokolüyle veri gönderme uygulaması gösterilmiştir. RS485 iletişiminin yapılabilmesi için ilgili UART modülünün aktif yapılması gerekir. RS485 iletişiminde bir veri hattında yer alan birden fazla cihazdan aynı anda yalnızca bir tanesi “master” olarak veri gönderebilir. Bu durumda diğerleri dinleme ve alma modundadır.
Uygulama örneğinde data isimli verinin 1 karakteri 160 adres numaralı alıcıya UART1 modülü üzerinden gönderilmektedir. Ardından aynı bilgi UART2 üzerinden gönderilir.
Şekil 9.10’da PIC16F628A kullanılarak gerçekleştirilmiş bir 4 kanallı zaman ayarlı kontrol devresi uygulaması vardır. Mikrodenetleyicinin USART Rx/Tx pinlerine Bluetooth alıcı/vericisinin Rx/Tx pinleri çaprazlama olarak bağlanmıştır. Böylece telefon ve tabletler üzerinden kontrol edilebilir bir cihaz tasarımı yapılmıştır.
Ancak şekilde Proteus’un COMPIM aracı kullanılarak simülasyonun çalışıp çalışmadığı test edilmiştir. “Virtual Serial Port Driver” denilen bir yazılım yardımıyla bilgisayarın iki COMPORT’u birbiriyle çaprazlama bağlanır ve alıcı/verici çifti oluşturulur. Örnek uygulama için bilgisayarın COM1 ve COM2 portları 9600 “baud rate” hızında birbirlerine çaprazlanmıştır. COMPIM aracı yardımıyla eşleştirmesi yapılan bu portlardan birinden sanal olarak veri okunur. MicroC’nin IDE’sinde Tools menüsü altında yer alan USART Terminal uygulaması çalıştırılarak COMPIM aracının bağlantı kurduğu çapraz COM porttan desimal olarak ya da ASCII olarak veri gönderilir. Bu uygulamada COMPIM aracı COM2 portuna ayarlanmıştır. Bu nedenle MikroC USART Terminal programında COM1 üzerinden bağlantı yapılır. Böylece COM1 üzerinden COM2’ye veri gönderilir (Bkz. Şekil 9.9).
Şekil 9.9 Proteus programının COMPIM aracına MikroC USART Terminalinden bilgi gönderilmesi
Şekil 9.10 PIC16F628A ile USART iletişimi ve COMPIM simülasyonu
Tablo 9.9 PIC16F628A ile USART iletişimiyle 4 kanal kontrol uygulaması mikroC kodu
#define buton1 PORTB.RB4 //sabit tanımlamaları yapılıyor
#define buton2 PORTB.RB5
#define buton3 PORTB.RB6
#define buton4 PORTB.RB7
#define lamba1 PORTA.RA0
#define lamba2 PORTA.RA1
#define lamba3 PORTA.RA2
#define lamba4 PORTA.RA3
unsigned int sayac, i; /* evrensel değişkenler
rutinler dışında tanımlanıyor */
unsigned short saniye1, saniye2, saniye3, saniye4;
char uart_rd='0';
//Dört kanal için dört ayrı saniye tutucu ayarlanıyor
void ayar(){
TRISA = 0x00; // PORT A portu çıkış olarak ayarlanıyor
TRISB = 0xF1; /* PORT B portunun RB1-RB3 portları çıkış,
RB0 ve RB4-RB7 giriş yapılıyor */
PORTA = 0x00; // PORT A'nın tüm çıkışları 0 yapılıyor
PORTB = 0x00;
OPTION_REG = 0x07; /* Ön ölçekleyici (prescaler) 256 olarak ayarlanıyor */
CMCON = 0X07; // Analog çıkışlar kapatılıyor
INTCON.GIE = 1; // Evrensel kesme aktif yapılıyor
INTCON.INTE = 1; // Harici kesme
INTCON.RBIE = 1; // B4-B7 portları kesmesi etkinleştiriliyor
INTCON.T0IE = 1; // Timer0 kesmesi aktifleştirildi
TMR0 = 128; // Timer0 değeri 128 olarak ayarlanıyor
sayac = 0;
saniye1, saniye2, saniye3, saniye4 = 0;
UART1_Init(9600); // 9600 bps'da UART iletişimi başlatılıyor
delay_ms(100);
}
void main()
{
ayar();
while(1){ //sonsuz döngü
while (UART1_Data_Ready()){ /* Okunacak veri var mı yok mu
diye kontrol ediliyor */
delay_ms(10); /* Verinin sıralı seri olarak okunması
sırasında bekleme yapılıyor */
uart_rd= UART1_Read();
if(uart_rd=='1')lamba1=1;
else if(uart_rd=='2')saniye1 = 11;
else if(uart_rd=='3')lamba2 = 1;
else if(uart_rd=='4')saniye2 = 16;
else if(uart_rd=='5')lamba3 = 1;
else if(uart_rd=='6')saniye3 = 21;
else if(uart_rd=='7')lamba4 = 1;
else saniye4 = 26;
}
if(saniye1>=10){ /* 10 veya 20 saniyeye ulaşıldı mı?
Ulaşıldıysa resetleniyor */
lamba1=0;
saniye1=0;
uart_rd='2';
}
if(saniye2>=15){
lamba2=0;
saniye2=0;
uart_rd='4';
}
if(saniye3>=20){
lamba3=0;
saniye3=0;
uart_rd='6';
}
if(saniye4>=25){
lamba4=0;
saniye4=0;
uart_rd='8';
}
}}
void interrupt() /* TMR0 veya RB4-7 kesmesi sonucu
gerçekleşecek dallanma rutini */
{
if(INTCON.RBIF){ //RB4-7 kesmesi mi?
if(!buton1){ //butona basıldığı anda
lamba1=1;
uart_rd='1';
}
if(!buton2){
lamba2=1;
uart_rd='3';
}
if(!buton3){
lamba3=1;
uart_rd='5';
}
if(!buton4){
lamba4=1;
uart_rd='7';
}
INTCON.RBIF=0; //B4-B7 kesme bayrağı temizleniyor
}
if(INTCON.INTF){
PORTA = 0x0F; //A portunun ilk dört pini aktif yapılıyor
INTCON.INTF=0; // Harici kesme bayrağı temizleniyor
uart_rd='9';
}
if(INTCON.T0IF){ //Timer0 kesmesi mi?
sayac++;
if(sayac >= 30){ //prescaler sayacı
if(lamba1){ //kanal1'in saniyesi işletiliyor
saniye1++;
}
if(lamba2){ //kanal2'nin saniyesi işletiliyor
saniye2++;
}
if(lamba3){ //kanal3'ün saniyesi işletiliyor
saniye3++;
}
if(lamba4){ //kanal4'ün saniyesi işletiliyor
saniye4++;
}
UART1_Write(uart_rd);
sayac=0;
}
INTCON.T0IF=0; //TMR0 bayrağı siliniyor ki tekrar kesme gerçekleşebilsin
TMR0=128; //TMR0 sayacı değeri tekrar yükleniyor 0 255 arası
}}
UART haberleşmesinin diğer pek çok seri iletişim protokolüne göre birtakım üstünlükleri bulunur. Yalnızca iki hat ile karşılıklı tam çift yönlü iletişim yapmak mümkündür. Özellikle asenkron mod iletişimde ayrıca bir sinyalizasyon hattı kullanmaya gerek kalmaz. Sahip olduğu parite biti özelliği ile düşük seviyede de olsa donanım düzeyinde hata denetimi sunar. Çok yaygın bir protokol olması sebebiyle günümüz modern haberleşme protokolleriyle de uyumlu çalışacak şekilde geniş bir dönüştürücü ağına sahiptir.
Yukarıda bahsedilen üstünlüklerinin yanında dikkat edilmesi gereken bazı kusurları da vardır. Asenkron haberleşme, seri senkron ve paralel haberleşmeye göre yavaş kalır. İletişim kuran tüm aygıtların “baud rate” değeri sınırlı küçük bir hata marjini içinde aynı olmalıdır. Veri çerçevesi boyutu en fazla 9-bit ile sınırlıdır. UART haberleşmesinin en önemli eksikliği de aynı anda yalnızca iki aygıtın iletişim hattını kullanabilmesidir. Çoklu “slave” ve çoklu “master” desteği yoktur. Çok sayıda aygıtın aynı hattı kullanabilmesi için USART iletişimini kullanan RS-485 gibi bir protokolün ya da senkron master/slave modunun kullanılması gerekir.
Mesafeye bağlı olarak verici ve alıcı tarafında “baud rate” değerinde eşleşmeme sorunu ortaya çıkar. Ayrıca hata oranının yüksek olması da bir mesafeden sonra “aliasing-örtüşme” denen ve sinyallerin birbirine girişim yapmasına yol açan bir hataya neden olur. Dolayısıyla özellikle asenkron türü seri haberleşmede hata oranı olabildiğince düşük tutulmalı ve mesafe düz bakır kablolarda birkaç on metreyi geçmemelidir. Çok daha uzun mesafelerde (100m – 300m) iletişim için kalkanlı (shilded) kablo kullanılmalı ve RS-485 protokolü ya da senkron iletişim tercih edilmelidir.
RS-232 Protokolü
Daha önceden de açıklandığı üzere UART haberleşmesini kullanan farklı protokoller vardır. En eskilerinden ve yaygın olanlardan biri RS-232 protokolüdür. RS-232 protokolü bilgisayarlar arası ve diğer çevre aygıtları (modemler, vb.) arasında kısa mesafe seri iletişim için geliştirilmiş bir teknolojidir. Bu protokolün dikkat çeken farkı voltaj seviyesidir. Standart UART/USART protokolünü kullanan mikrodenetleyiciler TTL seviyesinde (0-5V) iletişim gerçekleştirir. Ancak RS-232 protokolünde voltaj aralığı +3V - +15V (CMOS seviyesi) arasındadır ve TIA/EIA-232-F standartı olarak adlandırılır.
Tablo 9.10 UART türüne göre voltaj seviyeleri
UART Türü |
Lojik Seviye |
Voltaj |
TTL | Lojik-1 (Yüksek) | 5V |
Lojik-0 (Alçak) | 0V |
TIA/EIA-232-F | Lojik-1 (İşaret-Mark) | -3 ve -15 V arası |
Lojik-0 (Boşluk-Space) | +3 ve +15 V arası |
Şekil 9.11’de ASCII K (0x48) karakterinin RS-232 protokolünü kullanan UART hattı üzerinden iletim sinyalizasyonu gösterilmiştir. Sinyalizasyon örneğinde görüldüğü üzere -3V ve +3V arası geçerli bir RS 232 seviyesi değildir. ±5 V, ±10 V, ±12 V, ve ±15 V değerleri hat sürücüsüne bağlı olarak görülen çeşitli voltaj seviyeleridir. Lojik-1 negatif voltajla temsil edilir ve “işaret-mark” olarak isimlendirilir. Lojik-0 ise pozitif voltajla temsil edilir ve “boşluk-space” olarak adlandırılır.
Şekil 9.11 RS-232 protokolünü kullanan UART hattında örnek sinyalizasyon (Kaynak: Wikipedia)
Voltaj seviyelerindeki bu fark, TTL - TIA/EIA-232-F dönüşümü yapılmasını gerektirir. Bir mikrodenetleyici ile bir PC arasındaki iletişimde bu voltaj dönüşümünü yapacak çeşitli arayüz çipleri vardır. Bunlardan en yaygını MAX23X serisi olarak bilinen sürücü/alıcı entegreleridir. Günümüzün çoğu masaüstü bilgisayarında ve dizüstü bilgisayarların neredeyse tamamında RS-232 arayüzü kaldırılmış bulunmaktadır. Ancak bir önceki konu başlığında bahsedildiği üzere, bilgisayarların TTL seviyesindeki USB arabirimleriyle uyumlu çalışan bir UART dönüştürücüsü kullanabilirsiniz. Böylece elinizde eski bir çift taraflı dişi DB-9 kablosu varsa bilgisayarınızı ve mikrodenetleyicinizi kolayca birbirine bağlayabilirsiniz. Şekil 9.12’de böyle bir dönüştürücü donanımının farklı iki tasarımı görülmektedir. Soldaki tasarımda DB-9 konnektörü kullanımına gerek yoktur.
Şekil 9.12 USB-UART ve USB-RS232 seri dönüştürücüler
RS-232 protokolünü kullanan donanımda standart UART donanımına göre ek pinler vardır. Bu pinler için geliştirilmiş bağlantı konnektörlerinden yaygın olarak kullanılanı DB-9’dur. DB-9 konnektörünün pin isimleri ve yapısı Şekil 9.13’te gösterildiği gibidir. Tablo 9.12’de bu pinlerin açıklamaları verilmiştir.
Şekil 9.13 RS-232 DB-9 dişi/erkek konnektör yapısı
Mikrodenetleyici ile bilgisayar arasındaki iletişimde bu pinlerden yalnızca RxD, TxD pinlerinin ve şase hattının kullanımı yeterlidir. Diğer pinler farklı modlarda modem haberleşmesi için kullanılır.
Tablo 9.11 DB-9 pin ve sinyal açıklamaları
Pin No | Pin İsmi | Sinyal İsmi | DTE (PC)’ye göre sinyal yönü |
1 | DCD | Data Carrier Detect (DTE uzak DCE aygıtından taşıyıcı siyalini alır) | İçeri |
2 | RxD | Receive Data (DCE’den DTE’ye veri iletir) | İçeri |
3 | TxD | Transmit Data (DTE’den DCE’ye veri iletir) | Dışarı |
4 | DTR | Data Terminal Ready (DTE almaya, başlamaya ya da bir çağrıyı devam ettirmeye hazır) | Dışarı |
5 | GND | Ground (Şase) | - |
6 | DSR | Data Set Ready (DCE aygıtı veri almaya ve göndermeye hazır) | İçeri |
7 | RTS | Request to Send (DTE DCE’nin veri iletmeye hazırlanmasını ister) | Dışarı |
8 | CTS | Clear to Send (DCE DTE’den veri almaya hazır olduğunu bildirir) | İçeri |
9 | RI | Ring Indicator (DCE, telefon hattı üzerinden gelen bir çağrı sinyali algılar) | İçeri |
Şekil 9.14’te PIC16F887 mikrodenetleyicisi ile PC arasında veri alış verişini sağlayan devrenin PROTEUS ISIS® çizimi gösterilmiştir. Uygulamada bir önceki konu başlığında anlatılana benzer olarak COMPIM aracı kullanılmıştır. Sanal COMPORT bağlayıcı yazılımıyla bilgisayarın COM1 ve COM2 portları birbirine bağlanmıştır. Devre şeması içindeki COMPIM aracı COM1 portuna, Mikroelektronika programının USART terminali aracı COM2 portuna bağlanır.
Mikroelektronika programının USART terminal aracıyla Elektronik devre simülasyonu yazılımı da çalıştırıldığında karşılıklı veri akışı başlar. Uygulamada örnek olarak mikrodenetleyiciye bilgisayar üzerinden bir karakter ya da karakter grubu gönderilmekte, bu bilgi aynı şekilde mikrodenetleyiciden bilgisayara iletilmektedir. Mikrodenetleyiciye bilgisayardan iletilen bilgi PROTEUS-ISIS® yazılımının “Virtual Terminal” yazılımıyla izlenebilmektedir. Bilgisayara mikrodenetleyiciden gönderilen bilgi ise USART terminal aracının “Receive” ekranından izlenebilir.
Şekil 9.14 Mikrodenetleyici ve PC arasında seri iletişim uygulaması
Bu çalışmanın gerçek fiziki uygulamasında MAX232 ya da türevi bir dönüştürücü entegresi şemada gösterildiği şekilde bağlanarak kullanılır. Uygulamanın kütüphane kullanılmadan hazırlanmış kaynak kodları Tablo 9.12’de gösterilmiştir. MAX232 entegresinin gerçek fiziki ortamda uygulaması yapılırken DC beslemesi 5V VCC kaynağından sağlanır.
Tablo 9.12 Mikrodenetleyici ve PC arasında seri iletişim uygulaması mikroC kaynak kodu
char veri = 0;
char kontrol = 0;
//Fonksiyon deklerasyonlari
void UART_RXTX_Init(void);
void port_Init(void);
//Ana fonksiyon
void main(void) {
port_Init();
UART_RXTX_Init();
while(1) {
while(!TRMT){} //TSR kaydedicisi dolu oldugu surece (lojik-0) doner
if(kontrol){
TXREG = veri;
// TXREG = 10; //satirkaydirma (LF) karakteri
// TXREG = 13; //satirbasi (CR) karakteri
kontrol = 0;
}
}
}
//UART alici-verici olarak ayarlaniyor
void UART_RXTX_Init(void){
BAUDCTL = 0;
TXSTA.BRGH = 1;
SPBRG = 51;
TXSTA.SYNC = 0;
RCSTA.SPEN = 1;
TXSTA.TXEN = 1;
RCSTA.CREN = 1;
}
//Portlar ve kesmeler ayarlaniyor
void port_Init(void){
OSCCON = 0X70;
INTCON.GIE = 1;
INTCON.PEIE = 1;
//PIE1.TXIE = 1;
PIE1.RCIE = 1;
ANSEL = 0; //tüm portlar dijital I/O
ANSELH = 0;
CM1CON0 = 0; //karsilastiricilar kapali
CM2CON0 = 0;
TRISA = 0;
PORTA = 0;
TRISB = 0;
PORTB = 0;
TRISC = 0xC0;
PORTC = 0;
TRISD = 0;
PORTD = 0;
TRISE = 0;
PORTE = 0;
}
//Alma islemi kesme ile kontrol ediliyor
void interrupt(void){
if(PIR1.RCIF){
veri = RCREG;
PORTD = veri;
kontrol = 1;
}
}
Not: Simülasyon ortamında, kullanılan yazılımların sürümüne ve MAX23X entegrelerinin modellenmesine bağlı olarak alınan ve gönderilen verilerin ikilik dizisi farklı olabilir. Böyle bir durumda yazılım sürümünüzü değiştirmeniz gerekebilir. Doğru veri alımından ve iletiminden emin olmak için simülasyon ortamında MAX23X entegresini çıkartıp mikrodenetleyicinizi COMPIM aracına doğrudan bağlamayı deneyebilirsiniz.
RS-485 Protokolü
RS-485 çok sayıda aygıtın tek bir veri yoluna bağlanmasını sağlayan çok noktalı bir seri iletişim protokolüdür. TIA-485(-A) ya da EIA-485 olarak da bilinir ve bu kodlar seri haberleşme sistemlerindeki sürücü ve alıcıların elektriksel özelliklerini tanımlar. Kontrol ve otomasyon sistemlerinde yaygın olarak tercih edilen bir standarttır.
Mikrodenetleyicilerin sahip olduğu USART/E-USART modülleri kullanılarak RS-485 veriyolu haberleşmesi yapılabilir. İlgili kaydediciler ayarlanarak çoklu “slave” modunda çalışma sağlanır. Aynı anda tek bir aygıt veri iletirken (master), hat üzerindeki diğer aygıtlar dinleme (slave) durumundadır. Her bir “slave” aygıtın tekil bir adresi vardır ve yalnızca verinin gönderildiği adrese sahip aygıt veriyi alır. Haberleşme “master” olarak ayarlanmış aygıt tarafından başlatılır. Aynı anda yalnızca tek bir aygıtın 485 veriyolu üzerinden iletim yapmasını sağlamak programcının sorumluluğundadır.
RS-485 protokolü RS-232’de olduğu gibi ayrı bir elektriksel sinyalizasyon standartı olduğundan, mikrodenetleyiciler ile kullanılabilmesi için özel bir dönüştürücü donanımı gerektirir. Mikrodenetleyicinin USART/E-USART modülünün RX/TX pinleri RS-485 arayüz alıcı/vericisi donanımına bağlanır. LTC485 ya da MAXIM firmasının MAX48X serisi (MAX481, MAX483, MAX485, MAX487, vb.) dönüştürücüleri bu standartta iletişim için kullanılabilir.
İletimde yaygın olarak 120 Ω hat empedansına sahip kalkanlı burgulu çift (shielded twisted pair) kablo kullanımı tavsiye edilir. RS-485 uzun mesafeli iletişim sunar. Şekil 9.15’te RS-485 ve diğer bazı iletişim standartlarının mesafe ve sinyalizasyon karşılaştırması gösterilmiştir.
Şekil 9.15 Çeşitli veri iletişim standartlarında sinyalizasyon oranına göre mesafe karşılaştırmaları (Kaynak: Texas Instruments)
RS-485 sistemlerin genel olarak 10 Mbit/s (saniyede 10 Megabit) veri iletimi gerçekleştirebileceği kabul edilir. Daha düşük veri iletim hızlarında mesafe 1000 metre ve üzerine çıkabilir. Genel bir ipucu olarak iletim mesafesi ve veri hızı çarpımının 108’i geçmemesi gerektiği şeklinde bir kural vardır. Örnek vermek gerekirse; mesafe 100 m olarak varsayıldığında, iletilebilecek en yüksek veri hızının 108/100 = 1 Mbit/s olması tavsiye edilir. Dolayısıyla eğer 1 km’lik uzun mesafe iletim gerçekleştirilecekse, maksimum veri hızı 100 kbit/s’ye düşürülmelidir.
RS-485 iletişimde yaygın olarak karşılıklı tek yönlü (half-duplex) iletişim yapılır. Bu durumda MAX485 ve MAX487 dönüştürücüleri kullanılabilir. Karşılıklı çift yönlü (full-duplex) iletişim yapmak da mümkündür. Bu durumda MAX488-MAX491 sürücüleri kullanılır. Bu başlık altında karşılıklı tek yönlü iletişimden bahsedilecektir. Şekil 9.16’da RS-485 ağında MAX481/MAX483/MAX485/MAX487 sürücüleriyle gerçekleştirilen yarım dubleks iletişim bağlantı örneği gösterilmiştir.
Şekil 9.16 RS-485 ağında karşılıklı tek yönlü iletişim (Kaynak: Maxim Integrated Products)
Ağdaki aygıtların papatya zinciri (daisy-chain) ya da yol (bus) topolojisiyle bağlantısı tavsiye edilir. Yıldız ya da halka topolojileri hat üzerinde oluşan yansıma etkisi nedeniyle tavsiye edilmez. Yaygın olarak kullanılan bağlantı topolojileri Şekil 9.17’de gösterilmiştir.
Şekil 9.17 RS-485 ağında tercih edilen topolojiler (Kaynak: Texas Instruments)
Yol topolojisinde karşılıklı iki uç arasında uzun mesafeli bağlantı kurulur. Stub adı verilen ekler ile bu ana hatta bağlantı yapılır. Ana hattın uzunluğu iletilen verinin en yüksek bit hızına bağlı olarak yüzlerce metre mesafeye çıkabilirken bu ana hatta bağlanacak eklerin kablo uzunluğu bir kaç metreyi geçmemelidir (en fazla 5 m).
İki uç arasında 120 Ω’luk dirençlerle hat sonlandırması yapılır. Sonlandırma dirençleri genellikle hatta kullanılan kablonun empedansına eş değerde seçilir. Sonlandırma uçlarında ayrıca pull-up ve pull-down dirençleri de kullanılabilir. Böylece daha güvenilir bir sinyalleşme temin edilmiş olur. İletişim lojik-0 değerindeki “Start Bit” ile başladığı için hattın boştayken lojik-1 seviyesinde çekili tutulması garanti edilmiş olur.
RS-485 iletişim ağında bilgi sinyali, iki hat üzerinde oluşturulan fark (diferansiyel) sinyali tekniğiyle üretilir. Bilgi, A ve B olarak (bazı kaynaklarda ‘+‘ ve ‘-‘ ) adlandırılan hatlar üzerinde oluşturulan fark sinyaliyle iletilir. Bu teknik, endüstriyel otomasyon sistemleri için geliştirilmiş olan RS-485 arayüzünün yoğun manyetik girişime sahip ortamlarda daha verimli ve güvenilir çalışmasını sağlar.
Tersleyen çıkış B ya da ‘-‘ olarak, terslemeyen çıkış A ya da ‘+’ olarak gösterilir. Şekil 9.18’de voltaj farkına göre lojik-1 ve lojik-0 sinyallerinin elde edilmesi gösterilmiştir.
Şekil 9.18 RS-485 hattında lojik-0 ve lojik-1 değerleri (Kaynak: Wikibooks)
RS-485 sinyalleşmesinde +200 mV aralığı tanımsızdır. Farklı genlik aralıkları mevcut olup yaygın olarak üst genlik değeri +7 V’tur. Şekilde de görüldüğü üzere lojik-0 durumunda A hattı B hattına göre yüksek potansiyeldedir. Lojik-1 durumunda ise A hattı B hattına göre düşük potansiyeldedir.
MikroC programı RS-485 iletişimi için güçlü bir kütüphane sunar. Bu kütüphanenin başlıca sabitleri şunlardır:
- START bayt değeri = 150
- STOP bayt değeri = 169
- 50 numaralı adres değeri tüm köle aygıtlar için ortak yayın adresidir. (50 adres değerini içeren paketler, 150 ve 169 adresli köleler haricinde tüm köle aygıtlar tarafından alınır).
MikroC RS-485 kütüphanesi komutları ve açıklamaları aşağıdaki gibidir:
RS485Master_Init : RS-485 haberleşmesi için MCU’yu Master olarak ayarlar.
Evrensel değişkenler :
- RS485_rxtx_pin – Bu pin RS-485 alıcı/vericisinin RE/DE girişine bağlanır. RE/DE sinyali RS485 alıcı/vericisinin çalışma modunu belirler.
- RS485_rxtx_pin_direction – RS-485 iletme/alma pininin yönünü belirler.
Örnek Kullanımı : |
// RS485 modülü pini
sbit RS485_rxtx_pin at RC2_bit;
/* PORTC 2 numaralı bit gönderme/alma pini olarak ayarlanıyor */
// Pin yönü
sbit RS485_rxtx_pin_direction at TRISC2_bit;// RxTx pin yönü belirlenir
...
UART1_Init(9600); //UART1 modülü başlatılıyor
RS485Master_Init(); //RS-485 iletişimi için MCU master olarak ayarlanır
|
RS485Master_Receive : Köle aygıtlardan mesaj almak için kullanılır. Mesajlar çok baytlıdır, dolayısıyla bu komut her bayt alındığında çağrılmalıdır.
Parametreler :
- data_buffer: Alınan veriyi tutmak için aşağıdaki şekilde kullanılan 7 baytlık tampon bellek:
- data[0..2]: mesaj içeriği
- data_buffer[3]: alınan mesaj baytlarının sayısı, 1–3
- data_buffer[4]: mesaj alındığında 255 değerine kurulur
- data_buffer[5]: hata oluşursa 255 değerine kurulur
- data_buffer[6]: Mesaj gönderen köle aygıtın adresi
Örnek Kullanımı : |
char msg[8];
...
RS485Master_Receive(msg);
|
RS485Master_Send : Köle aygıtlara mesaj gönderir. Mesaj formatı kullanım örneğinde gösterildiği gibi olmalıdır.
Parametreler :
- data_buffer: gönderilecek veri
- datalen: iletilecek baytların sayısı. Geçerli değerler: 0...3
- Slave_address: Köle aygıt ya da aygıtların adresi
Örnek Kullanımı : |
char msg[8];
...
//0x12 hekzadesimal adresli köle aygıta verinin 3 baytlık kısmı gönderilir
RS485Master_Send(msg, 3, 0x12);
|
Açıklama :Aynı anda yalnızca tek bir aygıtın veri göndermesi kullanıcı sorumluluğundadır.
RS485Slave_Init : RS-485 haberleşmesi için MCU’yu Slave olarak ayarlar.
Evrensel değişkenler :
- RS485_rxtx_pin – Bu pin RS-485 alıcı/vericisinin RE/DE girişine bağlanır. RE/DE sinyali RS485 alıcı/vericisinin çalışma modunu belirler.
- RS485_rxtx_pin_direction – RS-485 iletme/alma pininin yönünü belirler.
Örnek Kullanımı : |
MCU’yu 160 numaralı adreste köle olarak başlatır.
|
// RS485 module pinout
sbit RS485_rxtx_pin at RC2_bit; /* PORTC 2 numaralı bit gönderme/alma pini olarak ayarlanıyor */
// Pin direction
sbit RS485_rxtx_pin_direction at TRISC2_bit; // RxTx pin yönü belirlenir
...
UART1_Init(9600); // UART1 modülü başlatılıyor
RS485Slave_Init(160); /* RS-485 iletişimi için MCU 160 numaralı adres değeriyle köle olarak ayarlanıyor */
|
RS485Slave_Receive : “Master” aygıttan mesaj almak için kullanılır. Eğer köle aygıtın adresi ve “master” aygıtın adresi eşleşmezse mesaj dikkate alınmaz.
Parametreler :
- data_buffer: Alınan veriyi tutmak için aşağıdaki şekilde kullanılan 6 baytlık tampon bellek:
- data[0..2]: mesaj içeriği
- data_buffer[3]: alınan mesaj baytlarının sayısı, 1–3
- data_buffer[4]: mesaj alındığında 255 değerine kurulur
- data_buffer[5]: hata oluşursa 255 değerine kuulur
Örnek Kullanımı : |
char msg[8];
...
RS485Slave_Receive(msg);
|
RS485Slave_Send : “Master” aygıta mesaj gönderir. Mesaj formatı kullanım örneğinde gösterildiği gibi olmalıdır.
Parametreler :
- data_buffer: gönderilecek veri
- datalen: iletilecek baytların sayısı. Geçerli değerler: 0...3
Örnek Kullanımı : |
char msg[8];
...
// Master aygıta verinin 2 baytı gönderilir.
RS485Slave_Send(msg, 2);
|
Açıklama :Aynı anda yalnızca tek bir aygıtın veri göndermesi kullanıcı sorumluluğundadır.
Şekil 9.19’da 3 adet PIC16F887 mikrodenetleyicisinin RS-485 hattı üzerinden birbirleriyle master-slave haberleşmesine örnek verilmiştir. Uygulamada Efendi (Master) aygıt 160 numaralı Köle (Slave) aygıta mesaj gönderir ve yanıt için bekler. Köle aygıt veriyi kabul eder, aldığı veriyi arttırır ve “master” aygıta geri gönderir. Veriyi alan “master” aygıt aynı işlemi yapar ve arttırılmış veriyi köle aygıta geri gönderir. İletişimde her hata oluşması durumunda “master” ve “slave” MCU’ların PORTD değerleri birer arttırılır. Uygulamaya 3’üncü bir MCU eklenmiştir. 50 numaralı adres üzerinden yapılan ortak yayın, bu 3’üncü MCU tarafından alınır ve dat[0] verisi A portundan gösterilir. İletişimde 10 defadan fazla hata olması durumunda 3’üncü MCU’nun PORTE çıkışları birer arttırılır.
“Master” aygıt alınan veriyi PORTB üzerinde gösterirken, alma sırasında oluşan hata 0xAA değeriyle ve takip eden her başarısız deneme PORTD üzerinden gösterilir.
Şekil 9.19 3 adet PIC16F887'nin RS-485 hattı üzerinden master-slave haberleşmesi
Devre uygulamasında da görüleceği gibi MAX487 entegresinin (RE) ve DE pinleri mikrodenetleyicinin ortak pinine bağlanmıştır. Bu pinler entegrenin alma ve gönderme durumunu etkinleştirme pinleridir. (RE) pini lojik terslemeli olduğundan mikrodenetleyicinin tek bir pini ile aynı anda yalnızca alma ya da gönderme işlemi etkinleştirilmiş olur.
Tablo 9.13 RS-485 Master MCU mikroC kodu
char dat[7]; // mesaj alma/gönderme verisi
char i,j;
sbit rs485_rxtx_pin at RC2_bit; // iletim/gönderim pini tanimlanir
sbit rs485_rxtx_pin_direction at TRISC2_bit; // iletim/gönderim pin yönü
// Kesme rutini
void interrupt(){
if(RCIF_bit){
RS485Master_Receive(dat);
}
}
void main(){
unsigned int cnt = 0;
OSCCON = 0X72; // Dengeli 8MHz dâhili osilatör
ANSEL = 0; // AN pinleri dijital I/O olarak ayarlanir
ANSELH = 0;
C1ON_bit = 0; // karsilastiricilar kapali
C2ON_bit = 0;
PORTB = 0;
PORTD = 0;
TRISB = 0;
TRISD = 0;
UART1_Init(9600); // UART1 modülü baslatilir
Delay_ms(100);
RS485Master_Init(); // MCU Master olarak baslatilir
dat[0] = 0;
dat[1] = 127;
dat[2] = 255;
dat[4] = 0; // mesaj alindi bayragi temizlenir
dat[5] = 0; // hata bayragi temizlenir
dat[6] = 0;
RS485Master_Send(dat,3,160);
RCIE_bit = 1; // UART1 alma kesmesi etkin
TXIE_bit = 0; // UART1 iletim kesmesi kapali
PEIE_bit = 1; // cevresel kesmeler etkin
GIE_bit = 1; // tum kesmeler etkin
while (1){
// gecerli mesaj alimi tamamlandiginda
// data[4] 255 degerine kurulur
if(dat[6] == 160){ //Adresi 160 olan köle aygitin verisi islenir
if (dat[5]) { // hata algilanirsa, PORTD ve cnt degiskeni artirilir
PORTD++;
cnt++;
}
if (dat[4]) { // mesaj basarili olarak alindiysa
dat[4] = 0; // mesaj alindi bayragini temizle
j = dat[3]; // dat[3] verisi bu uygulamada 3 degerine kuulmustur
for (i = 1; i <= j; i++) { // 3 baytlik veri gonderildiginden
PORTB = dat[i-1];//PORTB uzerinde sirasiyla dat[0], dat[1] ve dat[2]
Delay_ms(250); // 250msn'lik araliklarla gösterilir
} // alinan dat[0]'i arttir, data[1] ve dat[2]'yi azalt
dat[0] = dat[0]+1; // köle aygita geri gönder
dat[1] = dat[1]-1;
dat[2] = dat[2]-1;
Delay_ms(1);
RS485Master_Send(dat,3,160);//160 adresli köleye islenmis veri gönder
}
if (cnt > 10) { // eger iletisimde 10 defadan fazla hata
RS485Master_Send(dat,1,50); // olursa mesaji yayin adresinden gönder
cnt=0;
}
}
}
}
Tablo 9.14 RS-485 Slave01 MCU mikroC kodu
char dat[6]; // mesaj alma/gönderme verisi
char i,j;
sbit rs485_rxtx_pin at RC2_bit; // iletim/gönderim pini tanimlanir
sbit rs485_rxtx_pin_direction at TRISC2_bit;// iletim/gönderim pin yönü
// Kesme rutini
void interrupt(){
if(RCIF_bit){
RS485Slave_Receive(dat);
}
}
void main() {
OSCCON = 0X72; // Dengeli 8MHz dâhili osilatör
ANSEL = 0; // AN pinleri dijital I/O olarak ayarlanir
ANSELH = 0;
C1ON_bit = 0; // karsilastiricilar kapali
C2ON_bit = 0;
PORTB = 0;
PORTD = 0;
TRISB = 0;
TRISD = 0;
UART1_Init(9600); // UART1 modülü baslatilir
Delay_ms(100);
RS485Slave_Init(160); // MCU 160 nolu adresle köle olarak baslatilir
dat[4] = 0; // mesaj alindi bayragi temizlenir
dat[5] = 0; // hata bayragi temizlenir
RCIE_bit = 1; // UART1 alma kesmesi etkin
TXIE_bit = 0; // UART1 iletim kesmesi pasif
PEIE_bit = 1; // cevresel donanim kesmesi etkin
GIE_bit = 1; // tüm kesmeler etkin
while (1){
if (dat[5]){ // iletisimde hata algilanirsa, PORTD'yi arttirarak
PORTD++; // hatayi bildir ve hata baytini temizle
dat[5] = 0;
}
if (dat[4]) { // gecerli mesaj alimi tamamlandiginda
dat[4] = 0; // data[4] temizlenir
j = dat[3];
for (i = 1; i <= j;i++){
PORTB = dat[i-1];
Delay_ms(250);
}
dat[0] = dat[0]+1; // dat[0]'i arttir, dat[1] ve dat[2]'yi azalt
dat[1] = dat[1]-1;
dat[2] = dat[2]-1;
Delay_ms(1);
RS485Slave_Send(dat,3); // ve master aygita geri gönder
}
}
}
Tablo 9.15 RS-485 Slave02 MCU mikroC kodu
char dat[6];
char i,j;
sbit rs485_rxtx_pin at RC2_bit;
sbit rs485_rxtx_pin_direction at TRISC2_bit;
void interrupt(){
if(RCIF_bit){
RS485Slave_Receive(dat);
}
}
void main() {
OSCCON = 0X72;
ANSEL = 0;
ANSELH = 0;
C1ON_bit = 0;
C2ON_bit = 0;
PORTA = 0;
PORTB = 0;
PORTD = 0;
PORTE = 0;
TRISA = 0;
TRISB = 0;
TRISD = 0;
TRISE = 0;
UART1_Init(9600);
Delay_ms(100);
RS485Slave_Init(100);
dat[4] = 0;
dat[5] = 0;
RCIE_bit = 1;
TXIE_bit = 0;
PEIE_bit = 1;
GIE_bit = 1;
while(1){
if (dat[5]){ // hata varsa
PORTE++;
dat[5] = 0;
}
if (dat[4]){ // mesaj alindiysa
dat[4] = 0;
PORTA = dat[0];
Delay_ms(1);
}
}
}
Gerçek uygulamada, Şekil 9.20’de gösterilen RS-485 dönüştürücü modülü ile uzun mesafeli dengeli ve kararlı çalışma sağlanabilir.
Şekil 9.20 RS-485 TTL dönüştürücü modülü
MSSP Modülü İle Seri İletişim
Master Synchronous Serial Port (MSSP) modülü diğer çevresel aygıtlar ve mikrodenetleyicilerle iletişim kurmayı sağlayan bir seri iletişim modülüdür. İletişim kurulan çevresel aygıtlar; harici EEPROM bellekleri, kaydırmalı kaydediciler, ekran sürücüleri, A/D çeviricileri, D/A çeviriciler, vb. olabilir. SPI ve I2C™ olmak üzere iki farklı çalışma arabirimine sahiptir.
- SPI (Serial Peripheral Interface) modu
- I2C™ (Inter Integrated Circuit) modu: Bu mod 2-Wire modu olarak da literatürde yer alır.
SPI arabirimi aşağıdaki modları ve özellikleri destekler:
- Sahip (Master) modu
- Bağımlı (Slave) modu
- Saat paritesi
- Bağımlı seçme senkronizasyonu (yalnızca bağımlı modu)
- Bağımlı aygıtların papatya-zinciri formatında bağlantısı
I2C™ arabirimi aşağıdaki modları ve özellikleri destekler:
- Sahip modu
- Bağımlı modu
- NACKing baytı (bağımlı modu)
- Sınırlı çoklu-sahip modu
- 7-bit ve 10-bit adresleme
- Başlama ve Bitiş kesmeleri
- Kesme maskeleri
- Saat darbesi genişletmesi
- Veri yolu çarpışma algılaması
- Genel çağrı adresi eşleştirmesi
- Adres maskeleme
- Adres tutma ve veri tutma modları
- Seçilebilir SDA tutma zamanları
Bünyesinde bir adet MSSP modülü olan PIC mikrodenetleyicilerinde denetim ve kontrol işlemleri için çeşitli kaydediciler kullanılır. Bu kaydediciler PIC12F1840 için aşağıdaki gibidir:
-
SSPSTAT : İletişimin ne durumda olduğunu izlemeyi sağlayan kaydedicidir.
-
SSP1CON1 : İletişim modlarını ayarlamayı sağlayan kaydedicidir.
-
SSP1CON2 : Sahip olduğu bitlerle iletişim kanalındaki veri akışının durumu kontrol edilir.
-
SSP1CON3 : Veri paketinin son bitiyle ilgili kontrol işlemlerini yapan kaydedicidir.
-
SSP1MSK : I2C iletişiminde 7-bit ya da 10-bit adres eşleştirmesi için kullanılır.
-
SSP1ADD : I2C iletişiminde adres olarak ya da SPI “master” modunda ve I2C iletişimlerinde baud hızı üreticisinin ayarlayıcısı olarak kullanılır.
Aşağıda PIC12F1840 mikrodenetleyicisi için MSSP modülünün kaydedicileri gösterilmiştir. SSP1CON3 kaydedicisi hariç diğer tüm kaydediciler PIC16F887 mikrodenetleyicisinde de aynı özelliklere sahiptir.
SSP1STAT: SSP1 DURUM KAYDEDİCİSİ (ADRES: 214h – PIC12F1840, 94h – PIC16F887)
bit 7 SMP: SPI veri girişi örnekleme biti
SPI Master modu:
1 = Giriş verisi veri çıkış süresinin sonunda örneklenir.
0 = Giriş verisi veri çıkış süresinin ortasında örneklenir.
SPI Slave modu:
SMP biti SPI Slave modunda kullanılırken temizlenmelidir.
I2C Master ya da Slave modu:
1 = Standart hız modu için değişim oranı (Slew Rate) kontrolü kapatılır (100 kHz ve 1 MHz).
0 = Yüksek hız modu için değişim oranı etkinleştirilir(400 kHz).
bit 6 CKE: SPI saat darbesi kenar seçme biti (yalnızca SPI modu)
SPI Master ya da Slave modu:
1 = İletim aktif saat darbesi durumundan boş saat darbesi durumuna geçişte olur.
0 = İletim boş saat darbesi durumundan aktif saat darbesi durumuna geçişte olur.
Yalnızca I2C™ modu:
1 = Eşik değerler SMBus spesifikasyonu ile uyumlu olsun diye giriş lojiği etkinleştirilir.
0 = SMBus özel girişleri kapatılır.
bit 5 D/A: Veri/Adres biti (yalnızca I2C modu)
1 = Alınan ya da iletilen son baytın veri olduğunu işaret eder.
0 = Alınan ya da iletilen son baytın adres olduğunu işaret eder.
bit 4 P: Durma (Stop) biti
(Yalnızca I2C modu. Bu bit, SSP1EN biti temizlenip MSSP1 modülünün kapatılmasıyla temizlenir.)
1 = Son olarak tespit edilen bitin Stop biti olduğunu işaret eder (bu bit Reset durumunda ‘0’ olur).
0 = Son olarak Stop bitinin tespit edilmediğini işaret eder.
bit 3 S: Başlama (Start) biti
(Yalnızca I2C modu. Bu bit, SSP1EN biti temizlenip MSSP1 modülünün kapatılmasıyla temizlenir.)
1 = Son olarak tespit edilen bitin Start biti olduğunu işaret eder (bu bit Reset durumunda ‘0’ olur).
0 = Son olarak Start bitinin tespit edilmediğini işaret eder.
bit 2 R/W: Okuma/Yazma biti bilgisi (yalnızca I2C modu)
Bu bit son adres eşleşmesini takip eden R/W biti bilgisini tutar. 7-bitlik ya da 10-bitlik adres bilgisi sonrası gelen bittir. Bu bit yalnızca adres eşleşmesinden sonraki Start bitine, Stop bitine ya da ACK değil bitine kadar geçerlidir.
I2C Slave modu:
1 = Okuma
0 = Yazma
I2C Master modu:
1 = İletim var
0 = İletim yok. Bu durumdayken, SEN, RSEN, RCEN ya da ACKEN bitlerinin durumu da MSSP1 modülünün boşta olup olmadığını işaret edecektir.
bit 1 UA: Adres bitini günceller (yalnızca 10-bit I2C modu)
1 = Kullanıcının SSP1ADD kaydedicisindeki adresi güncellemesi gerektiğini ifade eder.
0 = Adresin güncellenmesine gerek yoktur.
bit 0 BF: Tampon dolu durum biti
Alma (SPI ve I2C modları):
1 = Alma tamamlandı, SSP1BUF dolu
0 = Alma tamamlanmadı, SSP1BUF boş
İletme (yalnızca I2C modu):
1 = Veri iletimi işliyor (ACK ve Stop bitlerini içermez), SSP1BUF dolu
0 = Veri iletimi tamamlandı (ACK ve Stop bitlerini içermez), SSP1BUF boş
SSP1CON1: SSP1 KONTROL KAYDEDİCİSİ 1 (ADRES: 215h – PIC12F1840, 14h – PIC16F887)
bit 7 WCOL: Yazma çarpışması algılama biti
Master modu:
1 = I2C koşulları bir iletimin başlaması için uygun değilken, SSP1BUF kaydedicisine bir yazma eylemi algılanır.
0 = Çarpışma yok
Slave modu:
1 = SSP1BUF kaydedicisi önceki sözcüğü göndermeye devam ediyorken, kendisine yenisinin yazılması durumu gerçekleşir (yazılım içinde temizlenmelidir).
0 = Çarpışma yok
bit 6 SSP1OV: Alma taşması işaretçisi biti(1)
SPI modu:
1 = SSP1BUF kaydedicisi hala önceki bilgiyi tutuyorken yeni bir bayt alınır. Taşma durumunda, SSP1SR içindeki veri kaybolur. Taşma yalnızca Slave modunda gerçekleşir. Slave modunda, kullanıcı taşma olayına sebebiyet vermemek için, yalnızca veri gönderimi yapsa bile, SSP1BUF kaydedicisini okumalıdır. Master modunda, her yeni alma ve iletim SSP1BUF kaydedicisine yazma eylemiyle başladığı için taşma biti kurulmaz.
0 = Taşma yok
I2C modu:
1 = SSP1BUF hala önceki baytı tutuyorken yeni bir bayt alınır. SSP1OV biti iletim modunda dikkate alınmaz (yazılım içinde temizlenmelidir).
0 = Taşma yok
bit 5 SSP1EN: Eşzamanlı Seri Portunu Etkinleştirme biti
Her iki modda da, etkinleştirildiğinde, bu bitler giriş ya da çıkış olarak düzgün şekilde yapılandırılmalıdır.
SPI modu:
1 = Seri portu etkinleştirir ve SCK, SDO, SDI ve (SS) ̅ bitlerini seri port pinlerinin kaynağı olarak yapılandırır(2).
0 = Seri portu kapatır ve bu pinleri I/O pini olarak yapılandırır.
I2C modu:
1 = Seri portu etkinleştirir ve SDA ve SCL pinlerini seri port pinlerinin kaynağı olarak yapılandırır(3).
0 = Seri portu kapatır ve bu pinleri I/O pini olarak yapılandırır.
bit 4 CKP: Saat darbesi polaritesini seçme biti
SPI modu:
1 = Saat darbesi için boşta (Idle) durumu yüksek seviyedir (lojik-1).
0 = Saat darbesi için boşta durumu düşük seviyedir (lojik-0).
I2C Slave modu:
SCL serbest bırakma kontrolü
1 = Saat darbesi etkinleştirilir.
0 = Saat darbesini düşükte tutar (saat darbesinin genişletilmesi). (Veri kurulum süresinin temin edilmesi için kullanılır.)
I2C Master modu:
Bu modda kullanılmaz
bit 3-0 SSP1M<3:0>: Eşzamanlı Seri Portu Seçme bitleri
0000 = SPI Master modu, saat darbesi = FOSC/4
0001 = SPI Master modu, saat darbesi = FOSC/16
0010 = SPI Master modu, saat darbesi = FOSC/64
0011 = SPI Master mode, saat darbesi = (TMR2 çıkışı)/2
0100 = SPI Slave modu, saat darbesi = SCK pini, SS pin kontrolü etkinleştirildi
0101 = SPI Slave modu, saat darbesi = SCK pini, SS pin kontrolü kapatıldı, SS I/O pini olarak kullanılabilir
0110 = I2C Slave modu, 7-bit adres
0111 = I2C Slave modu, 10-bit adres
1000 = I2C Master modu, saat darbesi = FOSC/(4 * (SSP1ADD+1))(4)
1001 = Ayrıldı
1010 = SPI Master modu, saat darbesi = FOSC/(4 * (SSP1ADD+1))(5)
1011 = I2C firmware kontrollü Master modu (Slave boşta)
1100 = Ayrıldı
1101 = Ayrıldı
1110 = I2C Slave modu, Start ve Stop bitleri kesmelerinin etkinleştirildiği 7-bit adres
1111 = I2C Slave modu, Start ve Stop bitleri kesmelerinin etkinleştirildiği 10-bit adres
Not
1: Master modunda, her yeni alma ve iletme SSP1BUF kaydedicisine yazma eylemiyle başladığından taşma kurulmaz.
2: Etkinleştirildiğinde, bu pinler uygun şekilde giriş ya da çıkış olarak yapılandırılmalıdır.
3: Etkinleştirildiğinde, SDA ve SCL pinleri giriş olarak yapılandırılmalıdır.
4: I2C modu için SSP1ADD’nin 0, 1 ya da 2 değerleri desteklenmez.
5: SSP1ADD’nin 0 değeri desteklenmez. Bunun yerine SSP1M = 0000 olarak kurun.
SSP1CON2: SSP1 KONTROL KAYDEDİCİSİ 2 (ADRES: 216h – PIC12F1840, 91h – PIC16F887)
bit 7 GCEN: Genel çağrı etkinleştirme biti (yalnızca I2C Slave modu)
1 = SSP1SR içinde genel bir çağrı adresi (0x00 ya da 00h) alındığında kesmeyi etkinleştirir.
0 = Genel çağrı adresi kapalı
bit 6 ACKSTAT: Onay (Acknowledge) durumu biti (yalnızca I2C modu)
1 = Onay alınmadı
0 = Onay alındı
bit 5 ACKDT: Onay veri biti (yalnızca I2C modu)
Alma modunda: Kullanıcı bir alma eylemi sonunda bir onay (Acknowledge) sırası başlattığında değer iletilir.
1 = Onay değil
0 = Onay
bit 4 ACKEN: Onay sırası etkinleştirme biti (yalnızca I2C Master modu)
Master alma modunda:
1 = SDA ve SCL pinlerinde onay sırası başlatır ve ACKDT veri bitini iletir. Otomatik olarak donanım tarafından temizlenir.
0 = Onay dizisi boşta
bit 3 RCEN: Alma etkinleştirme biti (yalnızca I2C Master modu)
1 = I2C için alma modu etkin
0 = Alma boşta
bit 2 PEN: Durma (Stop) koşulu etkinleştirme biti (yalnızca I2C Master modu)
SCK serbest bırakma kontrolü:
1 = SDA ve SCL pinlerinde durma koşulunu başlatır. Donanım tarafından otomatik olarak temizlenir.
0 = Durma koşulu boşta
bit 1 RSEN: Tekrarlı başlama (Start) koşulu etkinleştirme biti (yalnızca I2C Master modu)
1 = SDA ve SCL pinlerinde tekrarlı başlama koşulunu başlatır. Donanım tarafından otomatik olarak temizlenir.
0 = Tekrarlı başlama koşulu boşta
bit 0 SEN: Başlama koşulu etkinleştirme biti (yalnızca I2C Master modu)
Master modu:
1 = SDA ve SCL pinlerinde başlama koşulunu başlatır. Donanım tarafından otomatik olarak temizlenir.
0 = Başlama koşulu boşta
Slave modu:
1 = Slave iletim ve Slave alma modlarının her ikisi için de saat darbesinin genişletilmesi etkinleştirilir.
0 = Saat darbesinin genişletilmesi kapatılır.
Not 1: ACKEN, RCEN, PEN, RSEN, SEN bitleri için: Eğer I2C modülü boşta değilse bu bit kurulmayabilir ve SSP1BUF kaydedicisine yazılmayabilir ya da SSP1BUF kaydedicisine yazma eylemi kapatılır.
SSP1CON3: SSP1 KONTROL KAYDEDİCİSİ 3 (ADRES: 217h – PIC12F1840)
bit 7 ACKTIM: Onay süresi durum biti (yalnızca I2C modu)(3)
1 = I2C yolunun bir onay işlemi sırasında olduğunu işaret eder, SCL saat darbesinin 8’inci düşen kenarında kurulur.
0 = Bir onay sırasının olmadığı anlaşılır, SCL saat darbesinin 9’uncu yükselen kenarında temizlenir.
bit 6 PCIE: Durma koşulu kesme etkinleştirme biti (yalnızca I2C modu)
1 = Durma koşulunun algılanma kesmesi etkinleştirilir.
0 = Durma koşulunu algılama kesmeleri kapatılır(2)
bit 5 SCIE: Başlama koşulu kesme etkinleştirme biti (yalnızca I2C modu)
1 = Başlama ya da yeniden başlama koşullarının algılanma kesmesi etkinleştirilir.
0 = Başlama koşulunu algılama kesmeleri kapatılır(2)
bit 4 BOEN: Tamponun üzerine yazma etkinleştirme biti
SPI Slave modu:(1)
1 = SSP1BUF tamponunun içeriği, BF bitinin durumuna bakılmaksızın her yeni bilgi geldiğinde güncellenir.
0 = SSP1SAT kaydedicisinin BF biti kuruluyken, yeni bir bilginin gelmesi durumunda SSP1CON1 kaydedicisinin SSP1OV biti kurulur ve tampon içeriği güncellenmez.
I2C Master modu ve SPI Master modu:
Bu bit önemsenmez.
I2C Slave modu:
1 = SSP1BUF tamponu güncellenir ve yalnızca BF bitinin ‘0’ olması durumunda SSP1OV bitinin durumuna bakılmaksızın, alınan bir adres/veri baytı için ACK sinyali üretilir.
0 = SSP1BUF tamponu yalnızca SSP1OV biti temizken güncellenir.
bit 3 SDAHT: SDA tutma zamanı seçim biti (yalnızca I2C modu)
1 = SCL’nin düşen kenarı sonrası minimum 300 ns’lik SDA’da tutma süresi
0 = SCL’nin düşen kenarı sonrası minimum 100 ns’lik SDA’da tutma süresi
bit 2 SBCDE: Slave modu veri yolu çarpışma algılaması etkinleştirme biti (yalnızca I2C Slave modu)
Eğer SCL’nin yükselen kenarında, modül bir yüksek durum çıkışı veriyorken SDA düşük seviyede örneklenirse, PIR2 kaydedicisinin BCL1IF biti kurulur ve veri yolu boşa çıkartılır.
1 = Slave veri yolu çarpışma kesmelerini etkinleştirir.
0 = Slave veri yolu çarpışma kesmeleri kapatılır.
bit 1 AHEN: Adres tutma etkinleştirme biti (yalnızca I2C Slave modu)
1 = Bir eşleşen alınan adres baytı için SCL’nin 8’inci düşen kenarını takiben; SSP1CON1 kaydedicisinin CKP biti temizlenir ve SCL sinyali düşük seviyede tutulur.
0 = Adres tutma kapalı
bit 0 DHEN: Veri tutma etkinleştirme biti (yalnızca I2C Slave modu)
1 = Bir alınan veri baytı için SCL’nin 8’inci düşen kenarını takiben; bağımlı donanım SSP1CON1 kaydedicisinin CKP bitini temizler ve SCL sinyali düşük seviyede tutulur.
0 = Veri tutma kapalı
Not
1: Papatya-zinciri SPI çalışması için; kullanıcının son alınan bayt hariç tümünü yok saymasına izin verir. Yeni bir bayt alındığında SSP1OV hala kuruludur ve BF = 1’dir, fakat donanım SSP1BUF tamponuna en son baytı yazmaya devam eder.
2: Bu bit, Başlama ve Durma koşulu algılamasının açıkça etkinleştirildiği Slave modlarında etkili değildir.
3: ACKTIM durum biti yalnızca AHEN ya da DHEN biti kuruluyken aktifdir.
SSP1MSK: SSP1 MASK KAYDEDİCİSİ (ADRES: 213h – PIC12F1840, 93h – PIC16F887)
bit 7-1 MSK<7:1>: Maskeleme bitleri
1 = Alınan n adres biti, I2C adres eşleşmesini tespit etmek için SSP1ADD ile karşılaştırılır.
0 = Alınan n adres biti I2C adres eşleşmesini tespit etmek için kullanılmaz.
bit 0 MSK<0>: I2C Slave modu için maskeleme biti, 10-bit adres
I2C Slave modu, 10-bit adres (SSP1M<3:0> = 0111 or 1111):
1 = Alınan 0 adres biti, I2C adres eşleşmesini tespit etmek için SSP1ADD<0> ile karşılaştırılır.
0 = Alınan 0 adres biti I2C adres eşleşmesini tespit etmek için kullanılmaz.
I2C Slave modu, 7-bit adres için bu bit dikkate alınmaz.
SSP1ADD: MSSP1 ADRES VE BAUD HIZI KAYDEDİCİSİ (ADRES: 212h – PIC12F1840, 93h – PIC16F887)
Master modu:
bit 7-0 ADD<7:0>:Baud hızı saat darbesi bölücüsü bitleri
SCL pini saat darbesi periyodu = ((ADD<7:0> + 1) *4)/FOSC
10-Bit Slave modu — En Büyük Değerlikli Adres baytı:
bit 7-3 Kullanılmaz: En Büyük Değerlikli Adres baytı için kullanılmaz. Bu kaydedicinin bit durumu “don’t care – dikkate almayın” durumundadır. Master aygıt tarafından gönderilen bit deseni I2C tanımlamasıyla düzeltilir ve ‘11110’ değerine eşitlenmelidir. Bununla birlikte, bu bitler donanım tarafından karşılaştırılır ve bu kaydedicinin değerinden etkilenmez.
bit 2-1 ADD<2:1>: 10-bit adresin İki En Büyük Değerlikli bitleridir.
bit 0 Kullanılmaz: Bu modda kullanılmaz. Bit durumu “don’t care – dikkate almayın” durumundadır.
10-Bit Slave modu — En Düşük Değerlikli Adres baytı:
bit 7-0 ADD<7:0>: 10 bit adresin sekiz en düşük değerlikli bitidir.
7-Bit Slave modu:
bit 7-1 ADD<7:1>: 7-bit adres
bit 0 Kullanılmaz: Bu modda kullanılmaz. Bit durumu “don’t care – dikkate almayın” durumundadır.
I2C Modu
Philips Semiconductor firmasınca öncelikli olarak TV ünitelerinin içinde kullanılan aygıtların haberleşmesi için geliştirilmiş bir iletişim protokolüdür. I2C iletişim veri yolu I2C aygıtlarının birbirleriyle haberleşmesini sağlayan 2-hatlı bir haberleşme kanalıdır. Bu iletişim modu ile “full master” ve “slave” modunda yalnızca tek yönlü iletişim yapılır. İletişimi gerçekleştirmek için ilgili mikrodenetleyicinin aşağıdaki iki pini kullanılır. DS1307 saat ve tarih takvim entegresi, 24C02 EEPROM, PCF8574/A port genişleticisi gibi donanımlar bu haberleşme modunu kullanırlar. Farklı mikrodenetleyicilerin birbirleriyle haberleştirilmesinde de bu protokolden yararlanılabilir.
- • Serial Data (SDA) – RC4/SDI/SDA : Seri veri iletimi ve alımı
- • Serial Clock (SCL) – RC3/SCK/SCL : Seri veri iletişimi için gerekli saat darbesi
SDA/SCL isimleri I2C modu için kullanılan kısaltmalardır.
Veri (SDA) ve saat darbesi (SCL) hatlarının çıkışları open-drain olduğundan pull-up dirençleriyle besleme hattına bağlanması gerekir. Kullanılacak direnç değeri 1,8k – 47k arasında veri hattının uzunluğuna bağlı olarak değişir. Aynı hat üzerine birbirinden farklı cihazlar bağlanabilir (dijital tarih entegresi, port genişletici, sıcaklık sensörü, bellek, vb.) Hat meşgulken yeni bir veri gönderimi söz konusu değildir. Veri gönderen aygıt o an için master-sahip konumundadır. Diğer aygıtlar slave-bağımlı konumuna geçer. Veri aynı hat üzerinden tüm aygıtlara gönderilir ancak yalnızca ilgili aygıt veriyi alır. Verinin hangi aygıta ait olduğunu bildiren köle adresinin veri gönderilmeden önce iletilmesi gerekir. Şekil 9.21’de gerçek zaman saati (Real Time Clock - RTC) entegresi, EEPROM entegresi ve iki adet PCF8574 port genişleticisi gibi farklı amaçlara sahip cihazların aynı hat üzerine bağlantısı gösterilmiştir.
Şekil 9.21 Farklı I2C™ aygıtlarının aynı SCL, SDA hattına bağlantısı
MikroC programının I2C iletişim özelliğine sahip donanımlarla hızlı şekilde haberleşme yapılabilmesini sağlayan I2C kütüphanesi mevcuttur.
I2C1_Init : I²C modülünü arzu edilen frekansta çalışmaya hazırlar. Diğer rutinler işletilmeden önce bu rutinin çağrılması gerekir. İletişim için gerekli frekans değeri belirlenirken mikrodenetleyicinin SSPADD kaydedicisinin ilk 7 bitine yazılan değer kullanılır. Hesaplamada aşağıdaki ilişkiye dikkat edilmelidir:
Saat frekansı = FOSC/4*(SSPADD + 1)
Kullanılışı : I2C1_Init(100000);
Açıklama : I2C1 modülünü 100 kHz frekansta hazırlar.
I2C1_Start : Veri alma ya da veri gönderme prosesi başlatılmadan önce kullanılır.
Kullanılışı : I2C1_Start();
Açıklama : Veri yolunun boş olduğunu belirledikten sonra START sinyalini başlatır.
I2C1_Repeated_Start : Veri alma ya da gönderme prosesi başlatılmadan önce kullanılır.
Kullanılışı : I2C1_Repeated_Start();
Açıklama : Veri yolu boşalana kadar tekrarlı START sinyali verir.
I2C1_Is_Idle : Veri yolu boşsa ‘1’, diğer durumda ‘0’ gönderir.
Kullanılışı : if (I2C1_Is_Idle()) {...}
Açıklama : I2C1 veri yolu boşsa küme parantezi içindeki işlemler yürütülür.
I2C1_Rd : Slave aygıttan 1 byte bilgi okur. “ack” parametresiyle birlikte kullanılır. Eğer 0’sa onay yok (not acknowledge), 1’se onay (acknowledge) sinyali gönderir.
unsigned short take;
Kullanılışı : take = I2C1_Rd(0);
Açıklama : “take” isimli değişkene bilgi alınır ve onay biti gönderilmez.
I2C1_Wr : I2C1 yolundan 1 byte’lık bilgi gönderilir.
unsigned short take;
Kullanılışı : I2C1_Write(0xA3);
Açıklama : Veri yoluna 0xA3 hekzadesimal verisi gönderilir.
I2C1_Stop : Veri alma ya da gönderme prosesi sonrası kullanılır.
unsigned short take;
Kullanılışı : I2C1_Stop();
Açıklama : STOP sinyali işletilir ve I2C1 veri yolu boşa çıkartılır.
Şekil 9.22’de I2C iletişiminin sinyalizasyonu özet olarak gösterilmiştir. Farklı iletişim modları hakkında daha ayrıntılı bilgi için veri kılavuzunu incelemeniz tavsiye edilir.
Şekil 9.22 I2C adres ve veri gönderme sinyalizasyonu
Şekilde gösterilen sinyalizasyonda eşzamanlı Master-to-Slave seri iletişimi gerçekleştirilmektedir. Cihazların senkronizasyonunu SCL sinyali sağlamaktadır. Mikrodenetleyici tarafından frekansı belirlenen saat darbesinin iletim hattına gönderilmesiyle birlikte Master ve Slave aygıtlar bu saat darbesi ile senkronize edilirler. Her veri değişimi Master tarafından başlatılır. MSSP modülü etkinleştirildiğinde Start koşulunun oluşması beklenir. Master aygıt ilk önce START bitini (lojik-0) SDA pini üzerinden gönderir. Ardından seçili “slave” aygıtının 7-bitlik ya da 10-bitlik adresi gönderilir. Adres paketinin ardından R/W ̅ biti gönderilir. Bu bit lojik-1 olduğunda Master aygıtın Slave aygıttan okuma yapacağı, lojik-0 olduğunda ise Slave aygıta yazma yapacağı anlaşılır.
Eğer adresi gönderilen Slave aygıt iletişim hattında mevcutsa, “slave” aygıt tarafından bir bitlik ACK (Acknowledgment – Onay) sinyali gönderilir. ACK sinyalinin değeri MSSP1 modülünün SSP1CON2 kaydedicisinin ACKSTAT bitine yazılır. ACK sinyalinin lojik-0 olması adresin onaylandığı anlamına gelir. SCL’nin 9’uncu düşen kenarında SSP1IF kesme sinyali üretilir (yazlım içinde temizlenmelidir). Kesme sinyali sonrası veri baytı gönderilir. Veri gönderimi bittiğinde ACK sinyali lojik-1 olur ve Master aygıtın ACKSTAT biti lojik 1 seviyesine çekilir. Bilgi gönderenin “slave” aygıt olması durumunda “master” aygıttan ACK sinyali beklenmez.
SCL hattı lojik-1 seviyesindeyken, SDA sinyalinin lojik-0’dan lojik-1’e geçişi STOP (Durma) komutu anlamına gelir ve iletişim sonlandırılır. I2C iletişiminde adres/veri bitlerinin geçişi SCL hattı lojik-0 seviyesindeyken gerçekleşir. SCL hattı lojik 1 seviyesinde Start/Stop koşullarını başlatmak için durur.
Bazı mikrodenetleyicilerde (bazı PIC18 ve 24 serisi, PIC32 serisi) birden fazla I2C modülü bulunur. Bu modüller numaralandırılarak gösterilir. Kütüphanede yer alan numarasız I2C komutları jenerik koddur ve eğer mikrodenetleyicide o an için hangi modül varsayılan I2C modülü olarak ayarlanmışsa onun için kullanılır. Dolayısıyla birden fazla I2C modülü olan mikrodenetleyicilerde jenerik kütüphaneyi kullanmadan önce varsayılan modül ayarının yapılmış olması gerekir. Bunun için ilgili mikrodenetleyicinin veri kılavuzuna bakmanız önerilir.
PIC18F K42 ve K83 ailesi mikrodenetleyicilerde diğer 8-bit mikrodenetleyicilerden farklı olarak yeniden adreslenebilir I2C modülü (I2Cx_Remappable) bulunmaktadır. Böylece I2C modülünün hatları arzu edilen bir I/O piniyle irtibatlandırılabilir.
I2C iletişimi karmaşık bir seri iletişimdir. Bu protokole özgün olarak kullanılan bazı terimler vardır. Terimlerin İngilizce orijinal isimleri, karşılıkları ve açıklamaları Tablo 9.16’da sunulmuştur.
Tablo 9.16 I2C iletişimi terminolojisi
Terim | Türkçe Karşılığı | Açıklaması |
Transmitter | Verici | Veri yoluna bilgi paketini gönderen aygıt. |
Receiver | Alıcı | Veri yolundan bilgi paketini alan aygıt. |
Master | Sahip | Transferi başlatan, bitiren ve saat darbesini üreten aygıt. |
Slave | Bağımlı | Master tarafından adresi bildirilen ve iletişim kurulan aygıt. |
Multi-master | Çoklu-sahip | Birden fazla aygıtın iletişim başlattığı durum. |
Arbitration | Tahkim | Bir anda yalnızca tek bir sahip aygıtın iletişim kurmasını sağlayan bir karar mekaniz-masıdır. Hangi sahip aygıt iletişim yapmaya öncelik kazanırsa hat ona teslim edilir. Böylece her durumda hatta iletişim temin edilmiş olur. |
Synchronization | Eşzamanlılık | Veri yolundaki iki ya da daha fazla aygıtın aynı saat hızında çalışmasının sağlanmasıdır. |
Idle | Boş | Hat herhangi bir sahip aygıt tarafından kontrol edilme-mektedir. Bu durumda SDA ve SCL hatları lojik-1 seviyesindedir. |
Active | Aktif | Herhangi bir anda bir ya da daha fazla aygıtın veri yolunu kontrol etmesidir. |
Addressed Slave | Adresli Bağımlı | Eşleşen adrese sahip bağımlı aygıtı ifade eder. Eşleşme sonrası sahip ve bağımlı arasında bağlantı kurulur. |
Matching Address | Eşleşen Adres | SSP1ADD kaydedicisinde yer alan adres değeriyle eşleşen bağımlı aygıta gönderilen adres değerini ifade eder. |
Write Request | Yazma Talebi | Bağımlı aygıt, eşleşen adresi lojik-0 değerli R/W ̅ bitiyle birlikte alır ve veri Slave aygıta yazılır. |
Read Request | Okuma Talebi | Sahip aygıt lojik-1 değerli R/W ̅ bitini adres paketiyle birlikte gönderir ve Slave aygıttan okuma yapmaya hazır olduğunu bildirir. Restart işlemi ya da Stop biti gönderilene kadar sonraki baytlar veri olarak okunmaya devam eder. |
Clock Stretching | Saat Darbesini Uzatma | Özellikle bağımlı aygıtın, sahip aygıtın veri gönderme hızına yetişememesi durumunda, veri alımını garanti etmek için SCL saat darbesi hattını lojik-0’da tutması ve iletişimi bekletme-sidir. |
Bus Collision | Veri Yolu Çarpışması | SDA hattının lojik-1 olmasının beklendiği anda modül tarafından lojik 0 olarak okunmasıdır. |
I2C Donanımının Avantajları ve Dezavantajları
I2C iletişiminin diğer iletişim protokollerine ve yöntemlerine göre kullanıcıya sağladığı avantajları şu şekilde sıralayabiliriz:
-
SPI iletişiminden daha yavaş olmakla birlikte asenkron seri iletişimden daha hızlıdır. Veri yolu farklı hızlarda çalışabilir. Hat üzerindeki daha yavaş aygıta göre hız ayarlanabilir. Yaygın veri iletişim hızı standart modda 100 Kbs (saniye başına kilo bit)’dir. Hızlı modda 400 Kbs, yüksek hız modunda 1 Mbs hızında çalışabilir. Daha yeni versiyonlarda 3,4 Mbs hızına çıkılmıştır.
-
SPI iletişimine göre I2C’nin en büyük avantajı ihtiyaç duyulan pin sayısıdır. SPI iletişiminde bir “master” aygıtıyla bir “slave” aygıtın çift yönlü iletişimi için en az 4 hatta ihtiyaç vardır. Her bir slave aygıt ek bir çip seçme hattı gerektirir. Aygıt sayısı arttıkça bağlantı karmaşıklaşır. I2C iletişiminde ise tüm aygıtlar aynı iki hat üzerine bağlanır. I2C iletişim hattı 1024 bağımlı aygıtı destekler (16 aygıt adresi rezerve edildiğinden bu sayı gerçekte 1008’dir. Ancak pratikte bu kadar aygıtın birbirine bağlanması çeşitli elektriksel engeller nedeniyle pek mümkün değildir).
-
I2C iletişim hattı SPI’dan farklı olarak birden fazla “master” aygıtın tüm aygıtlarla haberleşmesini sağlar. Ancak iki “master” aygıtın birbiriyle haberleşmesi söz konusu değildir. Hattı sırayla kullanmaları gerekir.
-
Adres bilgisi de seri olarak iletildiğinden çok sayıda aygıtın aynı hatta bağlantısında bir sıkıntı yoktur. Dikkat edilmesi gereken adres çakışmasının olmamasıdır.
-
I2C iletişim protokolünün en önemli özelliklerinden birisi bağımlı (Slave) aygıttan ACK sinyalinin beklenmesidir. Adres ve Veri baytları gönderildikten sonra SCL sinyalinin 8’inci düşen kenarında bağımlı aygıttan ACK sinyali göndermesi beklenir. Böylece gerçekten bir donanım ile iletişim kurulduğu teyit edilmiş olur.
Sağladığı bu avantajlar yanında I2C iletişiminin kusurları da bulunmaktadır:
-
Asenkron USART ve senkron SPI seri iletişim protokollerine göre daha karmaşıktır.
-
İletişim hatlarının open-drain topolojisi nedeniyle diğer seri haberleşme yollarından daha fazla güç tüketimi olur.
-
Aygıtlar haberleşme hızlarını ayarlayabildiklerinden dolayı, yavaş aygıtlar daha hızlı aygıtların hızını keser.
-
Paylaşımlı doğası nedeniyle, veri yolundaki tek bir aygıtın çalışmasını durdurması tüm veri yolunun askıda kalmasına neden olabilir. Bu durumda Restart sinyaliyle veri yolu tekrar başlatılmalıdır.
-
I2C veri yolunun en büyük kusuru kısa mesafeli iletişime uygun olmasıdır. Bu protokolü geliştirenler, daha çok aynı PCB üzerindeki aygıtların birbirleriyle haberleşmelerini sağlamak istemişlerdir. RS-232, , RS-485 ve CAN-bus gibi uzun mesafeli haberleşme sistemlerine kıyasla oldukça kısa bir kablolama için uygundur.
I2C Sürücüsüyle Alfanümerik LCD Ekran Kontrolü
I2C iletişimi LCD sürücülerle yaygın olarak kullanılmaktadır. LCD’yi doğrudan mikrodenetleyiciye bağlamaya göre çok büyük avantajları vardır. Pin sayısı az mikrodenetleyicilerle yalnızca 2 pin kullanarak LCD sürmeniz mümkündür.
Şekil 9.23’te MSSP modülüne sahip PIC12F1840 ile I2C iletişim moduyla 4 satırlı LM044L serisi LCD (HD44780) ekranın sürülmesi uygulaması yapılmıştır. Bunun için PCF8574 I2C yol genişleticisi kullanılmıştır. Bu entegrenin özelliği, bünyesinde bulunan kaydırmalı kaydedici yardımıyla, seri olarak aldığı veriyi paralel portlara dağıtmasıdır. Böylece veri için 4 MSB biti ve kontrol için de 2 bit olmak üzere toplamda en az 6 bitlik dijital kontrole ihtiyaç duyan bir HD44780 LCD ekran, yalnızca 2 bitlik SCL/SDA I2C seri iletişim kanalından kontrol edilebilir.
Şekil 9.23 I2C iletişim moduyla alfanümerik LCD ekranın sürülmesi
Bu uygulamada mikroC program editörünün dâhili LCD kütüphanesi kullanılmamıştır. HD44780 serisi LCD için veri kılavuzu kullanılarak kullanıcı tanımlı veri yazdırma ve LCD kumanda fonksiyonları oluşturulmuştur. Bu nedenle LCD kütüphanesinin seçilmesine gerek yoktur. Ancak uygulamada mikroC program editörünün “I2C” kütüphanesinden yararlanıldığı için “Library Manager” penceresinden “I2C” kütüphanesi seçilmelidir.
Tablo 9.17’deki kod incelendiğinde, çok sayıda onaltılık sayı değerinin olduğu ve bunlara çeşitli isimlerle tanımlama yapıldığı görülmektedir. Bu onaltılık 1 baytlık sayılar; HD44780 serisi 16x2, 20x2 ve 20x4 alfanümerik LCD için yanlarında açıklamaları verilmiş olan komut kodlarıdır. Daha ayrıntılı bilgiye HD44780 LCD veri kılavuzundan ulaşılabilir (Bkz. Ek-I5).
Uygulamada LCD ekran 4 bit veri gönderme formatında kullanılmıştır. Bu nedenle 1 baytlık veri iki nibble parçasına bölünmektedir. Önce 1 baytlık bilginin yüksek kısmı ardından düşük kısmı gönderilmektedir. Veri yazma işlemleri arasında 50us’lik beklemeler yapılmasının nedeni, LCD’nin komutu tamamlaması için gerekli minimum süre bandının sağlanmasıdır. Uygulama devresinde PCF8574 port genişleticisi entegresinin adres hatlarını oluşturan A0, A1 ve A2 pinleri VDD kaynağına bağlanmıştır. Bu nedenle veri yazma modunda entegreyi seçecek adres bilgisi olarak “4Eh” verisi kullanılmaktadır (Bkz. Ek-I4).
Proje editörü penceresinde FOSC olarak dâhili osilatör kaynağı INTOSC modu seçilmiştir. Böylece OSC1 ve OSC2 pinleri normal I/O pini olarak boşa çıkartılmıştır. Dâhili osilatör seçildiğinde CONFIG1 yapılandırma sözcüğünün ilk üç biti FOSC<2:0>=”100” olur. Ardından OSCCON osilatör kontrol kaydedicisinin IRCF<3:0> bitleriyle 15 farklı osilatör frekansı seçilebilir. PIC12F1840 mikrodenetleyicisinde dâhili osilatör kaynağı INTOSC modu seçildiğinde varsayılan olarak IRFC = 0111 (500 kHZ) değerine kurulur. Gerçekten de yazılım içinde OSCCON kaydedicisine değer yüklenen satırı kapattığınızda LCD ekranda çalışma frekansı olarak 500 kHz değeri gözükecektir.
Tablo 9.17 I2C iletişim moduyla alfanümerik LCD ekranın sürülmesi mikroC kodu
// LCD Tanımlamaları
#define _LCD_TEMIZLE 0x01 // Ekranı temizle
#define _LCD_BASLANGIC 0x02 // Kursörü başlangıç noktasına getir
#define _LCD_KAPAT 0x08 // Ekranı kapat
#define _LCD_AC 0x0C // Ekranı aç - kursör kapalı
#define _LCD_CURSOR_ON 0x0E // Ekranı aç - kursör de açık
#define _LCD_BLINK_CURSOR_ON 0x0F // Kursörü yakıp-söndür
#define _LCD_MOVE_CURSOR_LEFT 0x10 // Kursörü görüntü veri RAM'ini değiştirmeden sola kaydır
#define _LCD_MOVE_CURSOR_RIGHT 0x14 // Kursörü görüntü veri RAM'ini değiştirmeden sağa kaydır
#define _LCD_SHIFT_LEFT 0x18 // Görüntü veri RAM'ini değiştirmeden görüntüyü sola kaydır
#define _LCD_SHIFT_RIGHT 0x1E // Görüntü veri RAM'ini değiştirmeden görüntüyü sağa kaydır
#define _LCD_FIRST_ROW 0x80 // Kursörü 1'nci satıra getir
#define _LCD_SECOND_ROW 0xC0 // Kursörü 2'nci satıra getir
#define _LCD_THIRD_ROW 0x94 // Kursörü 3'nci satıra getir
#define _LCD_FOURTH_ROW 0xD4 // Kursörü 4'nci satıra getir
#define _LCD_4BIT_HAZIRLA 0X20 // LCD 4 bit modunda hazırlanıyor ve RS/E bitleri lojik-0
#define _LCD_4BIT_FORMAT 0X28 // LCD 4 bit 2 satır 5x7 dotmatriks formatında kuruluyor
#define _PCF8574_ADDR 0x4E // A0-2 pinleri lojik-1(VDD'ye bağlıyken) olan PCF8574 yazma adresi
// Lcd sabitleri
char txt1[] = "I2C LCD";
char txt2[] = "16x2, 20x2, 20x4";
char txt3[] = "PIC12F1840";
char txt4[9] = " ";
unsigned short osilator;
void I2C_LCD_Cmd(char out_char) {
char hi_n, lo_n; //high nibble ve low nibble verileri
// 4bitlik modda önce high sonra low nibble değerleri gönderildiği için
// 1 byte'lık bilginin yüksek ve alçak 4bit kısımları ayıklanıyor
hi_n = out_char & 0xF0; // 1 Byte'ın yüksek kısmı alınıyor
lo_n = (out_char << 4) & 0xF0; // 1 Byte'ın alçak kısmı alınıyor
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(_PCF8574_ADDR);
I2C1_Is_Idle();
I2C1_Wr(hi_n | _LCD_AC);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(hi_n | _LCD_KAPAT);
I2C1_Is_Idle();
Delay_us(100);
I2C1_Wr(lo_n | _LCD_AC);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(lo_n | _LCD_KAPAT);
I2C1_Is_Idle();
I2C1_stop();
if(out_char == 0x01)Delay_ms(2);
}
void I2C_LCD_Chr(char row, char column, char out_char) {
char hi_n, lo_n;
switch(row){
case 1:
I2C_LCD_Cmd(_LCD_FIRST_ROW + (column - 1));
break;
case 2:
I2C_LCD_Cmd(_LCD_SECOND_ROW + (column - 1));
break;
case 3:
I2C_LCD_Cmd(_LCD_THIRD_ROW + (column - 1));
break;
case 4:
I2C_LCD_Cmd(_LCD_FOURTH_ROW + (column - 1));
break;
};
hi_n = out_char & 0xF0;
lo_n = (out_char << 4) & 0xF0;
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(_PCF8574_ADDR);
I2C1_Is_Idle();
I2C1_Wr(hi_n | _LCD_TEMIZLE | _LCD_AC);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(hi_n | _LCD_TEMIZLE | _LCD_KAPAT);
I2C1_Is_Idle();
Delay_us(100);
I2C1_Wr(lo_n | _LCD_TEMIZLE | _LCD_AC);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(lo_n | _LCD_TEMIZLE | _LCD_KAPAT);
I2C1_Is_Idle();
I2C1_stop();
}
void I2C_LCD_Chr_Cp(char out_char) {
char hi_n, lo_n;
hi_n = out_char & 0xF0;
lo_n = (out_char << 4) & 0xF0;
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(_PCF8574_ADDR);
I2C1_Is_Idle();
I2C1_Wr(hi_n | _LCD_TEMIZLE | _LCD_AC);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(hi_n | _LCD_TEMIZLE| _LCD_KAPAT);
I2C1_Is_Idle();
Delay_us(100);
I2C1_Wr(lo_n | _LCD_TEMIZLE | _LCD_AC);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(lo_n | _LCD_TEMIZLE | _LCD_KAPAT);
I2C1_Is_Idle();
I2C1_stop();
}
void I2C_LCD_Init() {
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(_PCF8574_ADDR);
I2C1_Is_Idle();
// 4 bitlik veri transfer moduna göre LCD ayarlanıyor
Delay_ms(10);
I2C1_Wr(_LCD_4BIT_HAZIRLA);
I2C1_Is_Idle();
I2C1_Stop();
Delay_ms(10);
I2C_LCD_Cmd(_LCD_4BIT_FORMAT);
}
void I2C_LCD_Out(char row, char col, char *text) {
while(*text)
I2C_LCD_Chr(row, col++, *text++);
}
void I2C_LCD_Out_Cp(char *text) {
while(*text)
I2C_LCD_Chr_Cp(*text++);
}
void main() {
ANSELA = 0x00;
TRISA = 0x00;
PORTA = 0x00;
LATA = 0x00;
/*
------ Osilatörün ayarlanması -------
Proje editöründe PLL kapalıysa (CONFIG1 PLLEN=0 ise),
dâhili osilatör frekansı 31kHz-16MHz arası ayarlanır.
PLLEN = 1 ise ya da SPLLEN = 1 yapılırsa 8 MHz dâhili
osilatör modunda x4 etkeniyle 32 MHz elde edilir.
Eğer sistemde iki osilatör kaynağı kullanılacaksa
o zaman OSCCON kaydedicisinin SCS<1:0> bitleri '10'
olarak ayarlanır. Böylece yazılımsal olarak iki kaynak arasında
geçiş yapılabilir. Eğer doğrudan sadece dâhili osilatör
kaynağı kullanılacak ve OSC1-OSC2 pinleri boşa çıkartılacaksa
CONFIG1 sözcüğünün FOSC<2:0> bitleri '100' yapılır (INTOSC).
Ardından OSCCON kaydedicisinin SCS<1:0> bitleri '00' olarak
ayarlanır.
*/
OSCCON = 0b01101000; //Şu anda 4MHz'e ayarlı
osilator = OSCCON;
osilator = (osilator >> 3) & 0x0F;
switch (osilator){
case 0: strcpy(txt4,"31 kHz");break;
case 1: strcpy(txt4,"31 kHz");break;
case 2: strcpy(txt4,"31.25 kHz");break;
case 3: strcpy(txt4,"31.25 kHz");break;
case 4: strcpy(txt4,"62.5 kHz");break;
case 5: strcpy(txt4,"125 kHz");break;
case 6: strcpy(txt4,"250 kHz");break;
case 7: strcpy(txt4,"500 kHz");break;
case 8: strcpy(txt4,"125 kHz");break;
case 9: strcpy(txt4,"250 kHz");break;
case 10: strcpy(txt4,"500 kHz");break;
case 11: strcpy(txt4,"1 MHz");break;
case 12: strcpy(txt4,"2 MHz");break;
case 13: strcpy(txt4,"4 MHz");break;
case 14: strcpy(txt4,"8 MHz");break;
case 15: strcpy(txt4,"16 MHz");break;
}
I2C1_Init(100000); // I2C Modülü 100kHz frekansta hazırlanıyor
I2C_LCD_Init(); //Kullanıcı tanımlı LCD fonksiyonu ile LCD açılıyor
I2C_LCD_Cmd(_LCD_AC);
I2C_LCD_Cmd(_LCD_TEMIZLE);
I2C_Lcd_Out(1,1,txt1); // Birinci satıra yaz
I2C_Lcd_Out(2,1,txt2); // İkinci satıra yaz
I2C_Lcd_Out(3,1,txt3); // Üçüncü satıra yaz
I2C_Lcd_Out(4,1,txt4); // Dördüncü satıra yaz
while(1) { //Sonsuz döngü
}
}
Piyasada hazır PCF8574 I2C port genişletme kiti bulunmaktadır. Böyle bir kit Şekil 9.24’te gösterilmiştir. Kitin üzerinde A0-A1-A2 donanımsal adres bacaklarını yapılandırmanızı sağlayan bir kademeli anahtar bulunmaktadır.
Şekil 9.24 I2C port genişletme kiti
I2C Sürücüsüyle Port Çoklama ve Kullanıcı Tanımlı I2C Kütüphanesi Tasarımı
I2C port genişleticileri kullanılarak mikrodenetleyicinizin bacak sayısının yetmediği uygulamalar için port çoklama yapabilirsiniz. Şekil 9.25’de yalnızca 6 I/O pini bulunan PIC12F1840 kullanılarak 2 adet 20x4 HD44780U sürücülü LCD ekranın kumanda uygulaması gösterilmiştir. Bu uygulama için PCF8574 entegresinden 2 adet kullanılmış olup, yalnızca SCL ve SDA pinleri kullanılarak toplamda 14 pinli bir cihaz kumandası gerçekleştirilmiştir.
Bir önceki başlıkta gösterilen uygulamada mikroC programının dâhili I2C kütüphanesi kullanılmıştır. I2C protokolünün çalışma ilkesine hâkim olunması durumunda kütüphane kullanmadan kendi iletişim algoritmanızı oluşturabilirsiniz. Mevcut uygulamada mikroC geliştirme arayüzünün hiçbir kütüphanesi kullanılmamıştır. Proje dosyası üç farklı “.c” dosyasından oluşmaktadır. “#include” tanımlayıcısıyla diğer dosyalar çağrılmaktadır. Projede yer alan dosyalar şu şekildedir:
-
Multiple_I2C_PIC12F1840.c
-
I2C_ozel.c
-
LCD_ozel.c
Şekil 9.25 Birden fazla I2C port genişleticisiyle cihaz kumandası
Tablo 9.18’de yer alan uygulama kodu projenin ana kaynak dosyasıdır. Ana dosyaya bağlantısı yapılacak diğer “.c” ya da “.h” dosyalarının not pad gibi bir metin düzenleyicisine alındıktan sonra proje klasörünün içine kaydedilmesi, bağlantı ve dosya yolu sorunlarını ortadan kaldıracaktır. Mevcut uygulamada LCD ekrana özel karakter yazılması da gösterilmiştir (Ayrıntılı bilgi için bkz. Ek-I5).
Tablo 9.18 I2C port genişleticisiyle çoklu cihaz kumandası "Multiple_I2C_PIC12F1840.c" mikroC kodu
#include "LCD_ozel.c"
char msg1[] = "I2C LCD 4 Bit";
char msg2[] = "PIC12F1840 4 MHz";
char msg3[] = "MikroC, EasyPIC v7";
char msg4[] = "LCD adresi 0x07";
char msg5[] = "LCD adresi 0x06";
char const msg6[] = "zel Karakter Testi";
char character[] = {0,10,21,17,10,4,0,0};//kullanıcı tanımlı kalp karakteri
char buyuk_o[] = {10,0,14,17,17,17,14,0};//kullanıcı tanımlı Ö harfi
void main() {
ANSELA = 0x00;
TRISA = 0x00;
PORTA = 0x00;
LATA = 0x00;
OSCCON = 0b01101000;
I2C_Init();
I2C_LCD_Init(Waddr1);
I2C_LCD_Cmd(Waddr1,_LCD_CURSOR_OFF);
I2C_LCD_Cmd(Waddr1,_LCD_CLEAR);
I2C_LCD_Init(Waddr2);
I2C_LCD_Cmd(Waddr2,_LCD_CURSOR_OFF);
I2C_LCD_Cmd(Waddr2,_LCD_CLEAR);
I2C_LCD_Out(Waddr1,1,1,msg1);
I2C_LCD_Out(Waddr1,2,1,msg2);
I2C_LCD_Out(Waddr1,3,1,msg3);
I2C_LCD_Out(Waddr1,4,1,msg4);
I2C_LCD_Out(Waddr2,1,1,msg1);
I2C_LCD_Out(Waddr2,2,1,msg2);
I2C_LCD_Out(Waddr2,3,1,msg3);
I2C_LCD_Out(Waddr2,4,1,msg5);
delay_ms(5000);
I2C_LCD_Cmd(Waddr1, _LCD_CLEAR);
CustomChar(Waddr1,1,1,buyuk_o, 64, 0);
I2C_LCD_Out(Waddr1,1,2,msg6);
I2C_LCD_Out(Waddr1,2,1,"Birinci LCD ekran");
CustomChar(Waddr1,3,10, character, 72, 1);
I2C_LCD_Cmd(Waddr2, _LCD_CLEAR);
CustomChar(Waddr2,1,1,buyuk_o, 64, 0);
I2C_LCD_Out(Waddr2,1,2,msg6);
I2C_LCD_Out(Waddr2,2,1,"Ikinci LCD ekran");
CustomChar(Waddr2,4,5, character, 72, 1);
while(1){
//TODO: kullanıcı kodu
}
}
Tablo 9.19’da kullanıcı tanımlı I2C kütüphanesinin oluşturulması gösterilmiştir. Dosya dikkatlice incelendiğinde mikroC’nin kütüphanesinde yer alan komutların çoğunun bulunduğu görülecektir. Görüldüğü gibi MSSP modülünün ilgili kaydedicileri ve onlara ait bitler kullanılarak ihtiyaç duyulan iletişim fonksiyonları oluşturulmuştur. SSP1CON1 kaydedicisiyle MSSP modülü I2C moduna alınmıştır. SSP1ADD kaydedicisiyle çalıştığınız mikrodenetleyicinin frekansına bağlı olarak iletişim hızının ayarlanması gerçekleştirilmiştir
I2Cx_Is_Idle() kütüphane fonksiyonunun işlevi I2C_Bosta() fonksiyonuyla gerçekleştirilmiştir. SSP1CON2 kaydedicisi 0x1F değerine ya da SSPSTAT kaydedicisi 0x04 değerine kurulu olduğu sürece hat üzerinde iletişim olduğu anlaşılır. Dolayısıyla tüm okuma ve yazma işlemlerinde bu fonksiyon kullanılır.
Tablo 9.19 I2C port genişleticisiyle çoklu cihaz kumandası "I2C_ozel.c" mikroC kodu
//MSSP modülü I2C modunda ayarlanır (PIC12F1840 için).
//Kaydedici ayarları diğer mikrodenetleyiciler için değişebilir.
void I2C_init(void){
TRISA.F1=1; //SCL saat darbesi pini giriş olarak ayarlandı
TRISA.F2=1; //SDA seri data pini giriş olarak ayarlandı
SSP1CON1 = 0B00101000; //I2C master modunda etkinleştirildi
SSP1ADD = 0X09; // hız = FOSC/(SSP1ADD + 1)*4 = 100kHz
SSPSTAT = 0B11000000; //dönüş oranı iptal edildi
}
//I2C transferi bitene kadar bekler
void I2C_Bosta(void){
while ( ( SSP1CON2 & 0x1F ) || ( SSPSTAT & 0x04 ) );
}
//I2C iletişimini başlatır
void I2C_Basla(void){
I2C_Bosta();
SEN_bit=1;
}
//1 baytlık veri gönderilir
void I2C_Yaz(char veri){
I2C_Bosta();
SSPBUF = veri;
}
//I2C haberleşmesini durdurur
void I2C_Durdur(void){
I2C_Bosta();
PEN_bit=1;
}
// i2c_Restart - I2C haberleşmesini yeniden başlatır
void I2C_Restart(void){
I2C_Bosta();
RSEN_bit=1;
}
// i2c_Read - Slave aygıttan bir baytlık bilgi okur
char i2c_Read(char ack){
// Slave aygıttan bilgi oku
// okunacak daha fazla bilginin olması durumunda ack 1 olmalıdır
// okunan verinin son byte'ı ise ack 0 olmalıdır
char i2cReadData;
I2C_Bosta();
RCEN_bit=1;
I2C_Bosta();
i2cReadData = SSPBUF;
I2C_Bosta();
if (ack) ACKDT_bit=0; // Ack
else ACKDT_bit=1; // NAck
ACKEN_bit=1; // onay sırasını gönder
return( i2cReadData );
}
// i2c_Address - Slave aygıtın adresini ve Read/Write modunu gönderir
// mod I2C_WRITE ya da I2C_READ olabilir
void i2c_Address(char address, char mode){
char l_address;
l_address=address<<1;
l_address+=mode;
I2C_Bosta();
SSPBUF = l_address;
}
Tablo 9.20’de kullanıcı tanımlı LCD kütüphanesinin oluşturulması gösterilmiştir. Dikkat edileceği gibi kütüphane dosyasında yine kullanıcı tanımlı I2C kütüphanesi kullanılmıştır. Bu dosya yardımıyla kendi tasarladığınız ve bellek bölgesini kendinizin belirlediği 8 adet karakteri de kullanabilmenizi sağlayan özel karakter yazdırma fonksiyonu da bulunmaktadır.
Tablo 9.20 I2C port genişleticisiyle çoklu cihaz kumandası "LCD_ozel.c" mikroC kodu
#include "I2C_ozel.c"
#define _LCD_FIRST_ROW 0x80
#define _LCD_SECOND_ROW 0xC0
#define _LCD_THIRD_ROW 0x94
#define _LCD_FOURTH_ROW 0xD4
#define _LCD_CLEAR 0x01
#define _LCD_RETURN_HOME 0x02
#define _LCD_CURSOR_OFF 0x0C
#define _LCD_UNDERLINE_ON 0x0E
#define _LCD_BLINK_CURSOR_ON 0x0F
#define _LCD_MOVE_CURSOR_LEFT 0x10
#define _LCD_MOVE_CURSOR_RIGHT 0x14
#define _LCD_TURN_ON 0x0C
#define _LCD_TURN_OFF 0x08
#define _LCD_SHIFT_LEFT 0x18
#define _LCD_SHIFT_RIGHT 0x1E
#define EN_DELAY 100
#define Waddr1 0x4E
#define Waddr2 0x4C
void I2C_LCD_Cmd(char address, char out_char);
void I2C_LCD_Chr(char address, char row, char column, char out_char);
void I2C_LCD_Chr_Cp(char address, char out_char);
void I2C_LCD_Init(char address);
void I2C_LCD_Out(char address, char row, char col, char *text);
void I2C_LCD_Out_Cp(char address, char *text);
void CustomChar(char address, char pos_row, char pos_char);
/* LCD_I2C found with PCF8574
P7,P6,P5,P4 of PCF8574 = DB7,DB6,DB5,DB4 LCD ekranın MSB bitleridir
P3 LCD ekran ışığını kontrol eder (ArkaIşık : 1 = açık / 0 = kapalı)
P2 LCD E biti saat darbesidir : E = 1'den 0'a geçişte LCD'ye veri yazılır
P1 R/W biti Read/Write: Read = 1 / Write = 0
P0 RS LCD Register Select biti: CmdReg = 0 / DataReg = 1
*/
void I2C_LCD_Cmd(char address, char out_char) {
char hi_n, lo_n;
char rs = 0x00;
hi_n = out_char & 0xF0;
lo_n = (out_char << 4) & 0xF0;
I2C_Basla();
I2C_Yaz(address);
I2C_Yaz(hi_n | rs | 0x04 | 0x08);
delay_us(50);
I2C_Yaz(hi_n | rs | 0x00 | 0x08);
delay_us(100);
I2C_Yaz(lo_n | rs | 0x04 | 0x08);
delay_us(50);
I2C_Yaz(lo_n | rs | 0x00 | 0x08);
I2C_Durdur();
if(out_char == 0x01)Delay_ms(2);
}
void I2C_LCD_Chr(char address, char row, char column, char out_char) {
char hi_n, lo_n;
char rs = 0x01;
switch(row){
case 1:
I2C_LCD_Cmd(address, 0x80 + (column - 1));
break;
case 2:
I2C_LCD_Cmd(address, 0xC0 + (column - 1));
break;
case 3:
I2C_LCD_Cmd(address, 0x94 + (column - 1));
break;
case 4:
I2C_LCD_Cmd(address, 0xD4 + (column - 1));
break;
};
hi_n = out_char & 0xF0;
lo_n = (out_char << 4) & 0xF0;
I2C_Basla();
I2C_Yaz(address);
I2C_Yaz(hi_n | rs | 0x04 | 0x08);
delay_us(50);
I2C_Yaz(hi_n | rs | 0x00 | 0x08);
delay_us(100);
I2C_Yaz(lo_n | rs | 0x04 | 0x08);
delay_us(50);
I2C_Yaz(lo_n | rs | 0x00 | 0x08);
I2C_Durdur();
}
void I2C_LCD_Chr_Cp(char address, char out_char) {
char hi_n, lo_n;
char rs = 0x01;
hi_n = out_char & 0xF0;
lo_n = (out_char << 4) & 0xF0;
I2C_Basla();
I2C_Yaz(address);
I2C_Yaz(hi_n | rs | 0x04 | 0x08);
delay_us(50);
I2C_Yaz(hi_n | rs | 0x00 | 0x08);
delay_us(100);
I2C_Yaz(lo_n | rs | 0x04 | 0x08);
delay_us(50);
I2C_Yaz(lo_n | rs | 0x00 | 0x08);
I2C_Durdur();
}
void I2C_LCD_Init(char address) {
char rs = 0x00;
I2C_Basla();
I2C_Yaz(address);
delay_ms(30);
I2C_Yaz(0x30 | rs | 0x04 | 0x08);
delay_us(50);
I2C_Yaz(0x30 | rs | 0x00 | 0x08);
delay_ms(10);
I2C_Yaz(0x30 | rs | 0x04 | 0x08);
delay_us(50);
I2C_Yaz(0x30 | rs | 0x00 | 0x08);
delay_ms(10);
I2C_Yaz(0x30 | rs | 0x04 | 0x08);
delay_us(50);
I2C_Yaz(0x30 | rs | 0x00 | 0x08);
delay_ms(10);
I2C_Yaz(0x20 | rs | 0x04 | 0x08);
delay_us(50);
I2C_Yaz(0x20 | rs | 0x00 | 0x08);
I2C_Durdur();
delay_ms(10);
I2C_LCD_Cmd(address, 0x28);
I2C_LCD_Cmd(address, 0x06);
}
void I2C_LCD_Out(char address, char row, char col, char *text) {
while(*text)
I2C_LCD_Chr(address, row, col++, *text++);
}
void I2C_LCD_Out_Cp(char address, char *text) {
while(*text)
I2C_LCD_Chr_Cp(address, *text++);
}
void CustomChar(char address,char pos_row, char pos_col, char gelen[], char karak_adres, char sira) {
char i;
I2C_Lcd_Cmd(address, karak_adres);
for (i = 0; i<=7; i++) I2C_Lcd_Chr_Cp(address, gelen[i]);
I2C_Lcd_Cmd(address, _LCD_RETURN_HOME);
delay_ms(2);
I2C_Lcd_Chr(address, pos_row, pos_col, sira);
}
SPI Modu
SPI modu, eşzamanlı olarak çift yönlü 8 bitlik veri iletimi sağlayan ve Motorola firmasınca geliştirilmiş bir seri iletişim protokolüdür. Piyasada bu protokolü kullanarak mikrodenetleyicilerle haberleşen çeşitli aygıtlar vardır (RTCler, ADC-DAC dönüştürücüler, grafik LCD sürücüleri, Ethernet denetleyiciler, sensörler, vb.). Master (Sahip) ve Slave (Bağımlı) çalışma modları vardır. İletişimi gerçekleştirmek için ilgili mikrodenetleyicinin aşağıdaki üç pini kullanılır:
-
Serial Data Out (SDO) – RC5/SDO : Seri veri çıkışıdır. MOSI (Master Out Slave In) olarak da bilinir.
-
Serial Data In (SDI) – RC4/SDI/SDA : Seri veri girişi. MISO (Master In Slave Out) olarak da bilinir.
-
Serial Clock (SCK) – RC3/SCK/SCL : Seri veri iletişimi için gerekli saat darbesi
SDO/SDI/SCK isimleri SPI modu için kullanılan kısaltmalardır.
İlave olarak her hangi bir Slave modu çalışmasında dördüncü bir pin daha kullanılır.
-
Slave Select (SS – RA5/SS/AN4 : Bu pin bağımlı aygıtın seçiminde senkronizasyonun sağlanma-sı için kullanılır. İlgili TRIS komutuyla giriş olarak ayarlanmalıdır. (SS) ̅ hattı Master aygıt iletişime hazır olana kadar lojik-1 seviyesinde tutulur. SS hattı lojik-0 seviyesine çekildiğinde yeni bir iletimin başlayacağı anlaşılır. SS pini bir eşzamanlı bağımlı aygıt iletişimine izin verir. Bu durumdayken SPI modülü, (SS) ̅ pin kontrolü etkin Slave modunda olmalıdır (SSP1CON1<3:0> = 0100). SS pini lojik-0 seviyesindeyken, iletim ve alma etkinleştirilir ve SDO pini sürülür. Uygulamaya bağlı olarak bu pine pull up/pull down direnci bağlanması tavsiye edilir.
SPI modülü SSP1SAT kaydedicisinin CKE biti kuruluyken Slave modda çalıştırılıyorsa, (SS) ̅ pin kontrolü etkin olmalıdır. Yine SPI Slave modda çalışıyorken SSP1SAT kaydedicisinin SMP biti temizlenmelidir. Kısacası bu pin, mikrodenetleyicinin Slave modda diğer Master aygıt tarafından seçilmesi için kullanılan çip seçme pinidir.
Master modunda çalışan bir cihaz slave modunda olan cihazların ne zaman yayın yapacağını belirler. Bu işlem SCK pininin çıkış yapılmasıyla olur. Böylece master cihazın seri sinyal hattı çıkış olur ve slave cihazın karşılık gelen SCK hattına bağlanarak o cihazın kendi sinyaliyle çalışmasını sağlar.
MikroC programının çok geniş bir SPI kütüphanesi mevcuttur.
SPI Ethernet, SPI LCD ve SPI grafik LCD gibi farklı donanımlar için kütüphaneleri mevcuttur. Ancak bu program kapsamında standart SPI kütüphanesi kullanılacaktır.
SPIx_Init : Bu rutin SPI modülünü; master modu, FOSC/4 saat hızı, saat darbesi işlemediğinde (clock idle) düşük seviyede kalma durumu, verinin düşükten yükseğe geçişte iletildiği ve giriş verisinin iki veri aralığının ortasında örneklendiği çalışma modunda çalıştırır. SPI kütüphanesinin diğer rutinleri işletilmeden önce bu rutinin çağrılması gerekir.
Kullanılışı : SPI1_Init();
Açıklama : MSSP modülüne sahip mikrodenetleyicinin 1 numaralı SPI modülü başlatılıyor.
SPIx_Init_Advanced : Daha detaylı SPI çalıştırma rutinidir. SPI modülünün farklı çalışma modlarını kullanıcının seçmesine imkan verir.
Açıklama |
Önceden tanımlı kütüphane sabitleri |
SPI çalışma modu: |
Master clock = Fosc/4
|
_SPI_MASTER_OSC_DIV4
|
Master clock = Fosc/16
|
_SPI_MASTER_OSC_DIV16
|
Master clock = Fosc/64
|
_SPI_MASTER_OSC_DIV64
|
Master clock kaynağı TMR2
|
_SPI_MASTER_TMR2
|
Slave seçme etkin
|
_SPI_SLAVE_SS_ENABLE
|
Slave seçme pasif
|
_SPI_SLAVE_SS_DIS
|
Veri örnekleme aralığı: |
Giriş verisi aralığın ortasında örneklenir |
_SPI_DATA_SAMPLE_MIDDLE |
Giriş verisi aralığın sonunda örneklenir |
_SPI_DATA_SAMPLE_END |
SPI saat darbesi çalışmama durumu: |
Saat darbesi işlemediğinde bekleme seviyesi HIGH |
_SPI_CLK_IDLE_HIGH |
Saat darbesi işlemediğinde bekleme seviyesi LOW |
_SPI_CLK_IDLE_LOW |
Veri iletim kenar seviyesi: |
Veri düşükten yükseğe geçişte geçerli olur |
_SPI_LOW_2_HIGH |
Veri yüksekten düşüğe geçişte geçerli olur |
_SPI_HIGH_2_LOW |
Kullanılışı : SPI1_Init()SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV4, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_LOW, _SPI_LOW_2_HIGH);;
Açıklama : SPI modülü yukarıdaki örnekte; master modu, saat frekansının 4’te 1’i hızı, giriş verisi örneklemesi iki veri aralığının ortası, saat darbesi olmadığında LOW durumu, veri iletiminin düşükten yükseğe geçişte işlemesi çalışma modunda ayarlanmıştır.
SPI1_Read : SPI veri yolunda 1 byte’lık veri okur.
short take, buffer;
Kullanılışı : take = SPI_Read(buffer);
Açıklama : “buffer” değişkeni, gerçek veri kaydırmalı kaydedici tarafından belleğe alınana kadar geçici olarak kullanılan tampon veri alanıdır. Okunan veri “take” değişkeninde tutulur.
SPI1_Write : Veriyi SPI veri yoluna yazar.
char buffer;
Kullanılışı : SPI_Write(buffer);
Açıklama : “buffer” isimli SPI verisi mikrodenetleyicinin SSPBUF kaydedicisine yazılır.
SPI iletişimi tam-çift yönlü 4-hat (full-duplex 4-wire) seri haberleşme modu olarak da anılır. I2C’de olduğu gibi bunda da aynı anda tek bir cihaz Master olarak davranır ve diğerleri Slave konumundadır. Bu modda, adres iletimi yerine o an iletişim kurulacak bağımlı aygıtı seçmede kullanınan CS (Chip Select) ya da SS (Slave Select) pinleri kullanılır. İletişim hattında ne kadar bağımlı aygıt varsa sahip durumda olan mikrodenetleyici tarafında da o kadar seçme pini tahsis edilmelidir. Şekil 9.26’da bir SPI veri yoluna çok sayıda Slave aygıtın bağlantısı gösterilmiştir. Şekilde görüldüğü üzere 3 adet bağımlı aygıt vardır ve mikrodenetleyici tarafında da 3 adet I/O hattı bu iş için tahsis edilmiştir.
Şekil 9.26 SPI veri yoluna Master ve çoklu Slave bağlantısı
SPI başlatılırken, birtakım ayarlamalar yapılmalıdır. Bu işlem SSP1CON1<5:0> ve SSP1STAT<7:6> kaydedicilerinin bitlerini programlayarak yapılır. Bu kontrol bitleri aşağıdaki tanımlamaların yapılmasını sağlar:
- Master modu (SCK1 saat darbesi çıkışı olur)
- Slave modu (SCK saat darbesi girişi olur)
- Saat darbesi polaritesi (SCK1’in boşta olduğu durumda, diğer ifadeyle bekleme anındaki lojik seviyesini ifade eder.)
- Veri girişi örnekleme fazı (veri çıkış süresinin ortası ya da sonu)
- Saat darbesi kenarı (çıkış verisi SCK1’in yükselen/düşen kenarında)
- Saat darbesi hızı (yalnızca Master modu)
- Slave seçme modu (yalnızca Slave modu)
Seri portun etkinleştirilmesi için, SSP1CON1 kaydedicisinin SSP1 etkinleştirme biti SSPEN kurulmalıdır. SPI modunun resetlenmesi ya da yeniden yapılandırılması için, SSPEN biti temizlenmelidir. Ardından SSP1CONx kaydedicilerini yeniden ayarlayın ve etkinleştirme bitini kurun. Bu pin kurulduğunda; SDI, SDO, SCK ve (SS) ̅ pinleri seri port pinleri olarak tahsis edilir. Pinlerin seri port işlevinde davranış göstermesi için veri yönlendirme bitlerinin de (TRIS kaydedicisi) aşağıdaki gibi programlanması gerekir:
- SDI hattına karşılık gelen TRIS biti kurulmalıdır.
- SDO hattına karşılık gelen TRIS biti temizlenmelidir.
- SCK (Master modu) hattına karşılık gelen TRIS biti temizlenmelidir.
- SCK (Slave modu) hattına karşılık gelen TRIS biti kurulmalıdır.
- SS hattına karşılık gelen TRIS biti kurulmalıdır.
MSSP1 modülü bir iletme/alma kaydırmalı kaydedicisine (SSP1SR) ve bir tampon kaydedicisine (SSP1BUF) sahiptir. SSP1SR kullanıcı tarafından erişilebilir fiziksel bir kaydedici değildir. Bu kaydediciyle veri mikrodenetleyicinin içine ya da dışına gönderilir. Verinin ilk önce MSB biti gönderilir. SSP1BUF kaydedicisi alınan veri hazır olana kadar SSP1SR kaydedicisine yazılan veriyi tutar. Diğer bir ifadeyle verinin tamamlanmış son hali SSP1BUF kaydedicisine yazılmış olur.
SSP1BUF kaydedicisine tam yazma işlemi gerçekleştiğinde, SSP1STAT kaydedicisinin Tampon Algılama biti (BF – Buffer Full Detect) ve SSP1IF kesme bayrağı kurulur. Alınan verinin (SSP1BUF) bu çifte tamponlama işlemine tutulması, henüz daha yeni alınmış verinin okunmasından önce bir sonraki verinin de kabulüne izin verir. Veri iletimi/alma sırasında SSP1BUF kaydedicisine herhangi bir yazma işlemi dikkate alınmayacaktır ve SSP1CON1 kaydedicisinin yazma çarpışma algılaması WCOL biti lojik-1 olacaktır. Kullanıcı yazılım içinde, SSP1BUF kaydedicisine sonraki yazma işlemlerinin başarılı şekilde gerçekleşmesi için WCOL bitini temizlemelidir.
SPI DAC Kontrolcüsüyle Dijital Analog Dönüştürücü
Şekil 9.27’de MCP4921 ve PIC16F887 kullanılarak yapılmış bir DAC uygulama devresi gösterilmiştir. Dâhili ADC modülüne sahip mikrodenetleyicilerin bile büyük kısmı DAC (Digital Analog Converter – Dijital Analog Dönüştürücü) modülüne sahip değildir. DAC işlemi için yaygın olarak harici donanımlar kullanılır. MCP4921 entegresi SPI iletişim protokolünü kullanan bir DAC donanımıdır. Bu donanım yalnızca dijital veri aldığı için uygulamada mikrodenetleyicinin SDI pini kullanılmamıştır. RC0 pini çip seçme pini olarak kullanılmıştır.
Mikrodenetleyicinin RA0 ve RA1 girişlerine bağlanan butonlar ile analog sinyale dönüştürülecek dijital bilginin değeri artırılır ve yükseltilir. Voltaj referans seviyesi olarak 5V’luk besleme kaynağı kullanılmıştır. Voltaj artım işlemi 0- 4095 adım aralığında (12 bitlik hassasiyet) gerçekleşir. Dolayısıyla maksimum adıma ulaşıldığında çıkışta 5V’luk gerilim görülür.
Şekil 9.27 SPI kütüphanesiyle DAC uygulaması
Tablo 9.22 SPI kütüphanesiyle DAC uygulaması mikroC kodu
// DAC modül bağlantıları
sbit Chip_Select at RC0_bit; //DAC'ı seçme pini
sbit Chip_Select_Direction at TRISC0_bit;
unsigned int value;
// DAC artımı (0..4095) --> çıkış voltajı (0..Vref)
void DAC_Output(unsigned int valueDAC) {
char temp;
Chip_Select = 0; // DAC çipini seç
// Yüksek byte gönderiliyor
temp = (valueDAC >> 8) & 0x0F; // valueDAC[11..8] değerini temp[3..0]'a yükle
temp |= 0x30; // DAC ayarını tanımla, MCP4921 datasheet'ini inceleyin
SPI1_Write(temp); // Yüksek byte'ı SPI ile gönder
// Düşük byte gönderiliyor
temp = valueDAC; // valueDAC[7..0] değerini temp[7..0]'a yükle
SPI1_Write(temp); // Düşük byte'ı SPI ile gönder
Chip_Select = 1; // DACçipini seçme
}
void main() {
ANSEL = 0;
ANSELH = 0;
TRISA0_bit = 1; // RA0 pini giriş
TRISA1_bit = 1; // RA1 pini giriş
Chip_Select = 1; // DAC'ı seçme
Chip_Select_Direction = 0; // CS# pinini çıkış olarak ayarla
SPI1_Init(); // SPI modülünü hazırla
value = 2048; // Program başladığında DAC çıkışa
// referans seviyesinin orta değerini verir
while (1) { // Sonsuz döngü
if ((RA0_bit) && (value < 4095)) { // Eğer RA0 butonuna basılırsa
value++; // değeri artır
}
else {
if ((RA1_bit) && (value > 0)) { // RA1 butonuna basılırsa
value--; // değeri azalt
}
}
DAC_Output(value); // Değeri DAC çipine gönder
Delay_ms(1); // Tuş basım adımını yavaşlat
}
}
SPI İle Farklı Mikrodenetleyicilerin Master-Slave Haberleşmesi
Mikrodenetleyicileri, farklı seri iletişim protokolleriyle aralarında haberleştirmek yaygın bir uygulamadır. SPI protokolüyle de iki ya da daha fazla mikrodenetleyiciyi haberleştirmek mümkündür. Şekil 9.28’de böyle bir uygulamanın devre şeması gösterilmiştir. Bu uygulamada mikroC programının kütüphanesi kullanılmamıştır. Kaydedici değerleri uygun şekilde ayarlanarak veri çakışması ve kaybı yaşanmayacak şekilde 3 adet mikrodenetleyicinin Master-Slave olarak haberleşmeleri sağlanmıştır.
Bu sisteme, mikroC programının SPI_LCD kütüphanesi yardımıyla MCP23S17 16-bitlik port genişleticisi kullanılarak 16x2 LCD’nin sürülmesi de eklenmiştir. Uygulamada mikrodenetleyiciler birbirleriyle papatya zinciri (daisy-chain) tekniğiyle bağlanmış ve böylece tek bir kontrol pini ile tüm mikrodenetleyiciler kumanda edilmiştir.
SPI papatya zinciri uygulamasında bir mikrodenetleyicinin çıkışı diğerinin girişi olur. Uygulamada zincirleme olarak Master aygıttan sırayla diğerlerine veri aktarımı vardır. Zincirin en sonunda yer alan mikrodenetleyiciden gelen veri Master mikrodenetleyiciye geri döner ve bu değer SPI_LCD sürücü uygulamasıyla yine aynı hat üzerinden LCD ekrana gönderilir.
Uygulama yazılımını gerçekleştirmek için ortak bir klasör altında 3 ayrı proje dosyası oluşturulmuştur. Proje dosyalarından birisi Master MCU için, diğer iki proje dosyası ise Slave MCU’lar içindir. Her çalışma dosyası ayrı olarak derlenmeli ve PROTEUS-ISIS® çalışma ortamında MCU’lara ayrı ayrı yüklenmelidir.
Ana MCU tarafından birer birer artan 1 baytlık sayı 1’inci Slave aygıta gönderilmektedir. 1.Slave aygıta gelen bilgi gelen yeni bilgiyle toplanır. Gelen bilgi mod5 ve mod10 işlemlerine tabi tutulur. Mod durumuna göre sinyal verdirilir. Toplanan sayı 2’inci Slave aygıta gönderilir. 2’inci Slave aygıtta mod2 ve mod10 işlemleri yapılır ve sayı Master MCU’ya gönderilir. Ardından zincir içinde işlenerek değeri değişmiş olarak gelen yeni sayı LCD ekrana yazdırılır.
Şekil 9.28 SPI papatya zinciri ile Master-Slave çoklu aygıt kumandası
Tablo 9.23 SPI papatya zinciri ile Master-Slave çoklu aygıt kumandası Master mikroC kodu
// Mikroc codes for SPI master device
#define chip_select PORTA.RA4
// Port GENİŞLETİCİ modül bağlantıları
sbit SPExpanderRST at RA4_bit;
sbit SPExpanderCS at RA5_bit;
sbit SPExpanderCS_Direction at TRISA5_bit;
sbit SPExpanderRST_Direction at TRISA4_bit;
unsigned short tx_data=0, rx_data=0;
char msg1[] = "I2C LCD 4 Bit";
char rx_data_txt[4];
void SPI_MASTER_INIT(void){
TRISA = 0b00001100;
SSP1STAT.SMP=0;
SSP1STAT.CKE=0;
SSP1CON1.SSPEN=1;
SSP1CON1.CKP=1;
SSP1CON1.WCOL=0;
SSP1CON1.SSPM3=0;
SSP1CON1.SSPM2=0;
SSP1CON1.SSPM1=0;
SSP1CON1.SSPM0=0;
PCON=0;
}
void main(){
ANSELA = 0x00;
INTCON.GIE = 1;
PORTA = 0x00;
LATA = 0x00;
OSCCON = 0b01101000;
CM1CON0=0;
ADCON0=0;
chip_select=1;
SPI_MASTER_INIT(); // Spi1_Init_Advanced(_SPI_MASTER_OSC_DIV4, _SPI_DATA_SAMPLE_
_END, _SPI_CLK_Idle_HIGH, _SPI_LOW_2_HIGH);
while(1){
tx_data++;
chip_select=0;
SSP1CON1.WCOL=0;
SSP1STAT.BF=0;
SSP1BUF=tx_data;
SSP1BUF=0;//Dışardan gelen bilgi için tampon temizleniyor
while(!SSP1IF_bit);
SSP1IF_bit=0;
SSP1CON1.WCOL=0;
SSP1STAT.BF=0;
rx_data = SSP1BUF;
BytetoStr(rx_data, rx_data_txt);
SPI_Lcd_Config(0);
SPI_Lcd_Cmd(_LCD_CLEAR);
SPI_Lcd_Cmd(_LCD_CURSOR_OFF);
SPI_Lcd_Out(1,1, "SAYI= ");
SPI_Lcd_Out(1,7, rx_data_txt);
delay_ms(250);
if(tx_data>=255) tx_data=0;
}
}
Tablo 9.24 SPI papatya zinciri ile Master-Slave çoklu aygıt kumandası Slave01 mikroC kodu
#define chip_select PORTA.RA3
unsigned int buffer, toplam;
unsigned short rx_data=0, tx_data=0;
void SPI_SLAVE_INIT(void){
TRISA=0b00001110;
SSP1STAT.SMP=0;
SSP1STAT.CKE=0;
SSP1CON1.SSPEN=1;
SSP1CON1.CKP=1;
SSP1CON1.SSPM3=0;
SSP1CON1.SSPM2=1;
SSP1CON1.SSPM1=0;
SSP1CON1.SSPM0=0;
SSP1CON3.BOEN=1; //Daisy-Chain uygulaması
}
void main(){
ANSELA = 0x00;
INTCON.GIE = 1;
PORTA = 0x00;
LATA = 0x00;
OSCCON = 0b01101000;
SPI_SLAVE_INIT(); //Spi_Init_Advanced(SLAVE_SS_ENABLE,DATA_SAMPLE_END,CLK_Idle_HI
IGH,LOW_2_HIGH);
while(1){
while(!SSP1IF_bit); //Tam 8-bitlik bilgi gelene kadar bekleme yapılır.
SSP1IF_bit=0;
rx_data=SSP1BUF;
toplam+=rx_data;
if((rx_data%10 == 0)){
PORTA.F4=1;
PORTA.F5=0;
}
else if((rx_data%5 == 0)){
PORTA.F4=0;
PORTA.F5=1;
}
else{
if(toplam>255) toplam = toplam >> 8;
tx_data = toplam;
SSP1BUF = tx_data;
PORTA.F4=0;
PORTA.F5=0;
}
}
}
Tablo 9.25 SPI papatya zinciri ile Master-Slave çoklu aygıt kumandası Slave02 mikroC kodu
#define chip_select PORTA.RA3
unsigned int buffer, toplam;
unsigned short rx_data=0, tx_data=0;
void SPI_SLAVE_INIT(void){
TRISA=0b00001110;
SSP1STAT.SMP=0;
SSP1STAT.CKE=0;
SSP1CON1.SSPEN=1;
SSP1CON1.CKP=1;
SSP1CON1.SSPM3=0;
SSP1CON1.SSPM2=1;
SSP1CON1.SSPM1=0;
SSP1CON1.SSPM0=0;
SSP1CON3.BOEN=1; //Daisy-Chain uygulaması
}
void main(){
ANSELA = 0x00;
INTCON.GIE = 1;
PORTA = 0x00;
LATA = 0x00;
OSCCON = 0b01101000;
SPI_SLAVE_INIT();//Spi_Init_Advanced(SLAVE_SS_ENABLE,DATA_SAMPLE_END,CLK_Id le_HIGGH,LOW_2_HIGH);
while(1){
while(!SSP1IF_bit);
SSP1IF_bit=0;
rx_data=SSP1BUF;
if(rx_data%10 == 0){
PORTA.F4=1;
PORTA.F5=0;
}
else if(rx_data%2 == 0){
PORTA.F4=0;
PORTA.F5=1;
}
else{
SSP1BUF = rx_data;
PORTA.F4=0;
PORTA.F5=0;
}
}
}
DİKKAT: Uygulamada LCD ekranın Reset kontrolünü sağlayacak boşta bacak kalmadığından, Slave MCU’ları kontrol etmede kullanılan Mater RA4 bacağı sıralı olarak Reset işlemi için de tahsis edilmiştir. Bu normal şartlarda tavsiye edilen bir uygulama değildir. Daha rahat çalışma koşulları adına, Master PIC12F1840 yerine PIC18F2550’yi kaydedicilerini benzer şekilde ayarlamak suretiyle kullanabilirsiniz.
SPI Protokolüyle Haberleşen Dijital Potansiyometre
Mikroelektronika firmasının kullanıma sunduğu çok sayıda modüler kit bulunmaktadır. Şekil 9.29’da gösterilen MCP41XX temelli dijital potansiyometre kiti de bunlardan biridir. Piyasada, SPI ya da SPI/I2C modlarında haberleşen farklı modellerde dijital potansiyometre devreleri bulmak mümkündür.
Şekil 9.29 SPI arabirimli dijital potansiyometre
Dijital potansiyometrelerin PB ve PA olarak isimlendirilen referans bacakları vardır. Bu bacakların arasında Wiper olarak adlandırılan PW isimli ayar hattı bulunur. Dijital potansiyometreler de ilgili kanal ya da kanallarının direnç değeriyle anılırlar. Uygulamada 10K’lık dijital potansiyometre kullanılmıştır. SPI hattından 8-bit olarak gönderilen değere göre 0-10K arası direnç değişimi gerçekleştirilir. Mekanik potansiyometrelerin kullanıldığı her yerde kullanılabilir. Böylece mikrodenetleyici temelli olarak kumanda işlemi gerçekleştirilebilir.
MCP41XX tek kanallı bir dijital potansiyometredir. Şekil 9.30’da 6 kanallı olan AD5206BN10 entegresi kullanılarak gerçekleştirilmiş bir uygulama devresi gösterilmiştir. Devrede CH isimli kanal seçme butonuyla 6 kanal ileri yönde seçilmektedir. Hangi kanala gelinirse bağımsız olarak ilgili kanala ait direnç değeri SPI hattı üzerinden değiştirilmektedir. Direnç değerlerinin değişimleri UP ve DOWN isimli butonlarla yapılmaktadır.
Uygulama devresinde 6 kanalın tüm A ve B bacakları sırasıyla VDD ve VSS kaynaklarına bağlanmıştır. Böylece voltaj referansı elde edilmesi suretiyle, mikrodenetleyicinin analog girişlerinden okuma işleminin gerçekleştirilmesi hedeflenmiştir. Wiper hatlarının değeri 0-255 arasında 8-bitlik SPI verisiyle değiştirilerek 0-1023 arasında 10-bitlik analog değer elde edilmiştir. İlgili analog değerin kaç ohmluk dirence karşılık geldiği referans ayarlamasıyla yapılabilir. Kanal sayısının birden fazla olduğu dijital potansiyometrelerde istenen kanala erişebilmek için adres değeri girilmesi gerekir.
AD5206’da 6 kanal olduğundan adres hatları 3-bit ile temsil edilmektedir ve ilk önce adres bilgisi gönderilmektedir. SPI’da MSB biti önce gönderildiğinden ilk 5-bit 0 olarak gönderilmekte ve cihaz tarafından bir işlem gerçekleşmemektedir. Ardından 3 adres + 8 dijital pot verisi olmak üzere 11-bitlik veri paketi gönderilmektedir. Aygıt tarafından bu 11-bitlik paketin ilk gelen 3 biti adres bilgisi olarak işlenir.
Dijital potansiyometrede 10KΩ değeri için Wiper direncinin hesaplanması şu şekilde olur;
Wiper hattının ilk temas noktası B hattından başlar ve 0’dır. Ancak bu noktadaki direnç 45Ω kadardır ve RW olarak adlandırılır. RAB nominal dirençtir ve bu örnek için 10KΩ’dur. Bundan sonraki her adım bu iki direnç değeri referans alınarak gider.
Denklem 9.3
Tam skalada (Dx = 255), RWB = 10006 Ω olarak elde edilir.
Skala orta noktaya (Dx = 128) getirildiğinde, RWB = 5045 Ω olarak elde edilir.
RWA değerleri, elde edilen RWB değerinin eşleniğidir. Her iki ucun arası tam olarak 10090Ω kadar yapar.
Denklem 9.4
Şekil 9.30 SPI arabirimiyle dijital potansiyometre uygulaması
Tablo 9.26 SPI arabirimiyle dijital potansiyometre uygulaması mikroC kodu
// LCD bağlantıları tanımlanıyor
sbit LCD_RS at RB0_bit;
sbit LCD_EN at RB1_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;
sbit LCD_RS_Direction at TRISB0_bit;
sbit LCD_EN_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;
sbit Up at RD0_bit;//Kanal değeri artırma
sbit Down at RD1_bit;//Kanal değeri azaltma
sbit kanal at RB2_bit; //Kanal seçme
sbit DigPot_CS at RD2_bit;//SPI dijital pot aygıtını seçen hat
sbit DigPot_CS_Direction at TRISD2_bit;
unsigned int adc_value;
char counter0, counter1, counter2, counter3, counter4, counter5, sayac;
char pot_adres=0;
char message1[] = "Analog";
char message2[] = "W";
char *kanal_no="0";
char *ADC = "0000";
void InitMain() {
//Kaydediciler yapılandırılıyor
ANSEL = 0b00111111; // RA0-5 analog giriş
TRISA = 0b00101111; // RA0 giriş
ANSELH = 0x00; // Tüm B portu dijital
TRISB = 0x04;
TRISC = 0X10;
TRISD = 0X03; //RD0 ve RD1 giriş
TRISE = 0x01;
OSCCON = 0X70;
CM1CON0 = 0;
INTCON.GIE = 1;
INTCON.RBIE = 1;
IOCB = 0X04;
//Diğer modüller yapılandırılıyor
ADC_Init(); // ADC kütüphanesi komutuyla ADC açılıyor
SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV4, _SPI_DATA_SAMPLE_END, _SPI_CLK_IDLE_HIG
GH, _SPI_LOW_2_HIGH);
Lcd_Init(); //LCD kütüphanesiyle LCD yapılandırılıyor
DigPot_CS = 1;
DigPot_CS_Direction = 0;
adc_value = 0;
counter0 = 255;
counter1 = 255;
counter2 = 255;
counter3 = 255;
counter4 = 255;
counter5 = 255;
Lcd_Cmd(_LCD_CLEAR); // CLEAR display
Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off
Lcd_Out(1,1,message1); // Write message1 in 1st row
Lcd_Out(2,1,message2); // Write message2 in 2nd row
}
void Debounce_Key(){
Delay_ms(100);
}
void ADC_Display(unsigned short ch){
unsigned int adc_value;
adc_value = ADC_Read(ch);
ADC[0] = adc_value/1000 + 48;
ADC[1] = (adc_value/100)%10 + 48;
ADC[2] = (adc_value/10)%10 + 48;
ADC[3] = adc_value%10 + 48;
kanal_no[0] = ch%10 + 48;
LCD_Out(1, 7, kanal_no);
LCD_Out_Cp(" = ");
LCD_Out(1,11, ADC);
kanal_no[0] = ch%10 + 49;
LCD_Out(2,2, kanal_no);
}
void kanal_oku(char pot_adres_g, char sayac_g){
DigPot_CS = 0;
SPI1_Write(pot_adres_g);
SPI1_Write(sayac_g);
ADC_Display(pot_adres_g);
DigPot_CS = 1;
}
void main() {
InitMain(); // Ana yapılandırma rutini çağrılıyor
do{
switch(pot_adres){
case 0:
sayac=counter0;
break;
case 1:
sayac=counter1;
break;
case 2:
sayac=counter2;
break;
case 3:
sayac=counter3;
break;
case 4:
sayac=counter4;
break;
case 5:
sayac=counter5;
break;
}
kanal_oku(pot_adres, sayac);
if(!Up){
switch (pot_adres){
case 0:
counter0++;
Debounce_Key();
break;
case 1:
counter1++;
Debounce_Key();
break;
case 2:
counter2++;
Debounce_Key();
break;
case 3:
counter3++;
Debounce_Key();
break;
case 4:
counter4++;
Debounce_Key();
break;
case 5:
counter5++;
Debounce_Key();
break;
}
}
if(!Down){
switch (pot_adres){
case 0:
counter0--;
Debounce_Key();
break;
case 1:
counter1--;
Debounce_Key();
break;
case 2:
counter2--;
Debounce_Key();
break;
case 3:
counter3--;
Debounce_Key();
break;
case 4:
counter4--;
Debounce_Key();
break;
case 5:
counter5--;
Debounce_Key();
break;
}
}
}while (1);
}
void interrupt(){
if(INTCON.RBIF){
if(!kanal){
pot_adres++;
if(pot_adres>5) pot_adres=0;
}
INTCON.RBIF=0;
}
}
1-Wire Protokolü ve Kütüphanesi
1-Wire protokolü Dallas elektronik teknolojisinin geliştirdiği ve tek hat üzerinden Master/Slave formatında iletişim gerçekleştirmeyi sağlayan bir haberleşme protokolüdür. DS18B20 gibi dijital sıcaklık sensörü ya da uzun yıllar İstanbul’da toplu ulaşımda da kullanılmış olan Akbil (iButton) cihazlarının iletişim için kullandığı bir protokoldür. Bu protokolü kullanan cihazların veri hattı mikrodenetleyicinin ilgili pinine pull-up direnci ile bağlanır.
Şekil 9.31 Dijital sıcaklık ve saat devresi
Şekil 9.32 Sıcaklık ve saat devresi baskı devre uygulaması
Şekil 9.33 Sıcaklık ve saat devresi malzeme yerleşimi
Tablo 9.27 Dijital sıcaklık ve saat devresi mikroC kodu
// LCD modül bağlantıları
sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;
sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;
// LCD modül bağlantıları sonu
const unsigned short TEMP_RESOLUTION = 9;
char *text = "000.00";
unsigned temp;
char i;
void Read_Temperature()
{
// Sıcaklık okuma işletiliyor
Ow_Reset(&PORTE, 2); // Onewire reset sinyali
Ow_Write(&PORTE, 2, 0xCC); // SKIP_ROM komutu işletiliyor
Ow_Write(&PORTE, 2, 0x44); // CONVERT_T komutu işletiliyor
Delay_us(120);
Ow_Reset(&PORTE, 2);
Ow_Write(&PORTE, 2, 0xCC); // SKIP_ROM komutu işletiliyor
Ow_Write(&PORTE, 2, 0xBE); // READ_SCRATCHPAD komutu işletiliyor
temp = Ow_Read(&PORTE, 2);
temp = (Ow_Read(&PORTE, 2) << 8) + temp;
}
void Display_Temperature(){
unsigned short RES_SHIFT = TEMP_RESOLUTION - 5;
char temp_whole;
unsigned int temp2write;
unsigned int temp_fraction;
temp2write = temp;
// Sıcaklık negatif mi kontrol ediliyor
if (temp2write >> 15)
{
text[0] = '-';
temp2write = ~temp2write + 1;
}
else
text[0] = '+';
// Tam sıcaklık değeri çıkartılıyor
temp_whole = temp2write >> RES_SHIFT ;
// Tam sıcaklık değeri karakterlere çevriliyor
text[1] = (temp_whole/10)%10 + 48; // 10'lar hanesi alınıyor, 48 ASCII kodda 0'a karşılık gelir
text[2] = temp_whole%10 + 48; // 1'ler hanesi alınıyor
text[3] = '.';
// Virgülden sonraki kısım alınıyor
temp_fraction = temp2write & 0x000F;
temp_fraction *= 625; // ikilik sistemde virgül sonrası her bir hane 625 katsayısı şeklinde ilerler
// Öncelikle 0x000F değeriyle 64 bitlik sıcaklık bilgisinin ilk dört biti alınıyor
// ve binler seviyesine çıkartılması için 625 değeriyle çarpılıyor. Ör: 0,50 derece
// 0B1000 bilgisiyle gösterilir. Bu ikilik değerin karşılığı 8'dir. 8x625 = 5000 yapar.
text[4] = temp_fraction/1000 + 48; // Vigüllü kısmın binde/birler hanesi alınıyor
temp_fraction = temp_fraction/100; /*Virgüllü kısım 100'e bölünüp mod 10 ile yüzde/birler hanesi alınıyor*/
temp_fraction = temp_fraction%10; /* Örnek olarak ilk dört biti 0b1100 olan bir 64 bitlik sıcaklık
bilgisinin virgüllü kısmı 0,75 derece (12x625 = 7500) olur. 7500/1000 = 7'dir (tamsayı kısmı alındığı
için). Ardından 7500 değeri 100'e bölünür, sonuç 75 olur. 75%10 = 5'dir. Böylece Virgülden sonra 7 ve
5 değerleri alınmış olur */
text[5] = temp_fraction+48;
// Sıcaklık LCD'ye yazılıyor
Lcd_Out(2, 9, text);
// 233 ASCII kodu derece işareti yazdırılıyor
Lcd_Chr(2,15,0xDF);
Lcd_Chr_CP('C');
}
unsigned short read_ds1307(unsigned short address){
unsigned short r_data;
I2C1_Start();
I2C1_Wr(0xD0); // yön biti işletilir (0 yazma, 1 okuma içindir)
I2C1_Wr(address);
I2C1_Repeated_Start();
I2C1_Wr(0xD1);
r_data=I2C1_Rd(0);
I2C1_Stop();
return(r_data);
}
void write_ds1307(unsigned short address,unsigned short w_data){
I2C1_Start(); // I2C start sinyali
I2C1_Wr(0xD0); // yön biti işletilir(0 yazma, 1 okuma içindir)
I2C1_Wr(address);
I2C1_Wr(w_data);
I2C1_Stop(); // I2C stop sinyali
}
unsigned char BCD2UpperCh(unsigned char bcd){
//Onlar hanesini karakter olarak alan fonksiyon
return ((bcd >> 4) + '0'); //Onlar hanesi 4 adım sağa kaydırılıyor
}
unsigned char BCD2LowerCh(unsigned char bcd){
//Birler hanesini alan fonksiyon
return ((bcd & 0x0F) + '0'); //Onlar hanesi yok ediliyor
}
int Desimal2BCD(int a){
//Desimal olarak verilen sayıyı BCD formata çevirir
int t1, t2; //Örnek olarak a sayısının 23 olduğunu düşünelim
t1 = a%10; // t1 = 23%10 = 3 olarak birler hanesi elde edilir, 0000 0011
a = a/10; // 10'lar hanesi için 10'a bölünüyor
t2 = a%10; // t2 = 2 olarak onlar hanesi elde ediliyor
t2 = t2 << 4; // t2 = 2 sayısı 4 bit sola kaydırılıyor. Böylece 0010 0000 elde ediliyor
t1 = t1 | t2; // 0000 0011 | 0010 0000 mantıksal VEYA sonucu 0010 0011 değeri elde ediliyor
return t1;
}
int BCD2Desimal(int a){
//BCD olarak alınan sayıyı desimal formata çevirir
int r,t; //Örneğin a sayısının 23 olduğunu düşünelim. BCD formatı 0x23'dür, yani 0b 0010 0011
t = a & 0x0F; // t = 0010 0011 & 0000 1111 = 0000 0011 = 3 olur.
r = t; //r = 3 olarak bir kenara yazılır.
a = 0xF0 & a; // 1111 0000 & 0010 0011 = 0010 0000 elde edilir.
t = a >> 4; //0010 0000 dört bit sağa kaydırılır, 0000 0010 elde edilir. t= 2olur.
r = t*10 + r; // r'nin yeni değeri, r = 2x10 + 3 = 23 oluyor, yani 0x17, 0b 00001 0111
return r; //23 desimal değeri döndürülüyor
}
int second;
int minute;
int hour;
int hr;
int day;
int dday;
int month;
int year;
int ap;
unsigned short set_count = 0;
unsigned uzunluk_msj01, uzunluk_msj02, konum;
short set;
char time[] = "00:00:00";
char date[] = "00-00-00";
void main()
{
TRISA = 0x07;
TRISB = 0X00;
TRISC = 0X00;
TRISD = 0X00;
TRISE = 0X00;
PORTA = 0x00;
PORTB = 0X00;
PORTC = 0X00;
PORTD = 0X00;
PORTE = 0X00;
I2C1_Init(100000); //DS1307 I2C 100KHz'de çalışacak şekilde ayarlanıyor
CMCON = 0x07; // Karşılaştırıcılar kapatılıyor, I/O pinleri dijitale ayarlanıyor
ADCON1 = 0x06; // ADC dönüştürücüsü kapatılıyor
Lcd_Init(); // LCD başlatılıyor
Lcd_Cmd(_LCD_CLEAR); // Ekran temizleniyor
Lcd_Cmd(_LCD_CURSOR_OFF); // Kursör kapatılıyor
Lcd_out(1,1,"Saat =");
Lcd_out(2,1,"Sicak =");
do{
set = 0;
if(PORTA.F0 == 0){
Delay_ms(100);
if(PORTA.F0 == 0){
set_count++;
if(set_count > 3){
set_count = 0;
}
}
}
if(set_count){
if(PORTA.F1 == 0){
Delay_ms(100);
if(PORTA.F1 == 0)
set = 1;
}
if(PORTA.F2 == 0){
Delay_ms(100);
if(PORTA.F2 == 0)
set = -1;
}
if(set_count && set){
switch(set_count){
case 1:
hour = BCD2Desimal(hour);
hour = hour + set;
if(hour > 23){
hour = 0;
}
else if(hour < 0){
hour = 23;
}
hour = Desimal2BCD(hour);
write_ds1307(2, hour); //saati yaz
break;
case 2:
minute = BCD2Desimal(minute);
minute = minute + set;
if(minute >= 60)
minute = 0;
if(minute < 0)
minute = 59;
minute = Desimal2BCD(minute);
write_ds1307(1, minute); //dakikayı yaz
break;
case 3:
if(abs(set))
write_ds1307(0,0x00); //0 saniyeye resetle ve osilatörü başlat
break;
}
}
}
second = read_ds1307(0); //0 nolu adres ile saniye
minute = read_ds1307(1); //1 nolu adres ile dakika
hour = read_ds1307(2); //2 nolu adres ile saat bilgisi alınıyor
// hr = hour & 0b00111111;
time[0] = BCD2UpperCh(hour);
time[1] = BCD2LowerCh(hour);
time[3] = BCD2UpperCh(minute);
time[4] = BCD2LowerCh(minute);
time[6] = BCD2UpperCh(second);
time[7] = BCD2LowerCh(second);
Lcd_out(1, 9, time); //Saat dakika saniye LCD'ye aktarılıyor
Read_Temperature(); //Sıcaklık okunuyor
Display_Temperature(); //Sıcaklık LCD'ye aktarılıyor
Delay_ms(100);
}while(1);
}
Manchester Kod ve RF İletişim
Kablosuz kısa mesafe iletişimde RF (Radio Frequency) modülleri sıklıkla tercih edilir. Bunlardan 434 MHz ASK (Amplitude Shift Keying – Genlik Kaydırmalı Anahtarlama) modülasyonlu alıcı/verici modülleri, kısa mesafe uzaktan kumandalı tüm uygulamalarda yaygın olarak kullanılmaktadır. Yüksek frekanslı taşıyıcı radyo dalgalarının sayısal bilgiyle modüle edilebilmesi için sayısal verinin dönüştürülmesi gerekir. Bunun için “Manchester Kodu” denilen bir sayısal kodlama tekniği tercih edilmektedir.
Şekil 9.34 Sayısal bir verinin farklı iki tip Manchester kod standardına göre kodlanması (Kaynak: Wikipedia)
Şekil 9.35 Manchester koda göre ASK modüleli sinyalin elde edilmesi
Şekil 9.36 434 MHz RF alıcı/verici modülleriyle iletişim uygulaması
Şekil 9.37 434 MHz RF alıcı/verici modülleri ve anten bağlantı donanımları
Tablo 9.28 434 MHz RF alıcı/verici modülleriyle iletişim uygulaması ALICI devresi mikroC kodu
// LCD modül baglantilari
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D4 at RB0_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;
// LCD modül baglantilari sonu
// Manchester modül baglantilari
sbit MANRXPIN at RC0_bit;
sbit MANRXPIN_Direction at TRISC0_bit;
sbit MANTXPIN at RC1_bit;
sbit MANTXPIN_Direction at TRISC1_bit;
// Manchester modül baglantilari sonu
char error, ErrorCount, temp;
void main() { ErrorCount = 0;
ANSEL = 0;
ANSELH = 0;
C1ON_bit = 0;
C2ON_bit = 0;
OSCCON = 0X72;
TRISC.F5 = 0;
Lcd_Init();
Lcd_Cmd(_LCD_CLEAR);
Man_Receive_Init(); // Alici baslatilir
while (1) {
Lcd_Cmd(_LCD_FIRST_ROW);
while (1) { // Baslama bayti için beklenilir
temp = Man_Receive(&error); // Bayte alimina çalisilir
if (temp == 0x0B) // Verici kodunda karsilik gelen baslama bayti
break; // Baslama kodu yakalandiysa if yapisindan çik
if (error) // Hata da varsa if yapisindan çik
break;
}
do {
temp = Man_Receive(&error); // Bayt alimina calisilir
if (error) { // Hata olduysa
Lcd_Chr_CP('?'); // LCD ekan üzerine soru isareti yaz
ErrorCount++; // Hata sayicisini günceller
if (ErrorCount > 20) { // Çok fazla hata olduysa
temp = Man_Synchro(); // Tekrar senkronizasyon dene
//Man_Receive_Init() //Alternatif olarak aliciyi tekrar baslatmayi
ErrorCount = 0; // deneyebilirsiniz. Hata sayicisi sifirlanir
}
}
else { // Hata yoksa
if (temp != 0x0E) // vericide tanimlanan bitis kodu görülene
Lcd_Chr_CP(temp); // kadar alinan karakterler LCD'ye yazilir
}
Delay_ms(25);
}
while (temp != 0x0E) ;// Eger bitis koduna ulasildiysa döngüden çikilir
}
}
Tablo 9.29 434 MHz RF alıcı/verici modülleriyle iletişim uygulaması VERİCİ devresi mikroC kodu
sbit MANRXPIN at RC0_bit;
sbit MANRXPIN_Direction at TRISC0_bit;
sbit MANTXPIN at RC1_bit;
sbit MANTXPIN_Direction at TRISC1_bit;
char index, character;
char s1[] = "mikroElektronika";
void main(){
ANSEL = 0;
ANSELH = 0;
C1ON_bit = 0;
C2ON_bit = 0;
OSCCON = 0X72;
Man_Send_Init(); // Verici baslatilir
while (1){
Man_Send(0x0B); // Aliciyla eslesme için bir baslama kodu belirlenir
Delay_ms(100); // Bir süreligine beklenilir
character = s1[0]; // Stringten ilk karakter alinir
index = 0; // Indeks degiskeni kurulur
while (character){//String dizisinin sonundaki sifira ulasilana kadar
Man_Send(character); // karakter dizisini gönder
Delay_ms(90);
index++; // indeks degiskeni arttirilir
character = s1[index]; // sonraki karakter alinir
}
Man_Send(0x0E); // Gönderimin bittigini belirten koda karar verilir
Delay_ms(1000);
}
}
Şekil 9.38 Çok kanallı RF iletişim uygulaması
Tablo 9.31 Çok kanallı RF iletişimi VERİCİ devresi mikroC kodu (PIC12F675)
sbit BUTON at GP5_bit;
char sayac=1;
void main() {
TRISIO=0x20; // GPIO5 giriş
GPIO=0;
CMCON =0x07;
ANSEL=0X00; //Analog girisler dijital I/O olarak ayarlanıyor
OPTION_REG.INTEDG=0;
OPTION_REG = 0X00;
WPU = 0X20; // GPIO5 için dahili pull-up aktif
INTCON.GIE = 1; // Evrensel kesme aktif yapılıyor
INTCON.GPIE = 1; // Giriş değişim kesmesi aktif yapılıyor
IOC = 0X20; // GP5 portu için giriş değişim kesmesi aktif
while(1){
if(sayac>4) sayac = 1;
}
}
void interrupt(){
if(INTCON.GPIF){
if(!BUTON){
GPIO = sayac;
sayac = sayac << 1;
}
INTCON.GPIF=0;
}
}
Tablo 9.32 Çok kanallı RF iletişimi ALICI devresi mikroC kodu (PIC16F628A)
sbit SARI at RA0_bit; // PortA 0.biti 1.kanal
sbit MAVI at RA1_bit; // PortA 1.biti 2.kanal
sbit KIRMIZI at RA2_bit; // PortA 2.biti 3.kanal
sbit SW1 at RB1_bit; // PortB 1.biti
sbit SW2 at RB2_bit; // PortB 2.biti
sbit SW3 at RB3_bit; // PortB 3.biti
int saniye=0, zamansayaci=0;
char sayac=0;
void main() {
TRISA=0x00;
PORTA=0;
TRISB=0x7E;
PORTB=0;
CMCON =0x07;
OPTION_REG = 0X07; //TMR0 ORANI 1:256
TMR0 = 128;
INTCON.T0IE=1;
INTCON.GIE=1;
INTCON.RBIE=1;
while(1){
if(SW3==0 && SW2==0 && SW1==0){
if(saniye>10){
saniye=0;
PORTA=0;
}
}
if(SW3==0 && SW2==0 && SW1==1){
if(saniye>20){
saniye=0;
PORTA=0;
}
}
if(SW3==0 && SW2==1 && SW1==0){
if(saniye>40){
saniye=0;
PORTA=0;
}
}
if(SW3==0 && SW2==1 && SW1==1){
if(saniye>80){
saniye=0;
PORTA=0;
}
}
if(SW3==1 && SW2==0 && SW1==0){
if(saniye>160){
saniye=0;
PORTA=0;
}
}
if(SW3==1 && SW2==0 && SW1==1){
if(saniye>320){
saniye=0;
PORTA=0;
}
}
if(SW3==1 && SW2==1 && SW1==0){
if(saniye>640){
saniye=0;
PORTA=0;
}
}
if(SW3==1 && SW2==1 && SW1==1){
if(saniye>1280){
saniye=0;
PORTA=0;
}
}
}
}
void interrupt(){
if(INTCON.RBIF){
sayac = PORTB; // değişim olduğunda giriş okunuyor
sayac = sayac >> 4; // MSB bitleri LSB'ye çekiliyor
PORTA = sayac;
INTCON.RBIF = 0; // bayrak temizleniyor
saniye=0; // zaman resetleniyor
}
if(INTCON.T0IE){
zamansayaci++;
if(zamansayaci>=30){
saniye++;
zamansayaci=0;
}
INTCON.T0IF=0;
TMR0=128;
}
}
USB Modülü İle Seri İletişim
Temel Bilgiler
Bu başlık altında USB arabirimine sahip PIC18FX550/X455 ailesi mikrodenetleyiciler kullanılarak bilgisayar ile iletişim kurulması anlatılacaktır. Ancak bu uygulamalara başlamadan önce USB donanımının ne olduğu üzerine bilgisi olmayanların ya da eksik bilgisi olanların mutlaka EK-I6’ya bakmaları tavsiye olunur.
Bu mikrodenetleyici ailesi tam hız (full-speed) ve düşük hız (low-speed) uyumlu USB 2.0 arayüz motoruna (Serial Interface Engine - SIE) sahiptir. SIE, mikrodenetleycinin USB sunucu cihazıyla iletişim kurmasını sağlar. Şekil 9.39’da PIC mikrodenetleyicilerinin USB çevresel aygıt donanımı gösterilmiştir.
Şekil 9.39 USB çevresel aygıt donanımı ve tercihleri
Şekil 9.40 PIC18FX455/X550 ailesi mikrodenetleyicilerde osilatör katı
USB Arabirimli Temel Uygulama Geliştirilmesi
Şekil 9.41 USB arabirimi üzerinden cihaz kumandası
Tablo 9.34 USB üzerinden cihaz kumandası CCS C kaynak kodu
#include
int8 iBuff[1];
int8 oBuff[1] = 0x29;
#DEFINE LED1 PIN_D0
#DEFINE LED2 PIN_D1
void main()
{
set_TRIS_A(0x00);
set_TRIS_B(0x00);
usb_init();
usb_task();
usb_wait_for_enumeration(); //Cihaz, hazır olana kadar bekle
// Devrede bulunan ledler ilk olarak çıkış olarak ayarlanir.
output_drive(LED1);
output_drive(LED2);
// Ledlerin ilk durumu lojik-0 olarak belirlenir.
output_low(LED1);
output_low(LED2);
while(TRUE)
{
while(usb_enumerated()) // USB Bağlıysa;
{
if (usb_kbhit(1)) // Eğer PC veri geldiyse;
{
usb_get_packet(1, iBuff, 1); // Gelen veri okunup "iBuff" dizisine kaydedilir.
if (iBuff[0] == 0x29) // Gelenveri "0x29" ise;
{
// LED1 500ms yan sön yapar.
output_high(LED1);
delay_ms(500);
output_low(LED1);
delay_ms(500);
// PC veri gönderilir.
usb_put_packet(1, oBuff, 1, USB_DTS_TOGGLE);
// Veri gönderildikten sonra led yan sön yapar.
output_high(LED2);
delay_ms(500);
output_low(LED2);
delay_ms(500);
}
}
}
}
}
Tablo 9.35 USB üzerinden cihaz kumandası devresi için başlık dosyası
#include <18F4550.h>
// USB haberleşmesinde kullanılacak data boyutları tanımlanır.
#define USB_CONFIG_HID_TX_SIZE 1
#define USB_CONFIG_HID_RX_SIZE 1
// USB için gerekli konfigürasyon dosyalarinin olduğu kütüphaneler çağrılır.
#include
#include
#include
#FUSES NOWDT //No Watch Dog Timer
#use delay(clock=48MHz,crystal=20MHz,USB_FULL)
#define USB_CONFIG_VID 0x33
#define USB_CONFIG_PID 0x461
#define USB_CONFIG_BUS_POWER 500
#define USB_STRINGS_OVERWRITTEN
char USB_STRING_DESC_OFFSET[]={0,4,18};
char const USB_STRING_DESC[]={
//string 0 - language
4, //length of string index
0x03, //descriptor type (STRING)
0x09,0x04, //Microsoft Defined for US-English
//string 1 - manufacturer
12, //length of string index
0x03, //descriptor type (STRING)
'H',0,
'A',0,
'K',0,
'A',0,
'N',0,
//string 2 - product
11, //length of string index
0x03, //descriptor type (STRING)
'G',0,
'E',0,
'N',0,
'C',0,
};
Tablo 9.36 USB Led uygulaması için C# kaynak dosyası
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace USBLed
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnHandleCreated(EventArgs e)
{// Windows üzerinden varolan kütüphanemizi tanıtıyoruz.
base.OnHandleCreated(e);
usbHidPort1.RegisterHandle(Handle);
}
protected override void WndProc(ref Message m)
{// Daha sonra USB HID aygıtımızı aktif hale getiren bir mesaj yolluyoruz.
usbHidPort1.ParseMessages(ref m);
base.WndProc(ref m);
}
private void Form1_Load(object sender, EventArgs e)
{
toolStripStatusLabel1.Text = "USB Bağlantı Kurulamadı.";
}
private void usbHidPort1_OnSpecifiedDeviceArrived(object sender, EventArgs e)
{// USB bağlandığında bunu yakalayıp bilgilendirir.
if (InvokeRequired)
{
Invoke(new EventHandler(usbHidPort1_OnSpecifiedDeviceArrived), new object[] { sender, e });
}
else
{
toolStripStatusLabel1.Text = "USB Bağlandı.";
}
}
private void usbHidPort1_OnSpecifiedDeviceRemoved(object sender, EventArgs e)
{// USB çıkarıldığında bunu yakalayıp bilgilendirir.
toolStripStatusLabel1.Text = "USB Çıkarıldı.";
}
private void baglan_Click(object sender, EventArgs e)
{
try
{
usbHidPort1.VendorId = 0x0033;
usbHidPort1.ProductId = 0x0461;
usbHidPort1.CheckDevicePresent();
}
catch (Exception)
{
//MessageBox.Show("Id girişleri yapiniz.");
}
}
private void usbHidPort1_OnDeviceArrived(object sender, EventArgs e)
{
toolStripStatusLabel1.Text = "USB Bağlandı.";
}
private void usbHidPort1_OnDeviceRemoved(object sender, EventArgs e)
{
toolStripStatusLabel1.Text = "USB Çıkarıldı.";
}
byte[] Giden_Veri = new byte[1 + 1]; // giden veri + uç nokta
void VeriYolla()
{
if (this.usbHidPort1.SpecifiedDevice != null) // Belirtilen USB Aygıtı tanımlıysa,
this.usbHidPort1.SpecifiedDevice.SendData(Giden_Veri);
// İlgili Buffer'a bilgiyi gönder.
for (int i = 0; i <=1; i++) Giden_Veri[i] = 0;
}// Veri gönderildiğinde tüm bufferları 0'la. (Buffer çakışmasını önler.)
private void led1_Click(object sender, EventArgs e)
{
if (usbHidPort1.SpecifiedDevice != null) // Belirtilen USB Aygıtı tanımlıysa,
{
Giden_Veri[0] = 0x29;
Giden_Veri[1] = 0x29;
// İlgili Buffer'a bilgiyi gönder.
this.usbHidPort1.SpecifiedDevice.SendData(Giden_Veri);
for (int i = 0; i <= 1; i++) Giden_Veri[i] = 0;
// Veri gönderildiğinde tüm bufferları 0'la. (Buffer çakışmasını önler.)
label2.Text = "Veri gönderildi.";
}
}
private void usbHidPort1_OnDataRecieved(object sender, UsbLibrary.DataRecievedEventArgs args)
{
CheckForIllegalCrossThreadCalls = false;
byte[] gVeri = new byte[usbHidPort1.SpecifiedDevice.InputReportLength + 1];
byte i = 0;
foreach (byte myData in args.data)
{
gVeri[i++] = myData;
}
if(gVeri[1] == 0x29)
{
label2.Text = "LED2 Yanıp Söndü.";
}
}
}
}
Şekil 9.42 HID Terminal yardımıyla USB veriyolu üzerinden MCU'ya veri gönderilmesi
Şekil 9.43 USB LED uygulama devresinin tasarlanan program ile kontrol edilmesi
LabVIEW İle USB Üzerinden Mikrodenetleyiciyle Haberleşme ve PID Temelli DC Motor Kumandası
Şekil 9.44 Çıkış ve giriş işareti arasındaki farkın kontrol yöntemine göre değişimi
Şekil 9.45 PID kontrolü için arayüz tasarımı
Şekil 9.46 PID.vi dosyası blok tasarım ekranı
Şekil 9.47 Kontrol parametreleri için vi dosyaları
Şekil 9.48 USB portu üzerinden PID temelli DC motor kumandası şematik devresi
Şekil 9.49 USB portu üzerinden PID temelli DC motor kumandası devre görüntüsü
#include
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN
#use delay(clock=48000000)
#include
#include
#byte PORTA = 0xF80
#byte PORTB = 0xF81
#byte PORTC = 0xF82
#byte PORTE = 0xF84
#define ledMavi PIN_B2
#define ledKirmizi PIN_B1
#define on output_high
#define off output_low
int okuma;
int16 duty_max = 0xAFC8;
int duty_min = 0;
int duty = 0;
void main()
{
output_B(0x00);
setup_timer_1(T1_EXTERNAL| T1_DIV_BY_1);
setup_ccp1(CCP_PWM);
setup_timer_2(T2_DIV_BY_16, 0xFFFF,1);
set_pwm1_duty(duty);
usb_cdc_init();
usb_init();
while (!usb_cdc_connected()) {
}
usb_task();
while (true)
{
off(ledKirmizi);
off(ledMavi);
if(usb_enumerated())
{
set_timer1(0);
delay_ms(250);
okuma=get_timer1();
if(okuma>0){
output_toggle(ledMavi);
usb_cdc_putc(okuma);
delay_ms(20);
}
if (usb_cdc_kbhit())
{
duty = usb_cdc_getc();
if(duty > duty_max)
{
duty=duty_max;
}
else if(duty < duty_min)
{
duty=duty_min;
}
set_pwm1_duty(duty);
on(ledKirmizi);
delay_ms(20);
}
}
}
}
|