Zdroj: http://prochazka.clanweb.eu/index.php?a=pic/lcd_4bit  •  Vydáno: 12.1.2008 16:17  •  Autor: hacesoft

Čtyřbitová komunikace LCD display s radičem HD44780 a mikrořadiče PIC16Fxx

icons pic Zdrojový kód pro PIC mikrokontrolér a LCD display s radičem HD44780. A přidání dalšího zdrojového kódu pro PIC16F877A, tentokrát napsaný v céčku. Jako kompilátor céčka je použit HI-TECH PICC který se dá jako demo stáhnout z: www.htsoft.com/ demo je u některých PICu omezena jen na 2KB kódu.

lcd2
lcd1
vyvojova_deska1

Podrobnější informace o LCD display s řadičem HD44780 najdete na: www.hw.cz/docs/lcd_iq_displaye/lcd_iq_dip.html
, ouwehand.net/~peter/lcd/lcd.shtml nebo www.cmail.cz/doveda/lcd/

Katalogový list lze stáhnout z: www.cmail.cz/doveda/lcd/hd44780u.pdf

První kód je napsán v assembleru ve vývojovém prostředí MPLAB pro jednočipový mikrořadič PIC16F84. Druhý program pro ovládání LCD displaye s řadičem HD44780 je taktéž napsán ve stejném vývojovém prostředí ale je použit jazyk C. Jednočipový mikrořadič je použit PIC16F877A. Jako kompilátor céčka je použit HI-TECH PICC, který se dá jako demo stáhnout z: www.htsoft.com demo je u některých PICu omezena jen na 2KB kódu.

   Zdrojové kódy pro PIC16F877A v céčku si můžete stáhnout z: Vyvojova deska PIC + XILINX zdrojak pro PIC



Kód v assembleru...

PIC16 code
; Zdroják pro MPLAB
; ; Čtyřbitová komunikace LCD display s radičem HD44780 a mikrořadiče PIC16F84.
;
; Dne 19.5.2001
;
; Pavel Procházka
; hacesoft@mujmail.cz
; www.prochazka.zde.cz

  #include "P16F84.INC"

  list p=16F84, f=INHX8M, r=DEC

; Z portu B je volný pouze bit 0. Ostatní vývody portu B používá LCD display.
;Port A je celý volný.
;K ovládání Portu B bitu 0 používejte instrukce BCF PORTB,0 a BSF PORTB,0.
;K nastavení tohoto bitu jako vstupní/výstupní použijte následující sekvenci:
; BCF nebo BSF reg_trisb,0
;       MOVf    reg_trisb,w
;       TRIS    PORTB
; Za podmínky že poběží v přerušení.
; Pokud nebudete používat přerušení stačí, aby bity co používá LCD display
;byli nastavenu jako výstupní. Ale musíte konfiguraci zapsat i do registru
;'reg_trisb' jinak po provedení jakéhokoliv příkazu s LCD displayem bude
;bit 0 porty B nastaven jako výstupní (základní nastavení). Pokud Vám
;bude stačit, aby bit 0 portu B byl jen výstupní, tak nic nemusíte
;konfigurovat.
; S portem A můžete nakládat jakkoliv.
;
; Zapojeni LCD displae:  vývod PICu    vývod LCD
;        PORTB 1 RS - vyber reg.
;        PORTB 2         RW - read/write
;        PORTB 3         E - enable
;        PORTB 4         DB4
;        PORTB 5         DB5
;        PORTB 6         DB6
;        PORTB 7         DB7
; Vývody DB0 až DB3 z LCD displaye připojit na 0V.


LCD_TEMP  equ 0x020     ; hodnota, která se ma poslat do LCD
reg_trisb equ 0x021     ; hodnota, která se vyšle na TRISB

DELAY     equ 0x022     ; Used in DELAYxxx routines
X_DELAY   equ 0x023     ; Used in X_DELAYxxx routines

#define   e   PORTB,3
#define   rw  PORTB,2
#define   rs  PORTB,1


  start ORG 0x0000
DEMO
  call  LCDINIT ;inicializace LCD display

        movlw   35h
        call    LCDPUTCHAR

        movlw   36h
   call LCDPUTCHAR

        movlw   37h
        call    LCDPUTCHAR

        movlw   38h
        call    LCDPUTCHAR

DEMO1   goto DEMO1

; inicializace LCD displae.
LCDINIT
        clrf    reg_trisb
   MOVLW 0x01E
   CALL X_DELAY500 ; 30 * 0.5mS = 15mS

;***************** Nastaveni 8-bitové komunikace ***************************

        MOVLW 0x030 ; 8-bit-interface
   call SENDPRIKAZ

