31 Temmuz 2015 Cuma

SDCC ile LCD Uygulaması (16F877A) Ders 10







Bu uygulamamızda 5 tane kod dosyamız var.  Daha doğrusu kodların okunurluğunu artırmak ve sizi olası hatalardan korumak maksadıyla kod dosyalarımızı ayrı ayrı dosyalarda oluşturduk. Bu daha profesyonel kod yazmanıza imkân sağlayacaktır.

Delay.h” başlık dosyamız

// 16F877A için delay fonksiyonu tanımlaması
// Bu ms_bekle 168877a 4 Mhz frekansı için ayarlanmış bir fonksiyondur

typedef unsigned short int uint16_t;

void ms_delay(uint16_t milisaniye)
{
uint16_t i,j;
for (i = 0; i < milisaniye; i++)
{
// Döngüde optimizasyon için bu asm komutu eklenmiştir.
for (j=0; j < 46; j++)
__asm nop __endasm;
}
}

// Bu fonksiyon her döngüde 20 us (mikrosaniye) gecikme sağlar

void us_delay_20(uint16_t usDegerx20)
{
uint16_t i;
for (i = 0; i < usDegerx20; i++) //her döngü adımı yaklaşık 20 us
{
// Döngüde optimizasyon için bu asm komutu eklenmiştir.
__asm nop __endasm;
}
}



bizim_lcd.h” başlık dosyamız

/* Durali Kiraz 2015
* www.duralikiraz.blogspot.com
*
* D4,D5,D6,D7 pinlerini kullanarak 4 bit iletişim kullanır.
* Cursor kapalıdır.
* RW kullanılmadığı için direk toprağa bağlanabilir.
* 2x16 LCD Kullanım Klavuzu
*
* lcd_baslat(); ile LCD'nin ilk ayarlarını yap
* lcd_ekranisil(); ile LCD'yi sil
* lcd_yaz("deneme"); Şeklinde yazı yazdır.
* veri_yolla('F'); Şeklinde tek ascii kodu yazdır.
* lcd_konumlanxy(1,13); Şeklinde LCD'nin istenilen yerine git.
*
*
*/


#define rs RC0 //Pin tanımlamaları
#define rw RC1
#define e RC2
#define lcd_port PORTB


/* LCD'de kullanilan komutlarin tanimlamasi*/
#define Sil 1 // Ekrani temizler
#define BasaDon 2 // Imleci sol üst köseye getirir
#define SolaYaz 4 // Imlecin belirttigi adres azalarak gider
#define SagaYaz 6 // Imlecin belirttigi adres artarak gider
#define ImlecGizle 12 // Göstergeyi ac, kursor görünmesin
#define ImlecAltta 14 // Yanip sönen blok kursor
#define ImlecYanSon 15 // Yanip sönen blok kursor
#define ImleciGeri 16 // Kursoru bir karakter geri kaydır
#define KaydirSaga 24 // Göstergeyi bir karakter sağa kaydır
#define KaydirSola 28 // Göstergeyi bir karakter sola kaydır
#define EkraniKapat 8 // Göstergeyi kapat (veriler silinmez)
#define BirinciSatir 128 // LCD'nin ilk satir başlangıç adresi
// (DDRAM adres)
#define IkinciSatir 192 // İkinci satirin başlangıç adresi
#define KarakUretAdres 64 // Karakter üreteci adresini belirle
// (CGRAM adres)

/* LCD'de Kullanilan Fonksiyon Seçimi */
#define CiftSatir8Bit 56 // 8 bit ara birim, 2 satir, 5*7 piksel
#define TekSatir8Bit 48 // 8 bit ara birim, 1 satir, 5*7 piksel
#define CiftSatir4Bit 40 // 4 bit ara birim, 2 satir, 5*7 piksel
#define TekSatir4Bit 32 // 4 bit ara birim, 1 satir, 5*7 piksel


extern void veri_yolla(unsigned char);
extern void lcd_ekranisil(void);
extern void lcd_yaz(const char *s);
extern void lcd_konumlanxy(unsigned char x, unsigned char y);
extern void lcd_baslat(void);
extern void lcd_komut(unsigned char c);



