Bu uygulama notunda çalışma zamanı esnasında PIC mikrodenetleyicilerin program hafızasına nasıl veri yazıp kullanabileceğinizi göstereceğim. Bazı kişilerin kullandığı mikrodenetleyicinin RAM hafızasının yetersiz gelmesi sonucu sistemi dağıtarak farklı mikrodenetleyicilere geçtiğini gördüm. Bu kadar zahmete girmek yerine RAM hafızada sakladığınız değişmeyen veya nadir olarak kullanılan static verileri flash hafızaya atarak flash’a göre çok daha düşük olan RAM den kazanç sağlayabilirsiniz. Bu uygulama notu ile sistem değiştirmek yerine küçük bir yazılım takviyesi ile ağır bir yükten kurtulabilirsiniz. Fakat bahsettiğim gibi verilerin çalışma esnasında oluştuktan sonra kullanım sıklığının düşük olması yada verilerin değişmeden kalması gerekmektedir. Çok fazla dinamik değişken ile anlık olarak çok yüksek miktarlarda işlem yapıyorsanız bu uygulama notundaki teknikler verimsiz kalacaktır.
Bu uygulama notunda elimde bulunan PIC18F97J60 mikrodenetleyicisi ile anlatım yapacağım. Kullandığınız mikrodenetleyicinin datasheet lerinde flash hafızaya yazım yapıp yapamadığını, blok uzunlukları gibi bilgileri öğrenebilirsiniz. Bu gibi bilgiler çipten çipe değişmektedir.Bu uygulama notunda anlattıklarım ile birden fazla amaç gerçekleştirilebilir. Bunlar aşağıda listelenmiştir.
- Çalışma esnasında Flash hafızayı RAM gibi kullanabilirsiniz.
- Çipi resetlediğinizde yada kapatıp açtığınızda verileriniz silinmediği için tekrar okunabilir olur.
- Çip programlandıktan sonra çalışma esnasında program hafızaya program ekleyip işlemcinin akışını programı yüklediğiniz adrese verirseniz dinamik olarak yüklemiş olduğunuz program çalışır. (Bootloader tarzı uygulamalarınız için)
Uygulama notunu pdf olarak aşağıdaki linkten indirebilirsiniz. Aşağıda dökümanın özetini bulacaksınız.
PIC Flash Program Hafızasına Yazma / Silme / Okuma İşlemleri ( 300kb )
PIC18F97J60 128K flash hafızaya sahiptir. Bu hafızayı adresleyebilmek için en az 17 bitlik göstericiye ihtiyaç vardır. PIC18F97J60 ta ise 22 bit kullanılmıştır. Bu 22 bitin 21 biti ile 2MB’a kadar adresleme yapılmaktadır. 22. bit mikrodenetleyici ID numarasına ve konfigürasyon ayarlarına erişmek için kullanılmaktadır ve bu dökümanda anlatılmayacaktır. PIC18F97J60 hafızasını 1024 bytelık bloklar halinde silebiliriz ve 64 bytelık bloklar halinde veri yazabiliriz.
PIC18F97J60 22 bitlik bir gösterici (TBLPTR, Table Pointer) kullandığından TBLPTRU, TBLPTRH, TBLPTRL olmak üzere 3 byte ile ifade edilmektedir.TBLPTR flash hafızadaki bir byte ı göstermektedir. TBLPTR göstericisinin adresini göstermiş olduğu byte’a TABLAT saklayıcısı ile veri yazılır veya okunur. Veri yazma ve okuma işlemlemlerinde TABLAT ve TBLPTR saklayıcılarının kullanımı aşağıdaki şekillerde gösterilmiştir.
Flash hafızaya veri yazmak için PIC18F97J60 işlemcisinde 2 adet kontrol, 1 adet tutucu ve 3 adet gösterici olmak üzere 6 adet saklayıcı vardır. Bu saklayıcılar aşağıda listelenmiştir.
- EECON1
- EECON2
- TABLAT
- TBLPTR (TBLPTRU, TBLPTRH, TBLPTRL)
EECON1 saklayıcısı flash hafızaya erişimde kontrol saklayıcısı olarak kullanılmaktadır. 4 adet biti vardır. EECON2 saklayıcısı ise fiziksel bir saklayıcı değildir. Okunduğunda tüm bitleri 0 olarak okunur. Bu saklayıcı yazma ve silme işlemini onaylamak için (güvenlik) kullanılmaktadır. Yazma veya silme işlemlerinden önce bu saklayıcıya ardışıl olarak 0X55 ve 0XAA değerleri yazılmalıdır.EECON1 saklayıcısı ve bitleri aşağıdaki gibidir.
TABLAT 16 bit genişliğinde olan flash hafıza ile 8 bit genişliğinde olan RAM hafıza arasında veri taşınırken tutucu olarak kullanılmaktadır.
Flash hafıza üzerinde işlemler yapılırken TBLPTR saklayıcıları kullanılmaktadır. Bu saklayıcılar Flash hafıza üzerindeki bir byte’ı göstermektedir. Gösterici 22bit olduğundan 3byte a bölünmüştür. Yazma, okuma, silme işlemlerinde ilk 21 bit kullanılmakta, 22. bit mikrodenetleyici ID numarasına ve konfigürasyon ayarlarına erişmek için kullanılmaktadır. Okuma işlemleri için 21 bitin hepsi kullanılmaktadır. Fakat silme ve yazma için belirli bitler kullanılmaktadır. Silme işlemleri için 20:10 arası bitler kullanılmakta ilk 10 bit (210=1024) kullanılmamaktadır. Silme işlemleri 1024 bytelık bloklar halinde yapılmaktadır. 20:10 arası bilgi ise bize hangi 1024 bytelık bloğun silineceğini söyler.
Yazma işlemlerinde ise Holding Registers adı verilen tutucular vardır. Flash hafızaya yazılmak istenen veriler önce bu HR tutucularına yazılmakta ve yaz komutu ile birlikte HR saklayıcılarındaki değerler flash hafızaya blok halinde yazılmaktadır. PIC18F97J60 için 64 adet HR tutucusu vardır. Yazma işlemleri için TBLPTR göstericisinin 20:6 arası bitleri kullanılmaktadır. İlk 6 bit (26=64) hangi HR tutucusuna yazılacağını göstermektedir. Aşağıdaki şekilde yazma, silme ve okuma işlemleri için TBLPTR saklayıcılarının hangi bitlerinin kullanıldığı gösterilmiştir.
Okuma İşlemleri:
Okuma işlemlerinde flash hafızadan bir byte alınıp RAM hafızaya aktarılır. Bunun için TBLRD komutu kullanılmaktadır. Bu komut aşağıdaki şekildeki gibi flash hafızadan TABLAT saklayıcısına bir byte veri okumaktadır. Okunacak byte'ı TBLPTR saklayıcısını kurarak belirleriz. Adres kuran ve okuma işlemi yapan C kodları aşağıdadır.
/**************************************************************************
* Yazar :Barış Samancı, www.barissamanci.net
*
* Fonksiyon :void ROMVeriOku (BYTE* ptr, BYTE nLen);
*
* Açıklama :Flash hafızadan ptr parametresinde adresi geçilen RAM
* hafizaya ikinci parametresinde geçilen uzunluk kadar veri okur
*
* Önşart :ROMAdresKur(DWORD adres) fonksiyonu ile okunacak adres
* önceden kurulmalıdır.
**************************************************************************/
void ROMVeriOku (BYTE *ptr, BYTE nLen)
{
BYTE i;
for( i=0 ; i<nLen ; i++ )
{
_asm
TBLRDPOSTINC
_endasm
ptr[i]=TABLAT;
}
}
/**************************************************************************
* Yazar :Barış Samancı, www.barissamanci.net
*
* Fonksiyon : void ROMAdresKur(DWORD adres);
*
* Açıklama : TBLPTR göstericisini istenilen adrese kurar
*
* Önşart : yok
**************************************************************************/
void ROMAdresKur(DWORD adres)
{
TBLPTRU = (adres & 0XFF0000) >> 16;
TBLPTRH = (adres & 0XFF00) >> 8;
TBLPTRL = adres & 0XFF;
}
Silme İşlemleri:
Flash hafızalar için veri yazılmadan önce ilgili veri bloğunun silinmesi gerekmektedir.PIC18F97J60 için silme işlemleri için silme blok uzunluğu 1024 bytetır. Silme işlemini başlattığınız zaman işlemci verdiğiniz adresten itibaren 1024 byte lık bir bloğu siler. Hangi 1024 bytelık bloğun silineceğini TBLPTR saklayıcısının 20:10 aralığına yazmanız gerekmektedir. Silme işlemleri için dahili bir timer kullanılmaktadır ve süreyi otomatik olarak kurmaktadır. Silme işlemi bu timer haber verdiğinde biter. Bu süre boyunca işlemci STALL diye ifade edilen durumda boşta beklemektedir. Silme işlemleri boyunca silme işlemini etkilemesin diye kesmeleri kapatmanız gerekmektedir. Bunun için INTCON saklayıcısındaki global kesme biti olan GIE bitine 0 yazabilirsiniz. Silme işlemleri bittiğinde ise tekrar açabilirsiniz.
/**************************************************************************
* Yazar :Barış Samancı, www.barissamanci.net
*
* Fonksiyon :void ROMVeriSil( void );
*
* Açıklama :TBLPTRnin gösterdiği bloğu silme blok uzunluğu kadar siler
*
* Önşart :ROMAdresKur(DWORD adres) fonksiyonu ile silinecek adres
* önceden kurulmalıdır.
**************************************************************************/
void ROMVeriSil( void )
{
EECON1bits.WREN = 1;
EECON1bits.FREE = 1;
INTCONbits.GIE = 0;
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1;
INTCONbits.GIE = 1;
}
Yazma İşlemleri:
Flash hafızaya PIC18F97J60 için bir kerede 64 byte yazılmaktadır. Veriler önce TABLAT saklayıcısı ile Holding Register denilen tutuculara yazılmakta ve yaz komutu ile birlikte HR tutucularındaki 64 byte blok halinde flasha yazılmaktadır.TABLAT saklayıcısı 8 bitlik olduğu için 64 adet HR saklayıcısı vardır. Hangi 64 bytelık bloğun yazılacağını TBLPTR saklayıcısının 20:6 aralığına yazmanız gerekmektedir. Yazma işlemlerinde TBLPTR saklayıcısının 5:0 arasındaki altı biti verinin TABLAT saklayıcısından hangi HR tutucusuna yazılacağını göstermektedir.
Ayrıca program hafızanın dayanıklılığını koruması için veri yazılacak hafıza hücresinin silme işlemleri arasında en fazla bir defa yazılması gerekmektedir. Aynı hücreye tekrar yazmanız gerekirse silme işlemi ile ilgili 1024 byte lık bloğu silmeniz gerekmektedir. Aşağıdaki şekilde yazma işleminin HR saklayıcıları ile nasıl gerçekleştirildiği gösterilmektedir.
/**************************************************************************
* Yazar :Barış Samancı, www.barissamanci.net
*
* Fonksiyon :void ROMVeriYukle (BYTE* pData, BYTE nLen);
*
* Açıklama :pData parametresinde adresi geçilen verileri,
* ikinci parametresinde geçilen uzunluk kadar
* flash hafızanın Holding Register Saklayicilarına
* yükler.
*
* Önşart :ROMAdresKur(DWORD adres) fonksiyonu ile adres
* önceden kurulmalıdır.
**************************************************************************/
void ROMVeriYukle (BYTE* pData, BYTE nLen)
{
BYTE i;
for (i=0; i<nLen; i++)
{
TABLAT = pData[i];
_asm
TBLWTPOSTINC
_endasm
}
}
/**************************************************************************
* Yazar :Barış Samancı, www.barissamanci.net
*
* Fonksiyon :void ROMVeriYaz ( void );
*
* Açıklama :HR saklayıcılarına ROMVeriYukle ileyazılan
* verileri flasha yazar
*
* Önşart :ROMVeriYukle ile flasha yazılacak verilerin HR
* saklayıcılarına yazılmış olması gerekmektedir.
**************************************************************************/
void ROMVeriYaz ( void )
{
EECON1bits.WREN = 1;
EECON1bits.FREE = 0;
INTCONbits.GIE = 0;
EECON2 = 0x55;
EECON2 = 0xaa;
EECON1bits.WR = 1;
INTCONbits.GIE = 1;
EECON1bits.WREN = 0;
}
Uygulama:
#define FLASH_ADRESI 0X001000
// FLASH_ADRESI adresine verimizi yükleyelim.
#pragma romdata FlashHafiza = FLASH_ADRESI
rom BYTE flash[1024]=={'b','a','r','i','s',' ','s','a','m','a','n','c','i',
'.','n','e','t','\0'};
#pragma romdata
void main(void)
{
BYTE ramhafiza[64];
BYTE str[15]={'m','e','r','h','a','b','a',' ',
'd','u','n','y','a','.','\0'};
//Flash hafızadaki verimizi printf ile basalım.
printf("1 ROM:%HS\r\n",flash);
//Flash hafızadaki verimizi 'ramhafiza' tamponuna okuyalım.
//'ramhafiza' tamponundaki verimizi printf ile basalım.
ROMAdresKur(FLASH_ADRESI);
ROMVeriOku (ramhafiza, 20);
printf("2 RAM:%s\r\n",ramhafiza);
//Flash hafizadaki verimizi silelim.
//Silinmiş olan bloğu printf ile basalım.
ROMAdresKur(FLASH_ADRESI);
ROMVeriSil();
printf("3 ROM:%HS\r\n",flash);
//Flash hafızaya str dizisini yazalım.
//Flash hafızadaki str dizisini printf ile basalım.
ROMAdresKur(FLASH_ADRESI);
ROMVeriYukle (str, 15);
ROMVeriYaz();
printf("4 ROM:%HS\r\n",flash);
while(1)
;
}
Sonuç:
Yukarıdaki kodda pragma satırları ile flash hafızada 1024 byte’lık bir alan ayrılmış ve varsayılan ilk değer olarak “baris samanci.net” değeri yüklenmiştir.Daha sonra bu alandan okuma, silme ve yazma işlemleri yapılmıştır. Kodların çıktısı aşağıdaki hyperterminal ekranında gözükmektedir. Bu ekrandan sonra işlemcimize reset attığımızda ekranda “baris samanci.net” yazan yerlerde merhaba dünya yazdığını göreceksiniz. Çünkü flash hafızadaki veriler enerji kesildiğinde korunmaktadırlar.Dolayısıyla yeniden başlatmalarda en son yüklediğiniz değerle karşılaşırsınız. Uygulamanızda saklamanız gereken değerleri bu şekilde saklayabilirsiniz.
MPLAB IDEsinden Program Memory penceresi ile mikrodenetleyicimize yüklediğimiz kodlara bir bakalım. Uygulamamızda kullandığımız 0X1000 adresine baktığımızda aşağıdaki manzara ile karşılaşırız. Görüldüğü gibi flash isimli dizimizi kod derlenirken flash program hafızasında istediğimiz adrese yerleştirdik ve mikrodenetleyicimize yükledik.
Anlatım PIC18F97J60 üzerinden yapılmıştır. Yazıda belirttiğim gibi bazı özellikler çipten çipe değişmektedir. Bu yüzden yazmış olduğum örnek kodların her işlemcide çalışacağı garanti değildir. Gerekli düzeltmeleri programcı yapmalıdır. Adres taşmaları kontrol edilmemiştir. Yazılan veya silinen bölgenin mikrodenetleyici üzerinde çalışmakta olan programa denk gelmesi sonucu oluşabilecek hasarlardan sistemi programlayan programcı sorumludur. Programcının hafıza bölgesini çok iyi irdeleyip adresleri ve göstericileri doğru bir şekilde kullanması gerekmektedir. Aksi halde flash hafızadaki programın üzerine yazıldığında sistem çalışmaz hale gelip maddi zararlara yol açabilir. Sorumluluk bu uygulama notundaki bilgileri kullanan programcıya aittir.Kodları türeterek byte, tamsayı, string yazan okuyan fonksiyonları kendiniz yazabilirsiniz. Uygulama notunu aşağıdaki linkten indirebilirsiniz.
PIC Flash Program Hafızasına Yazma / Silme / Okuma İşlemleri ( 300kb )