;*************** Nastaveni 4-bitové komunikace *****************************

        MOVLW 0x020 ; 4-bit-interface
        call    SENDPRIKAZ

;***************************************************************************
        MOVLW 0x028 ; 4-bit-interface, 2-lines
        CALL LCDPUTCMD
        MOVLW 0x008 ; disp.off, curs.off, no-blink
        CALL LCDPUTCMD
        MOVLW 0x001      ;lcd smazat
        CALL LCDPUTCMD
        MOVLW 0x00C ; disp.on, curs.off
        CALL LCDPUTCMD
        MOVLW 0x006      ; Inkrementace, normální mód.
        CALL LCDPUTCMD
        RETURN

SENDPRIKAZ
        CALL LCDBUSY     ; Wait for LCD to be ready
        BCF rw
        BCF rs
        BSF e
        call    Zapis_w  ; Zápis reg. W na PortB.
        BCF e
        return

; Čeká až Display dodělá příkaz. Obsah reg. W se touto rutinou nemění!
LCDBUSY
        movwf LCD_TEMP   ; Ulož reg. W do LCD_TEMP
        movf    reg_trisb,w
        andlw   b'00001111'
        iorlw   b'11110000'      ; Byty 4 až 7 brány B jsou vstupní.
        movwf   reg_trisb
        tris    PORTB
lcd_busy
        BCF rs
        BSF rw
        BSF e
        movf PORTB,w ; Read busy flag + DDram address
        BCF e
        BSF e
        BCF e
        andlw b'10000000'        ; Test na BF příznak.
        BTFSS STATUS,Z
        goto lcd_busy
        bcf rw
        movf    reg_trisb,w
        andlw   b'00001111' ; Byty 4 az 7 brány B jsou výstupní.
        movwf   reg_trisb
        tris    PORTB
        movf LCD_TEMP,w  ; Obnov reg. W
        return

; Pošle znak na Display.
LCDPUTCHAR
        CALL LCDBUSY ; Wait for LCD to be ready
        BCF rw
        BSF rs
        BSF e
        call    Zapis_w  ; Zapíše reg. W na PortB.
        BCF e

        BSF e
        swapf   LCD_TEMP,f
        call    Zapis_w  ; Zapíše reg. W na PortB.
        BCF e
        RETURN

; Pošle příkaz na Display.
LCDPUTCMD
        call SENDPRIKAZ
        BSF e
        swapf   LCD_TEMP,f
        call    Zapis_w  ; Zapise reg. W na PortB.
        BCF e
   return

; Prekopiruje obsah reg. LED_TEMP bity 4 až 7 na PORTB bity 4 až 7.
Zápis_w
        movlw   b'00001111'
        andwf   PORTB,f
; Vynuluje bity 4 az 7 portub, výsledek uloží do portb.
        movlw   b'11110000'
        andwf   LCD_TEMP,w     
; Vynuluje bity 0 az 3 lcd_temp, výsledek uloží do w.
        iorwf   PORTB,f
; OR mezi w a potrb, výsledek uloží do portb.
        return

;*********************************** a 500uS delay @ 4MHz X-tal
DELAY500
        MOVLW D'165' ; +1 1 cycle
        MOVWF DELAY ; +2 1 cycle
DELAY500_LOOP
        DECFSZ DELAY, F ; step1 1 cycle
        GOTO DELAY500_LOOP ; step2 2 cycles
DELAY500_END
        RETURN ; +3 2 cycles

;*********************************** a delay of 'W' * 500mS
X_DELAY500
        MOVWF X_DELAY ; +1 1 cycle
X_DELAY500_LOOP
        CALL DELAY500 ; step1 wait 500uSec
        DECFSZ X_DELAY, F ; step2 1 cycle
        GOTO X_DELAY500_LOOP ; step3 2 cycles
X_DELAY500_END
        RETURN ; +2 2 cycles

        end


Kód v Céčku...

PIC16 code
//DEVICE PIC16F877A
//hacesoft
//www.prochazka.zde.cz
//6.11.2005
//Ovládaní LCD DISPLAYe ve vývojové desce PIC + XILINX
//Která se nachází na www.prochazka.zde.cz

//Fyzické zapojeni LCD k PICu 16F877A
//RE0 ...RS -> vyber reg.
//RE1 ...RW -> read/write
//RB3 ...EN1 -> ENABLE LCD
//RB1 ...DB4
//RB2 ...DB5
//RB4 ...DB6
//RB5 ...DB7

#include "pic.h"
#include "pic168xa.h"
#include "LCD_vyvojova_deska.h"
#define XTAL_FREQ       20MHZ
#include "delay.c"
#include "stdio.h"
#include "ctype.h"
#include "string.h"