bizim_lcd.c kod dosyası

/* Durali Kiraz 2015
* www.duralikiraz.blogspot.com
*/

#include <pic14regs.h> //mikrodenetleyici hazır tanımları alınıyor
#include "bizim_lcd.h" // lcd.h dosyası tanımlanıp, değerler alınıyor
#include "delay.h" // Gecikme fonksiyonu tanımlanıyor


void lcd_busy(void)
{
   us_delay_20(12); //DelayUs(250);
}
void lcd_komut(unsigned char c) // Komut gönderme fonksiyonu
{
   rw=0; // LCD'ye yazma yapılacak
   rs=0; // LCD'ye komut gönderilecek
   e=1; // Düşen kenar tetikleme olduğu için E önce 1
   lcd_port = ( c & 0xF0 ); // Yüksek değerlikli bitler gönderiliyor
   e=0; // E, 0 yapılıyor
   lcd_busy(); //Belirli süre bekleniyor
   e=1; // E, 1 yapılıyor
   lcd_port = ( (c & 0x0F)<<4 ); // Düşük değerlikli bitler gönderiliyor
   e=0; // E, 0 yapılıyor
   lcd_busy(); // Belirli bir süre bekleniyor
}

void veri_yolla(unsigned char c)
{
  rw=0;
  rs=1; // Komut yolladan tek farkı, RS'nin 1 olması
  e=1;
  lcd_port = ( c & 0xF0 );
  e=0;
  lcd_busy();
  e=1;
  lcd_port = ( (c & 0x0F)<<4 );
  e=0;
  lcd_busy();
}

void lcd_ekranisil(void) // LCD siliniyor
{
  lcd_komut(0x1);
  ms_delay(2);
}

void lcd_yaz(const char * s) // LCD'ye string ifade gönderiliyor
{
  lcd_busy();
  while(*s)
    veri_yolla(*s++);
}

void lcd_konumlanxy(unsigned char x,unsigned char y) //LCD'nin belli bir
//bölgesine gidiliyor
{
  if(x==1)
    lcd_komut(0x80+((y-1)%16));
  else
    lcd_komut(0xC0+((y-1)%16));
}

void lcd_baslat()        // LCD ilk yükleme ayarları yapılıyor
{
    rs = 0;
    e = 0;
    rw = 0;
   
    ms_delay(15);
    e=1;
    lcd_komut(0x02);
    ms_delay(2);

    lcd_komut(CiftSatir4Bit);
    lcd_komut(SagaYaz);
    lcd_komut(ImlecGizle);
    lcd_ekranisil();
    lcd_komut(BirinciSatir);
}



lcdcalisma.c ana dosyamız

// Durali Kiraz 2015
// PIC16F877A için hazırlanmıştır.

#include "ayarlar.h"
#include "bizim_lcd.h" // LCD kütüphanesi tanımlanıyor


void main(void) // Ana fonksiyon alanı
{
  TRISB=0x00; // LCD'ye bağlı portlar çıkış yapılıyor
  TRISC=0x00;
  PORTB=0x00;
  PORTC=0x00;

  lcd_baslat(); // LCD ilk ayarları yapılıyor

  lcd_yaz(" DURALi KiRAZ"); // İlk satıra isim yazılıyor
  lcd_konumlanxy(2,1); // İkinci satıra geçiliyor
  lcd_yaz("1234567890"); // Rakamlar yazılıyor

  for(;;); //Sonsuz döngüye giriliyor

}


“ayarlar.h” dosyası

// Copyright (C) 2015 Durali Kiraz
// PIC16F877A için hazırlandı.

#include <pic14regs.h>

typedef unsigned short int uint16_t;

//sigorta tanımlarımızı yapıyoruz.
__code uint16_t __at _CONFIG __configword =
    _PWRTE_OFF &&
    _CP_OFF &&
    _FOSC_XT &&
    _WDT_OFF &&
    _BOREN_OFF &&
    _LVP_OFF ;




