3 wire lcd ve macro kullanımı
|Merhaba, Bu uygulamızda 3 wire(tel) üzerinden lcd uygulamasını gerçekleştirirken asm içinde macronun nasıl kullanıldığını göreceğiz. Öncelikle neden 3 wire üzerinden lcd ile haberleşmeye gereksinim var sorusuna cevap vereyim? Şahsen mümkün olduğunca küçük boyutta işlemci (az sayıda porta sahip) kullanmaya çalışıyorum ve haberleşmeleri ise seri haberleşme seçiyorum ki en az pinli işlemci kullanabileyim. Bu şekilde kullanmanın daha çok avantajları ama dezavantajları da var.
Avantajları :
Port sayısı az olduğu için işlemci ucuz oluyor, pcb’de daha az yer kaplıyor ve pcb yaptırım ücreti azalıyor.
Belki diyebilirsiniz ki, işlemci küçük ama yerine başka bir IC kullanıyorsun bu maliyete ve pcb yerleşimine etki etmiyor mu? Evet başka bir IC kullanıyorum, 74HC595, fiyat açısından çok ucuz (paha açısında çok portlu işlemci ile karşılaştırılamaz bile) ve IC’yi smd kullandığım için yer sorunu olmuyor.
LCD’yi 4bit haberleşme ile bile sürersek işlemcinin en az 7 portunu harcamamız gerekir. 3 wire kullanarak 4 port avantaja geçiyoruz. Bu olay bazı durumlarda maliyetten de önemli oluyor. Hatta 74hc595 için kullanılan latch pini hariç, serial data ve clock uçlarınıda başka IC’ler ile haberleşirken kullanabilir.
Dezavantajı : Endüstriyel uygulamalarda, 74hc595 latch’li bir shift register olduğu için yogun gürültü altında latch pini etkilenebilir ve çıkışları farklı bir değere latch edebilir.Bunun önüne geçmek için pcb üzerinde ve elektronik malzeme olarak bazı önlemler alınmalıdır. 74hc595’in diğer güzel tarafı hem latch’li olması hemde 50Mhz’e kadar clock’landırılabilir olmasıdır.
Genelde çoğu uygulamamda PC ortamında şema çizmem (aslında bunun nedeni eagle kullanıyorum ve şematik editörünü beğenmiyorum), karakalem ile şemayı belirler sonra program içinde şemadaki kritik değerleri belirtirim. Pcb’yi ise doğrudan çizerim. Elektroniğe hakimseniz böylesi kolay oluyor.
(Resimdeki lcd boardu delikli pertenax üzerine kuruludur, ana board’dan bağımsız olması ve tüm geliştirme boardlarıyla birlikte kullanılması için. Elektronik pahalı meslek, bazen ucuz çözümler kullanmak gerekir)
Bu uygulamada en kritik şey, 74hc595’in çıkışlarının lcd’ye hangi sıra ile bağlanacağıdır.
Q7 Q6 Q5 Q4 Q3 Q2 Q1 Q0 74HC595 çıkışları,
D7 D6 D5 D4 E RW RS – LCD pinleri , Q0 çıkışına bağlı pin yok.
E=enable, RW=Read/write, RS=Register select
Aşağıda bağlatının yapıldığı bir picture var. Yukarıda anlatılanlara göre bağlantısı yapıldı ve extra olarak
plaket üzerinde 78L05 (LCD 5V ve backlight için) ve backlight bias direnci var.
Program içinde benim oluşturduğum assembly’de çok kullanışlı string’ler için bir macro var. Başka macrolar da var. Program içinde uygulamaları görülecektir. Macrolar sayesinde asm dilini biraz daha basitleştirmiş oluruz ve çok tekrar gerektiren ama birbirinden farklı durumlar için bir macro yeterli olmaktadır. Bizim yazmamız gereken kodları, bizim yerimize derleyici oluşturur.
Mesela aşağıdaki örnekte , LCD’nin 1 satırından ve 1 karakterinden başlayarak “satir1” adlı string’i yazdırır.
LCD_YAZI satir1,1,1
;========================================================== ; Aykut ULUSAN, Elektronik Mühendisi, IZMIR ;=========================================================== ; 3wireLCD : 74HC595 ile SCK,SDA ve LOAD ucu ile 3 wire üzerinden LCD sürülür. ;=========================================================== LIST P=16F877 INCLUDE "P16F877.INC" TITLE "3wire LCD" ERRORLEVEL -302,-207,-205 RADIX DEC ; Sayı tipini belirtmediklerimi desimal kullan __CONFIG 0x3F32 ; hs,code protects off,Wdt off,Pwrte on, boden off,wrt off, CBLOCK 20H LCD_DATA DATA_OUT ; 3 wire LCD için HC595_COUNT TABLE_REG COUNT1 COUNT2 COUNT3 ENDC ; LCD'yi 74HC595 ile sürmekteyim #DEFINE LCD_SDA PORTA,1 ; 74HC595'in SDA pinine #DEFINE LCD_SCK PORTA,2 ; 74HC595'in SCK pinine #DEFINE LCD_LOAD PORTA,0 ; 74HC595'in LOAD pinine #DEFINE CARRY_FLAG STATUS,0 ; Carry flag #DEFINE ZERO_FLAG STATUS,2 ; Zero flag DLY_MS MACRO MS ; 1-255 msn arasında delay sağlar(20Mhz'de) MOVLW MS CALL X_MSN ENDM DLY_10US MACRO US ; US'ye verilecek değer 1 - 255 ile 10usn ile 2550usn(2,5msn) delay sağlanır MOVLW US MOVWF COUNT1 CALL X_ON_USN ENDM BANK0 MACRO ; Bank0'a geç BCF STATUS,RP1 BCF STATUS,RP0 ENDM BANK1 MACRO ; Bank1'e geç BCF STATUS,RP1 BSF STATUS,RP0 ENDM MOVFF MACRO REG1,REG2 ; Register1'i register2'ye kopyalayan macro MOVF REG1,W MOVWF REG2 ENDM MOVLF MACRO L,F ; Literali file'a taşı MOVLW L MOVWF F ENDM ; LCD karekter adresleri için macro : ; LCD_ADRES 1,1 için ; 1 .satır 1.adrese gider ; LCD_ADRES 1,3 için ; 1 .satır 3.adrese gider ; LCD_ADRES 2,1 için ; 2 .satır 1.adrese gider LCD_ADRES MACRO SATIR,ADRES ; IF SATIR==1 MOVLW 7FH ; 7F+1=80H 1. satır adres başlangıcı ELSE MOVLW 0BFH ; 0BF+1=0C0H 2. satır adres başlangıcı ENDIF ADDLW ADRES ;80H veya 0C0H ile ADRES değerini toplar CALL LCD_KOMUT_YAZ ENDM ; YAZI : Ekrana yazdılacak yazı etiketi ; SATIR : LCD'nin satırı (1 veya 2) ; ADRES : LCD'nin adresi ( 1......16 ya kadar) LCD_YAZI MACRO YAZI,SATIR,ADRES ; LCD_ADRES SATIR,ADRES ; LCD'de ilgili satıra gitmesini sağlar LCD_STRING YAZI ENDM LCD_STRING MACRO STRING CLRF TABLE_REG MOVF TABLE_REG,W CALL STRING CALL LCD_STR ; bu altprogram, BTFSS ZERO_FLAG ; table'dan 0 geldiyse çık GOTO $-4 ; Call ile table'dan karekter alır. ENDM ORG 0 goto start ORG 4 GOTO start LCD_INIT_TABLE_1: ; LCD'yi hazırlamak için hex değerler tutulur MOVF TABLE_REG,W ADDWF PCL,F DT 30H,30H,20H,0 LCD_INIT_TABLE_2: ; Burada tüm bitler gönderilir (8 bit) (mesela önce 2 sonra 8 gönderilir) MOVF TABLE_REG,W ADDWF PCL,F DT 28H,08H,06H,03H,0CH,0 satir1: ADDWF PCL,F DT "MACRO KULLANIMI",0 satir2: ADDWF PCL,F DT "ve 3 WIRE LCD",0 start: CALL PORTLARI_HAZIRLA ; port ayarlarını yap CALL LCD_INIT ; LCD'yi hazırla call YAZ_BAKALIM goto $ ;//////////////////////////////////////////////////////////////////////////////////////////////////// YAZ_BAKALIM: LCD_YAZI satir1,1,1 ; 1. satır, 1 karakterden itibaren LCD_YAZI satir2,2,2 ; 2. satır, 2 karakterden itibaren RETURN LCD_INIT: ; BSF LCD_ON ; lcd'nin power'ı ve backlight'ı iki tr. ile on yapılır DLY_MS .100 ; LCD'nin hazırlanması için gerekli süre (en az 40msn) MOVLW 30H ; CALL WRITE_595 DLY_MS .1 MOVLW 38H CALL WRITE_595 DLY_MS .1 ; 1ms MOVLW 30H ; CALL WRITE_595 DLY_MS .10 ; 10ms delay CALL CTRL_BYTE CALL LCD_SET_UP RETURN ; ******************************************************************************************************* LCD_SET_UP: CLRF TABLE_REG LCD_KOMUT: CALL LCD_INIT_TABLE_2 IORLW 0 BTFSC ZERO_FLAG ; Table'dan W=0 gelirse geri dön RETURN CALL LCD_KOMUT_YAZ INCF TABLE_REG,F GOTO LCD_KOMUT ; ******************************************************************************************************* LCD_CLR_LINE_1: ; 1. satırı sil MOVLW 80H ; 1. satırın ilk adresini gönder CALL LCD_KOMUT_YAZ ; CALL LCD_CLR_LINE ; 16 hücreyi de sil MOVLW 80H CALL LCD_KOMUT_YAZ ; 1. satırın ilk adresini gönder RETURN LCD_CLR_LINE_2: ; 2. satırı sil MOVLW 0C0H ; 2. satırın ilk adresini gönder CALL LCD_KOMUT_YAZ ; CALL LCD_CLR_LINE ; 16 hücreyi de sil MOVLW 0C0H CALL LCD_KOMUT_YAZ ; 2. satırın ilk adresini gönder RETURN LCD_CLR_LINE: CLRF TABLE_REG LINE_SIL: MOVLW " " ; Working'e boşluk yükle CALL LCD_DATA_YAZ INCF TABLE_REG,F BTFSS TABLE_REG,4 GOTO LINE_SIL RETURN LCD_CLEAR: ; LCD'yi komple temizler ve 2.satır başında bekler CALL LCD_CLR_LINE_1 ; 1. satırı sil CALL LCD_CLR_LINE_2 ; 2. satırı sil RETURN ; ******************************************************************************************************* CTRL_BYTE: ; 4 bit komutları göndermek için kullanılır CLRF TABLE_REG CTRL_BYTE_AL: CALL LCD_INIT_TABLE_1 ; IORLW 0 ; 0 gelirse gönderme bitmiştir BTFSC ZERO_FLAG ; Table'dan W=0 gelirse geri dön RETURN CALL CONTROL_BYTE_GONDER INCF TABLE_REG,F GOTO CTRL_BYTE_AL ; ******************************************************************************************************* CONTROL_BYTE_GONDER: MOVWF LCD_DATA ; komutu registere taşı MOVF LCD_DATA,W ; Sadece ilk 4 bit gönder, Enable=0 ANDLW 0F0H ; D7-D4 maskele CALL WRITE_595 MOVF LCD_DATA,W ; Sadece ilk 4 bit gönder, Enable=0 ANDLW 0F0H ; D7-D4 maskele IORLW 08H ; E=1 CALL WRITE_595 MOVF LCD_DATA,W ; Sadece ilk 4 bit gönder, Enable=0 ANDLW 0F0H ; D7-D4 maskele CALL WRITE_595 DLY_MS .1 RETURN ; ******************************************************************************************************* ; LCD'ye komut gönderen alt program, LCD'ye E'nin düşen kenarı ile yazılır. ; 4bitlik nibble datalar ve RS ve R/W ucları , E=0 sonra E=1 daha sonra E=0 için gönderilir LCD_KOMUT_YAZ: MOVWF LCD_DATA ; komutu registere taşı MOVF LCD_DATA,W ; yine aynı datanın ANDLW 0F0H ; D7-D4 bitlerini maskele IORLW 08H ; E=1 RS=R/W=0 ve Datanın high order byte'nı LCD ye gönder CALL WRITE_595 MOVF LCD_DATA,W ; ANDLW 0F0H ; D7-D4 maskele CALL WRITE_595 ; RS,R/W ve E ucu low, Datanın high order byte'nı LCD ye gönder SWAPF LCD_DATA,W ; LSB için nibble'ları değiştir ANDLW 0F0H ; D7-D4 maskele IORLW 08H ; E=1 RW=0 RS=0 x CALL WRITE_595 ; RS,R/W ve E=1, Datanın low order byte'nı LCD ye gönder SWAPF LCD_DATA,W ; LSB için nibble'ları değiştir ANDLW 0F0H ; D7-D4 maskele CALL WRITE_595 ; RS,R/W ve E ucu low, Datanın low order byte'nı LCD ye gönder DLY_10US .50 ; LCD'nin komutu çalıştırması için 500us RETURN ; ******************************************************************************************************* ; LCD'ye karekter gönderen alt program ve karekterin hazır olması için gerekli delay'i ; kapsar LCD_DATA_YAZ: MOVWF LCD_DATA ; komutu registere taşı MOVF LCD_DATA,W ; Ilk önce msb gönderilir ANDLW 0F0H ; D7-D4 maskele IORLW 0AH ; E=1 RW=0 RS=1 x CALL WRITE_595 ; LCD ye gönder MOVF LCD_DATA,W ; Ilk önce msb gönderilir ANDLW 0F0H ; D7-D4 maskele IORLW 02H ; E=0 RW=0 RS=1 x CALL WRITE_595 ; LCD ye gönder, SWAPF LCD_DATA,W ; lsb için nibble'ları değiştir ANDLW 0F0H ; D7-D4 maskele IORLW 0AH ; E=1 RW=0 RS=1 x CALL WRITE_595 ; LCD ye gönder, LCD enable SWAPF LCD_DATA,W ; lsb için nibble'ları değiştir ANDLW 0F0H ; D7-D4 maskele IORLW 02H ; E=0 RW=0 RS=1 x CALL WRITE_595 ; LCD ye gönder, LCD enable DLY_10US .10 ; LCD'nin datayı yazabilmesi için en az 50us RETURN ; ******************************************************************************************************* ; DATA_OUT içindeki 8 bitlik veri 595'e yazılır ve Latch edilir ; Böylece LCD'ye komut veya karekter ulaşır WRITE_595: MOVWF DATA_OUT ; DATA_OUT registerinin içeriği gönderilir MOVLW .8 MOVWF HC595_COUNT ; 8 Bit için sayıcı _595_BIT: BCF LCD_SDA BTFSC DATA_OUT,7 BSF LCD_SDA BSF LCD_SCK ; Shift BCF LCD_SCK RLF DATA_OUT,F DECFSZ HC595_COUNT,F GOTO _595_BIT BSF LCD_LOAD ; Latch et BCF LCD_LOAD RETURN ; ******************************************************************************************************* LCD_STR: ; LCD_STRING macrosunda kullanılır, tüm table'lar için geçerlidir. IORLW 0 ; 0 gelirse gönderme bitmiştir BTFSC ZERO_FLAG ; Table'dan W=0 gelirse geri dön GOTO $+3 ; Yazma işlemi bitmiştir , zero_flag=1'dir CALL LCD_DATA_YAZ INCF TABLE_REG,F RETURN BLINK_ON: ; Blink Aç MOVLW 0DH GOTO $+2 BLINK_OFF: ; Blink Kapat MOVLW 0CH CALL LCD_KOMUT_YAZ RETURN ; delay routinleri X_MSN: ; COUNT3 x 1msn delay sağlar ; CLRWDT ; Her msn'de WDT'yi sil MOVWF COUNT3 CALL ONE_MSN DECFSZ COUNT3,F GOTO $-2 RETURN ONE_MSN: MOVLF .100,COUNT1 ; 1msn delay için COUNT1'e 100 yükle ; Count1=1 iken toplam 11usn delay ; count1=100 iken toplam 1ms yani 1000usn olamalı(100x9,6usn+100x0,2usn+99x0,4usn+1,2usn)=960+20+40+1,2=1021usn ; count1=25 iken 250usn olmalı (25x9,6usn+25x0,2usn+24x0,4usn+1,2usn)=240+5+9,6+1,2=256usn X_ON_USN: ; Buraya kadar 800ns CALL ON_USN ; Count1x9,6usn DECFSZ COUNT1,F ; COUNT1x200ns GOTO $-2 ; (COUNT1-1)x400ns RETURN ; 400ns ; 20Mhz'de tam 9,6 usn delay sağlar ON_USN: ; 400ns MOVLF .11,COUNT2 ; 400ns NOP ; 11x200ns DECFSZ COUNT2,F ; 11x200ns GOTO $-2 ; 10x400ns RETURN ; 400ns PORTLARI_HAZIRLA: BANK0 ; Bank0'a geç CLRF INTCON ; Interupt Control Registerlerini clear et CLRF PIR1 ; Interrupt Flagleri temizle CLRF PORTA ; Latch'leri temizle CLRF PORTB CLRF PORTC BANK1 ; Bank1'e geç MOVLF 07H,ADCON1 ; PortA Digital I/O MOVLF 00H,OPTION_REG CLRF TRISA ; PortA çıkış BANK0 ; Bank0'a geç RETURN END
Uygula ile ilgili aşağıya bir kaç resim ekledim. Dediğim gibi bu tür uygulamaları normalde 28pin’e kadar olan uC’ler de kullanıyorum(tabii ki C versiyonunu) ama aşağıya çalışırlığını göstermek için benim yaptığım PIC184520/PIC16F877A geliştirme boardunu kullandım. Resimlerden de görüldüğü gibi, LCD ile 3 tel üzerinden haberleşiliyor. LCD kart beslemesi için 7V5-8V arası 78L05 için en iyi değerdir.