__CONFIG (HS & WDTDIS & BORDIS & LVPDIS); //Konfiguracni slovo
__IDLOC (1); //verze SW
#define BF_FLAG (false); //FALSE=Cekaci smyska. TRUE=Cekani na prinzak BF z displye.

static bit      EN      @ ((unsigned)&PORTE*8+2);
static bit      LCD_RW  @ ((unsigned)&PORTE*8+1);
static bit      LCD_RS  @ ((unsigned)&PORTE*8+0);
static bit      LCD_EN  @ ((unsigned)&PORTB*8+3);
static bit      LCD_BF  @ ((unsigned)&PORTB*8+5);

static bit      DB4     @ ((unsigned)&PORTB*8+1);
static bit      DB5     @ ((unsigned)&PORTB*8+2);
static bit      DB6     @ ((unsigned)&PORTB*8+4);
static bit      DB7     @ ((unsigned)&PORTB*8+5);

unsigned char lcd_data = 0;

//----------------------------------------------------------------
void lcd_busy (void){
#if _BF_FLAG==true //Nektere displaye neposilaji informaci o dokonceni
 TRISB1 = 1; //Nastavani RB1 na vstupni PIN
 TRISB2 = 1; //Nastavani RB2 na vstupni PIN
 TRISB4 = 1; //Nastavani RB4 na vstupni PIN
 TRISB5 = 1; //Nastavani RB5 na vstupni PIN

 asm ("xxx: nop");
 //for(;;) {
  LCD_RS = 0;
  LCD_RW = 1;
  LCD_EN = 1;
  asm ("movf _PORTB,w");
  LCD_EN = 0;
  LCD_EN = 1;
  LCD_EN = 0;
  asm ("andlw _LCD_BF"); //Test na BF příznak.
  asm ("btfsc _STATUS,2"); //TEST na ZERO
  asm ("goto xxx"); //TEST na ZERO
 //     }
 LCD_RW = 0;
 TRISB1 = 0; //Nastavani RB1 na vystupní PIN
 TRISB2 = 0; //Nastavani RB2 na vystupní PIN
 TRISB4 = 0; //Nastavani RB4 na vystupní PIN
 TRISB5 = 0; //Nastavani RB5 na vystupní PIN
#else
 DelayMs(9);
#endif
}
//----------------------------------------------------------------
void send_prikaz (unsigned char bCommand){
 lcd_data = bCommand;
 lcd_busy();
 LCD_RW = 0;
 LCD_RS = 0;
 LCD_EN = 1;
 zapis_w();
 LCD_EN = 0;
}
//----------------------------------------------------------------

void lcd_init(void){
//Pro jistotu se provede několikrát inicializace
 DelayMs(255);
 lcd_init1();
 DelayMs(255);
 lcd_init1();
 DelayMs(255);
 lcd_init1();
 DelayMs(255);
 lcd_init1();
 DelayMs(255);
 lcd_init1();
}
void lcd_init1(void){
//inicializace LCD displye
 //unsigned trisb;
 TRISB = (0b11000001 & TRISB) | 0b00000000;
 //nastaveni portu B bity 37 na výstupní
 //ostatní nechá nezměněny.
 TRISE0 = 0; //RS -> data ven
 TRISE1 = 0; //RW -> data ven
 ADCON1 = 12;
 //PortA je nastaven jako vstup analog a
 //RE0 až 2 jsou nastaveny datové piny.

 DelayMs(100);
//***************** Nastaveni 8-bitové komunikace ***************************

 //8-bit-interface
 send_prikaz(0x03);

 DelayMs(100);
//*************** Nastaveni 4-bitové komunikace *****************************

 //4-bit-interface
 send_prikaz(0x02);
 DelayMs(100);

//***************************************************************************
 //4-bit-interface, 2-lines
 lcd_put_cmd (0x28);
 //lcd_put_cmd (0b00100000);

 //disp.off, curs.off, no-blink
 lcd_put_cmd (0x08);

 //lcd smazat
 lcd_put_cmd (0x01);

 //disp.on, curs.off
 lcd_put_cmd (0x0c);

 //Inkrementace, normalni mod.
 lcd_put_cmd (0x06);
}
//----------------------------------------------------------------
void zapis_w(void){
 //zkopirovat 4 az 7 bity z registru lcd_data na porty B.
 //RB1 ...DB4 ... bit4 registru lcd_data
 //RB2 ...DB5 ... bit5 registru lcd_data
 //RB4 ...DB6 ... bit6 registru lcd_data
 //RB5 ...DB7 ... bit7 registru lcd_data
 #asm
  bcf _PORTB,1
  bcf _PORTB,2
  bcf _PORTB,4
  bcf _PORTB,5
  btfsc _lcd_data,4
  bsf _PORTB,1
  btfsc _lcd_data,5
  bsf _PORTB,2
  btfsc _lcd_data,6
  bsf _PORTB,4
  btfsc _lcd_data,7
  bsf _PORTB,5
 #endasm
}
//----------------------------------------------------------------
void lcd_put_char (unsigned char sString)
{
 //Pošle znak na Display.
 lcd_data = sString;
 lcd_busy();
 LCD_RW = 0;
 LCD_RS = 1;
 LCD_EN = 1;
 zapis_w();
 LCD_EN = 0;
 LCD_EN = 1;
 asm ("swapf _lcd_data , f");
 zapis_w();
 LCD_EN = 0;
}
//----------------------------------------------------------------