Bu dersimizde elektronik projelerimizin kısmen vazgeçilmezi sayılabilecek 16 sûtûn 2 satır Lcd display aygıtını nasıl sdcc ile sürebileceğimizi öğrenmeye çalışacağız. Bilindiği gibi devremizde oluşan bazı sonuçların gösterimi ve bazı verilen girilmesi gerektiğinde Lcd display kullanmak büyük bir avantaj sağlar. Ayrıca projelerimizin görselliğini de artırır.

Öncelikle lcd mikrodenetleyiciden bağımsız bir harici aygıttır. Bu yüzden şahsına münhasır bir kontrol mekanizması bulunmaktadır. Bu aygıtın nasıl kontrol edileceği datasheet'i içerisinde geniş bir şekilde açıklanmıştır. Dilerseniz kendi ihtiyacınıza uygun “sürücü” yazabilirsiniz. Biz burada kod benzerliğinden de istifade ederek Fırat Deveci'nin hazırlamış olduğu hitec c e-kitabında hazırlamış olduğu lcd sürücünü çok az bir değişiklikle burada kullanmak üzere kendimiz için hazırladık. Bu lcd sürücü dosyası bundan sonraki projelerimizde kullanacağımız lcd sürücü dosyamız olacaktır. Bu lcd sürücü dosyaları aygıtın kontrolünde çok kolaylaştırmaktadır. Bu dosyalarla ilgili daha fazla detayı kod üzerinde bulabilirsiniz.

Delay.h isimli dosyamızda ise programımız için oluşturduğumuz yaklaşık değerleri yakalayan bir gecikme fonksiyonlarını tanımladık. Programımızda 4 Mhz frekans ve PIC16F877A işlemcimiz için bunları kullanacağız. Anlayacağınız üzere sadece bu işlemci ve frekansta yaklaşık doğru sonucu elde edebileceğimiz fonksiyonlardır. us_delay_20 isimli fonksiyon her döngüde 20~22 mikro saniye zaman beklemesi sağlayan bir fonksiyondur. Açıkçası sadece bu projedeki geçici ihtiyacı karşılayacaktır.

ayarlar.cdosyamız önceki derslerde ana dosya içerisinde tanımladığımız sigorta ayarları vs. ek kodları tanımlayabileceğimiz bir dosyadır. Bundan sonra PIC16F877A mikrodenetleyicisi için bir proje oluşturacağımız zaman bu ayarları her seferinde tekrar yazmak yerine sadece başlık dosyamızın ismini belirttiğimizde çok kolaylık sağlamış olacağız.

“lcdcalisma.c” isimli ana dosyamızı inceleyecek olursak

#include "ayarlar.h"
#include "bizim_lcd.h" // LCD kütüphanesi tanımlanıyor

satırlarıyla kendimize özel oluşturduğumuz başlık dosyalarını projemize dahil ediyoruz. Main fonksiyonu içinde

TRISB=0x00; // LCD'ye bağlı portlar çıkış yapılıyor
TRISC=0x00;
PORTB=0x00;
PORTC=0x00;
satırları B portunu çıkış olarak ayarlayıp diğer tüm portlara da sıfır atamaktadır. Sonrasındaki

lcd_baslat(); // LCD ilk ayarları yapılıyor


fonksiyonu “bizim_lcd.c” sürücü dosyamızda gerçeklenmiş bir fonksiyondur. Bununla lcd display'imiz çalışmak üzere bir takım kodları çalıştırarak kendini karakterleri göstermeye hazırlar. Daha doğrusu kullanıma hazırlar.

lcd_yaz(" DURALi KiRAZ"); // İlk satıra isim yazılıyor

satırında ise lcd ye bir karakter serisini yazmasını söylüyoruz. Yine bu fonksiyonun kodunuda incelediğinizde bir hafıza işaretçisini parametre aldığını görebilirsiniz. Peki 2. satır 1. sutuna konumlanmak istersek? Aşağıdaki satırda bu işi yapıyor.

lcd_konumlanxy(2,1); // İkinci satıra geçiliyor

sonrasında da

lcd_yaz("1234567890"); // Rakamlar yazılıyor

for(;;); //Sonsuz döngüye giriliyor

