CCS-C ile PIC Programlama, Bit ve Byte İşlemleri Dahili Fonksiyonları
bit_clear()
Bir değişkenin istenilen Bit’ini Sıfır’lamak için kullanılır. Parametre olarak 8,16 veya 32 bitlik değişken alır. Geriyi değer döndürmeyen bu fonksiyonun kullanımı şu şekildedir.
bit_clear(degisken,BitNo)
1
2
3
4
5
6
7
8
9
|
int x; long y; int32 z; x = 0x1f; y = 0xa501; z = 0x9c54a8; bit_clear(x,0); // x'in 0. bitini sıfırla x= 0x1e olur bit_clear(y,8); // y'nin 8. bitini sıfırla y = 0xa401 olur bit_clear(z,31); // x'in 31. Bit'ini sıfırla x = 0x1c54a8 olur. |
Bu fonksiyonun görevini yerine getirecek Ansi C uyumlu kod ise şu şekilde yazılabilir. degisken & = ~(1 << Bitno); x= 0x1f iken bit_clear(x,0) komutu x değerini 0x1e yapıyordu. Ansi C kodu ile yapalım x &= ~(1<<0); 1 sayısını 0 kez sola kaydırırsak yine bir olur, yani kaydırmıyoruz 🙂 0000 0001 (1 sayısı) ~ operatörü Bit düzeyinde değil işlevi görür. ~1 = 1111 1110 ( Bir ler 0, Sıfırlar 1 oluyor) x & = 1111 1110 = x & 1111 1110 x degerini ve 1111 1110 değerini alt alta yazıp & (And) ler isek 0001 1111 (0x1f) 1111 1110 0001 1110 (0x1e) değerine ulaşmış oluruz
Şöyle bir soru akla gelebilir, (1<;<;8) veya (1<;<;22) gibi bir işlemde 1 sayısı int olarak mı long olarak mı veya int32 olarak mı değerlendiriliyor. Cevap: CCS-C kullandığınız değişkene göre kendisi bunu otomatik ayarlıyor. Şöyle bir örnek verelim;
1
2
3
|
long f; f = 0xa501; f &=~(1<<8); ifadesi için derleyici sadece BCF 3F.0 kodunu oluşturur |
f değişkeni 2 Byte olduğu için RAM’de 0x3e,03f adreslerinde saklanmaktadır. (Derleyicinin hangi değişkeni Hangi Adrese yerleştirdiğini öğrenmek için Compile Araç Çubuğundaki Symbol Map seçeneğine tıklayın)
1
2
3
|
f = 0xa501; ataması yapıldığında RAM Bölgesinde Adres 3e = 0x01 Adres 3f = 0xa5 olur |
F’nin 8. Bit’inin Sıfır’landığında 0xa5 değeri 0xa4 olacağı için asm oalrak BCF 3F.0 kodu doğrudur.
bit_set()
Bu fonksiyon bit_clear() fonksiyonun tersi işlem görür. Yani bir değişkenin istenilen Bit’inin değerini 1 yapmak için kullanılır.
bit_set(degisken,BitNo)
1
2
3
4
5
6
7
8
9
|
int x; long y; int32 z; x = 0x1f; y = 0xa501; z = 0x9c54a8; bit_clear(x,7); // x'in 7. bitini 1 yap x= 0x9f olur bit_clear(y,2); // y'nin 2. bitini sıfırla y = 0xa401 olur bit_clear(z,31); // x'in 31. Bit'ini sıfırla x = 0x0c54a8 olur. |
Eşdeğer Kod
degisken |=(1<;<; BitNo); // Sağlamasını yapıp sonucu görün
bit_test()
Bir değişkenin istenilen Bit’inin durumunu sorgulamak için kullanılır. Sorgulanan Bit 1 ise geriye döndürülen değer 1, Sıfır ise geriye döndürülen değer 0 olur.
bit_test(degisken,BitNo)
degisken: 8 , 16 veya 32 Bit Integer Bitno: 0-31 arası değer (Kullandığınız değişken tipine göre)
1
2
3
4
5
6
7
|
int x; int32 y; BOOLEAN led; if (bit_test(x,5)) // x'in 5. Biti 1 ise if (!bit_test(x,5)) // x'in 5. Biti 0 ise led = bit_test(y,26); //y'nin 26. Bit'i 1 ise led = 1 olur 0 ise led = 0 olur |
Eşdeğer Kod
(1<<bitno)& degisken;<=”” strong=””>
Kullanım örneği
1
|
if ((1<<5)&x) // x'in 5. bit'i 1 ise |
rotate_left()
Bu fonksiyon bir değişkeni veya dizi değişkeni sola döndürmek için kullanılır. Parametre olarak değişkenin adresi ve kaç Byte işlem yapılacağını alır. Geriye değer döndürmez
rotate_left(adres,byte)
adres: Hafıza bölgesi Byte: Döndürme işlemi Kaç byte üzerinden yapılacak
Döndürme İşlemi Nedir?
Hemen örnekle açıklayalım;
x = 0x81 olsun binary = 1000 0001 değerine karşılık gelir. x’i sola döndürmek şu demektir. En soldaki biti çıkar diğer bütün bit’leri 1 sola kaydır. Soldan çıkan Bit’i en sağa yerleştir. Yani kısaca en soldaki Bit’i al en sağa koy.
en soldaki biti alıp en sağa koyar isek yeni değer 0000 00011 olur. Hex olarak 0x03 değeri bulunur
1
2
|
int x = 0x81; rotate_left(&x,1); //komutundan sonra x= 0x03 olur |
&x = x değişkeninin adresi manasına gelir
1
2
|
long x=0xffaa; rotate_left(x,2); // x = 0xff55 olur |
rotate_right()
Bu fonksiyon ise sağa döndürme işlevi yapar. İşleyişi rotate_left() ile aynıdır sadece yön farklıdır. En sağdaki bit çıkarılıp en sola konur.
1
2
|
int x = 0x81; rotate_right(&,1); //komutundan sonra x= 0xc0 olur |
&x = x değişkeninin adresi
1
2
|
long x=0xffaa; rotate_right(x,2); // x = 0x7fd5 olur |
shift_left()
Sola kaydırma işlemlerinde kullanılan bu fonksiyonun kullanım şekli aşağıdaki gibidir.
shift_left(adres,bytes,deger)
adres: Ram hafıza bölgesi, yani değişkenin adresi bytes: İşlemin kaç Byte üzerinden yapılacak? data: 1 veya 0, kaydırma işlemiyle birlikte değişkene eklenecek bit
sola_kaydırma işleminde, en soldaki bit çıkarılıp diğer bitler sola kaydırılır. data parametresinde verilen değer en sağa eklenir.
Örnek verelim
1
2
|
int x= 0x81; // binary 1000 0001 shift_left(&;amp;amp;amp;x,1,1); |
en soldaki biti atalım 0000001 olur en sağa da 1 koyalım 0000 0011 olur.
1
2
|
shift_left(&;amp;amp;amp;x,1,1) = 0x03 olur shift_left(&;amp;amp;amp;x,1,0) = 0x02 olur (soldaki bit atılıp en sağa 0 konulur) |
1
2
|
int dizi[5]; shift_left(dizi,5,1); // 5 byte üzerindne işlem yapılır, dizinin ilk elmanının ilk biti 1 olur |
Not: dizinin adı asresi yerine geçer bu yüzden başına &; simgesi konmaz.
Bu fonksiyonun geriye döndürdüğü değer en soldan atılan Bit’dir.
1
2
3
|
int1 test_bit; int x=0x81 // binary = 1000 0001 test_bit = shift_left(&;amp;amp;amp;x,1,0)) // test_bit = 1 olur, en solda 1 vardı çünkü |
shift_right()
Bu fonksiyonun çalışması aynı shift_left() gibidir, bunda yön sola doğru değil sağa doğrudur. En sağdaki bit çıkartılır en soldan verilen değer girilir.
1
2
3
|
int x = 0x81; // binary = 1000 0001 shift_right(&;amp;amp;amp;x,1,1) = 0xc0 (binary 1100 0000) shift_right(&;amp;amp;amp;x,1,0) = 0x40 (binary 0100 0000) |
make8()
make8() fonksiyonu 16 veya 32 bit değişkenlerden 8 bitlik değerler almak için kullanılır. Kullanımı şu şekildedir.
make8(degisken,offset)
degisken: 16 veya 32 Bit integer offset: 0-3 arası değer. (16 için 0-1, 32 için 0-3)
1
2
3
4
5
6
7
|
long x=0x1234; long y = 0x12345678; int sekizBit; sekizBit = make8(x,1); //sekizBit = 0x12 olur sekizBit = make8(x,0); //sekizBit = 0x34 olur sekizBit = make8(y,3); //sekizBit = 0x12 olur sekizBit = make8(y,1); //sekizBit = 0x56 olur |
make16()
2 adet 8 Bitlik (1 Byte) değerden 16 bitlik (2 Byte) değer oluşturmak için kullanılır. Kullanılışı şu şekildedir;
make16(MSB,LSB);
MSB ve LSB 8 bit Integer
1
2
3
4
5
|
int x,y; long z; x = 0x55; y = 0x aa; z = make16(x,y); // z = 0x55aa olur. |
make32()
Parametre olarak verilen 8 bit veya 16 Bit değerlerden 32 bit değer oluşturmak için kullanılır. Kullanılışı şu şekildedir;
make32(deger1,deger2,deger3,deger4)
Parametrelerin ikisi opsiyoneldir, degerlerin 1 kısmı 16 bir kısmı 8 bit olabilir
int x,y,z,t; long a,b; int32 c;
x = 0xd1; y= 0x12; z = 0x55; t=0xa9; a= 0xf301; b =b7c4;
c = make32 (x,y,z,t); // c = 0xd11255a9 olur c = make32(b,a) // c = 0xb7c4f301 olur c = make32(x,a); // c = 00d1f301 olur (değer 00 ile 32 bite tamamlanır)
_mul()
Çarpma işlemi oldukça fazla kod üreten bir işlemdir. Bu fonksiyon daha optimize çarpma işlemleri yapmak için kullanılır. Çarpılacak sayıları parametre olarak alır, çarpım sonucunu geri döndürür.
_mul(deger1,deger2)
deger1, deger2 çarpılacak sayılardır. Bu degerler 8 Bit veya 16 Bit olabilir. Her ikisi 8 Bit olduğunda geriye dönen değer 6 Bit diğer durumlarda 32 Bit’tir.
1
2
3
4
5
6
|
int x=10,y=100; long z=500,sonuc1 int32 sonuc2; sonuc1 = _mul(x,y); // sonuc1 = 1000 olur sonuc2 = _mul(x,z); // sonuc2 =5000 olur |
swap()
8 Bitlik 1 sayıda, 4 er bitlik her iki kısma “nibble” denmektedir. Yani 8 bitlik bir sayıda 2 adet nibble vardır. swap() fonksiyonu parametre olarak verilen sayının nibble’larının yerini değiştirir. Şöyleki;
1
2
|
int x=0xa5; swap(x); // x = 0x5a olur. |
Eşdeğer Kodu
x = (x<;<;4) | (x>;>;4) ;
#fuse
Bir önceki yazımızda kod örneklerinde kullandığımız halde açıklamamıştık. Haklı olarak bazı arkadaşlar sordular. Bizde bu yazıda bahsedelim dedik.
PIC Mikro Denetleyicilerine Program yüklenirken, fuse denilen ayarlarında yüklenmesi gerekir. Bu ayarlar hex kodunu çipe yüklerken kullandığınız programda yapılabilir. Siz kodunuzda bu yarları belirtirseniz çipe yazılımı yüklediğiniz programda bu ayarları yapmazsınız.
Fuse ayarları PIC modeline göre değişiklik göstermektedir. CCS-C de View Menüsüne tıklayıp açılan kısımdan Valid Fuses butonuna basın. Açılacak olan pencerede istediğiniz PIC modelini seçin, o PIC için kullanabileceğiniz FUSE ayarları ekrana gelcektir.
PIC16F877 için FUSE ayarlarına Bakalım
XT: 4 Mhz veya 4 Mhz’den Küçük Kristal Osilatör HS: 4 Mhz’den Büyük Krsital Osilatör RC: Direnç / Kapasite Osilatörü Clock Out Var 4 mhz kristal kullanacaksanız #fuse XT daha büyük kullanacaksanız #fuse HS şeklinde ayarlamanız gerekiyor. NOWDT: WatcDog Timer Kapalı WDT: WathDog Timer Açık PUT: Power UP Timer açık NOPUT: Power UP Timer Kapalı PROTECT: Kod Okumaya Karşı Korumalı NOPROTECT: Kod Okumaya Karşı Korumasız PROTECT_50%: Kod Bölgesinin %50’si korumalı. BROWNOUT: Brown Out Koruması Aktif NOBROWNOUT: Brown Out Koruması Aktif Değil LVP: Düşük voltaj Programlama Açık NOLVP: Düşük voltaj Programlama Kapalı CPD: Data EEPROM Kod Korumalı NOCPD: Data EEPROM Kod Korumalı Değil WRT: Program Hafızası Yazma Korumalı NOWRT: Program Hafızası Yazma Korumalı Değil DEBUG: ICD debugger kullanılacak NODEBUG: ICD Debugger Kullanılmayacak
Fuse ayarlarının her birinin ne manaya geldiğini ayrıntılı olarak başka bir yazıda işleyeceğiz.
#use delay
CCS-C derleyicisinin 3 adet gecikme fonksiyonu vardır, bunlar delay_ms(), delay_us ve delay_cycles() fonksiyonlarıdır. Bu fonksiyonların düzgün çalışması için. İşlemcinin Saat Frekansı #use delay direktifi ile Derleyiciye bildirilir.
#use delay(clock = 20000000) // İşlemci Çalışma Hızı 20 Mhz)
Eğer Watch-Dog Zamanlayıcısını kullanıyorsanız, gecikme fonksiyonlarında zamanlayıcının sıfırlanması gerekir. Bu gibi durumlarda #use_delay aşağıdaki gibi kullanılır
#use delay(clock = 20000000,restart_wdt) // 20 Mhz, watch-Dog sıfırla
PROTEUS PROJESİ İÇİN ÖRNEK KODLAR
Kodları simule etmek için bir önceki yazımızda verdiğimiz proteus projesini kullanabilirsiniz.
Örnek-1
B portundaki dip-switch’ler D portundaki Led’leri kontrol ediyor.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <16f877.h> #use delay(clock=4000000) #fuses XT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG void main( void ) { int PORTB,PORTD,i; // PORTB,PORTD ve i 8 bit integer while (1) { PORTB = input_b(); // B portunu oku değeri PORTB'ye ata PORTD = 0; // PORTD değişkeni sıfırlanıyor (PORT DEĞİL) for (i=0;i<8;++i) // 8 adımlık döngü, her adımda i bir artıyor { if (!bit_test(PORTB,i)) // PORTB in önce 0. biti test ediliyor bit_set(PORTD,i); // bit 0 ise (switch = ON) PORTD ilk biti 0 // döngü sayesinde 8 bit kontrol edilir ve ayarlanır } output_d(PORTD); // PORTD değişkenini Porta yaz (Ledler) } } |
Örnek-2
Aynı işi yapan başka bir kod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <16f877.h> #use delay(clock=4000000) #fuses XT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG void main( void ) { int PORTB,PORTD,i; while (1) { PORTB = input_b(); // B portu PORTB değişkenine okunuyor PORTD = 0; //değişken sıfır'lanıyor for (i=0;i<8;++i) // 8 adımlık döngü her adımda 1 artıyor i { //PORTB değişkeninin 8 biti PORTD değişkenine shift ediliyor shift_left(&;amp;amp;amp;PORTD,1,bit_test(PORTB,i)); } output_d(PORTD); // değer LED'lere aktarılıyor } } |
Örnek-3
Geçen yazımızda yaptığımız yürüyen ışık.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <16f877.h> #use delay(clock=4000000) #fuses XT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG void main( void ) { int PORTB,PORTD,i; PORTD = 0x01; // önce ilk ledi yakacağız while (1) { output_d(PORTD); // değeri ledlere aktar delay_ms(250); rotate_left(&;amp;amp;amp;PORTD,1); // bir biti devamlı döndürüyoruz } } |
Örnek-3
Swap için örnek
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <16f877.h> #use delay(clock=4000000) #fuses XT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG void main( void ) { int PORTB,PORTD,i; PORTD = 0xf0; // Led'lerin sadece 4 tanesi yanacak while (1) { // sırasıyla ledlerin ilk 4 ü yanar sonra ikinci dördü yanar // daha sonra yine ilk dördü yanar. // değer 0xf0 ve 0x0f arasında dönüşüm yapar sürekli output_d(PORTD); delay_ms(250); swap(PORTD); } } |