void lcd_scroll(char Direction)
//Pohybuje celým displayem. Toto je funkce LCD (HW)
{
 if(Direction==SCROLL_LEFT)
 {
  lcd_put_cmd(0b00011000); //Scroll LEFT
 }
 else
 {
  lcd_put_cmd(0b00011100); //Scroll RIGHT
 }
}
//----------------------------------------------------------------
void ScrollMessage(unsigned char pozice, unsigned char radek,const char Message[])
//Pohybuje textem na zvoleném řádku od zadané pozice.
{
 char TempS[20];
 unsigned int MHead=0,Done=0,count;
 while(Done==0)
 {
  for(count=0;count<20;count++)
  {
   TempS[count]=Message[MHead+count];
   if(Message[MHead+count+1]==0) Done=1;
  }
  MHead++;
  lcd_goto(pozice, radek);
  lcd_string(TempS);
  DelayMs(250);
 }
}
//----------------------------------------------------------------
void lcd_goto(unsigned char pozice, unsigned char radek)
{
 if(radek >1) radek=1;
 lcd_put_cmd(((radek * 40) + 0x80)+ pozice);
}

//----------------------------------------------------------------
void lcd_string(const char * s)
{
 while(*s)
 lcd_put_char(*s++);
}
//----------------------------------------------------------------
void lcd_put_cmd (unsigned char bCommand)
{
 //Posle prikaz na Display.
 //asm ("movwf _lcd_data");
 send_prikaz (bCommand);
 LCD_EN = 1;
 asm ("swapf _lcd_data , f");
 zapis_w();
 LCD_EN = 0;
}
//----------------------------------------------------------------
main(void){
unsigned char loop;
 lcd_init();
for(;;) {
 ScrollMessage(0,0," Vita Vas Vyvojova deska PIC+XILINX ");
 lcd_cls;
 lcd_goto(0,1);
 lcd_string("Verze SW: 1.0.4.");
 ScrollMessage(0,0," Ze dne: 6. listopadu, 2005. ");
 ScrollMessage(0,0," Veskera dokumentace teto demonstracni desky se da stahnout z: ");

 for (loop = 0; loop < 10; loop++){
  lcd_goto(0,1);
  lcd_string("prochazka.zde.cz");
  DelayMs(250);
  DelayMs(250);
  DelayMs(250);
  lcd_goto(0,1);
  lcd_string(" ");
  DelayMs(250);
  DelayMs(250);
  DelayMs(250);
  }

  lcd_goto(0,0);
  lcd_string("prochazka.zde.cz");
  lcd_goto(0,1);
  lcd_string(" ----ahoj---- ");

 for (loop = 0; loop < 5; loop++){
  DelayMs(250);
  DelayMs(250);
  DelayMs(250);
  DelayMs(250);
  lcd_scroll(SCROLL_LEFT);
  DelayMs(250);
  DelayMs(250);
  DelayMs(250);
  DelayMs(250);
  }

 for (loop = 0; loop < 5; loop++){
  DelayMs(250);
  DelayMs(250);
  DelayMs(250);
  DelayMs(250);
  lcd_scroll(SCROLL_RIGHT);
  DelayMs(250);
  DelayMs(250);
  DelayMs(250);
  DelayMs(250);
  }

 lcd_cursor_home;
 lcd_string("prochazka.zde.cz");
 ScrollMessage(0,1," Uvitaci program vyvojoveho kitu PIC16F887A + XILINX XC9572XL. Zelenim tlacitkem se zvysuje hodnota na LED display a Modrim se hodnota Na LED display zmensuje. Zluta tlacitka ovladaji LED diody. Prepinace zapinaji a vypinaji blikani LED diod. Pokud je zapnuto blikani a stisknuto zlute tlacitko, tak LED dioda nesviti ale blika!!! ");
 DelayMs(250);
 DelayMs(250);
 DelayMs(250);
 DelayMs(250);
 DelayMs(250);
 DelayMs(250);
 DelayMs(250);
 DelayMs(250);
 lcd_cls;
 }

}