tekrar lcd ye bir karakter serisini yazıdırıyoruz ve son olarak for döngüsü ile programı sonsuz döngüye sokuyoruz. Diğer lcd komutlarımızıda sürücü dosyasını inceleyerek kendiniz uygulayabilirisiniz.

Dikkatinizi çektiyse bu derste ilk defa bir aygıt için sürücü dosyası kullandık. Normal şartlarda komut satırında derleme yapmaya çalıştığımda aşağıdaki hata mesajlarını döndürerek kodun object dosyasını oluşturduğunu fakat derlemeyi tamamlamadığını gördüm.

sdcc --use-non-free -mpic14 -p16f877a lcdcalisma.c
message: using default linker script "/usr/share/gputils/lkr/16f877a.lkr"
error: missing definition for symbol "_lcd_yaz", required by "lcdcalisma.o"
error: missing definition for symbol "_lcd_konumlanxy", required by "lcdcalisma.o"
error: missing definition for symbol "_lcd_baslat", required by "lcdcalisma.o"
Bunun sebebini araştırdığımda öncelikle harici sürücümüzün object dosyasının derlenmesi, daha sonra ana dosyamızın object dosyasının oluşturulup ikisinin birleştirme (linker) yapılmasının gerektiğini öğrendim. Bu kısım çok önemli şayet aşağıda anlatacağım adımları uygulayarak derleme işlemlerini gerçekleştirmezseniz hex dosyasını oluşturamazsınız. İlk olarak Linux üzerinde terminal/konsol penceresini, Windows kullanıyorsanız cmd.exe yi çalıştırmalısınız. Ardından sırayla şu iki satırı yazıp Enter lıyorsunuz. (Ben ubuntuya göre yazıyorum burada.)

sdcc --use-non-free -mpic14 -p16f877a -I path/to/pic14/includes -c bizim_lcd.c

sdcc --use-non-free -mpic14 -p16f877a -I path/to/pic14/includes -L path/to/pic14/libraries -Wl, lcdcalisma.c bizim_lcd.o



-I parametresi ; “Add to the include (*.h) path, as in -Ipath” şeklinde bir açıklamaya sahiptir. Yani bu parametreden sonra yazılan path parametresinde geçen başlık dosyaları derlemeye dahil edilecektir.

-c parametresi ; “--compile-only Compile and assemble, but do not link” şeklinde bir açıklamaya sahiptir. Bu parametreden sonra yazılan kod dosyası sadece derlensin ve linker (kod bağlama) işlemi yapılmasın anlamını taşır.

Bu şekilde ilk satır konsolda çalıştırıldığında normal şartlarda hiçbir mesaj vermeden sadece derlemeyi yapacaktır. Eğer hatalı kod durumu varsa elbette derleme hataları görüntülenecektir.

İkinci satırı inceleyecek olursak ;

-L parametresi; “Add the next field to the library search path” açıklamasına sahiptir. Yani kütüphane arama yoluna sonraki sahayıda ekle anlamına gelir. Önceki saha -I den sonra yazılan path bilgisidir. Sonraki arama yolu ise -L parametresiden sonra belirtilmiş path bilgisidir.

-W parametresi “Pass through options to the pre-processor (p), assembler (a) or linker (l)” açıklamasına sahiptir. Ön işlemcinin assembler veya linker işlemini pas geçmesi şeklinde yorumlanabilir.

-WI şeklinde yazıldığında parametre -I ile dahil edilmiş include dosyalarımızı linker işleminin pas geçilmesini sağlar.

Dolayısıyla ikinci satırdaki komutumuz ilk satırda oluşturulmuş lcd sürücünün object dosyası ile sonra oluşturulan ana dosyanın object dosyasınıda linker işlemine dahil ederek derlemeyi tamamlar ve *.hex dosyasını oluşturur. Bir sonraki dersimizde görüşmek üzere hoşçakalın.

duralikiraz.blogspot.com
31 Temmuz 2015

Muvaffakiyet Allahtandır”


Kaynaklar :
1 - Fırat Deveci Hi-tech C ile PIC programlama e-kitabı.
2- http://sourceforge.net/p/sdcc/discussion/1865/thread/e8062c7b/