Hi, Tim Webb wrote: >Could someone explain to me the basics to generate a custom character on a >44780 LCD? >Maybe someone could just give me a simple outline of the steps required... Well a *long* time ago I messed aorund with these displays for my own pic learning experience. I resurfaced some code that might be useful to you. I've omitted parts of it trying to clarify the part that your asking about, here goes: This snippet initialise the display to my likeing (I used an 3(4) wire i/f with an 74164 shift reg. to the display, odd i guess :) ), then it proceeds into filling the display char ram with character definitions stored into the eeram (8 chars) of the 16F84 before entering the main loop. /Tony First some defs I used: ;***** CONSTANT DEFINITIONS ; *** For the lcd display , commands good to have vars etc.. CONSTANT CLEAR_DISPLAY = 0x01 ; clears display, returns to 'home' CONSTANT CURSOR_HOME = 0x02 ; cursor to first position on disp CONSTANT CURSOR_MODE = 0x04 ; set cursor move mode ; these should be OR'd with above to 'enable' the feature CONSTANT OPT_CURMOD_INC = 0x02 ; increment cursor after each byte CONSTANT OPT_CURMOD_SHIFT = 0x01 ; shift display when bytes are written CONSTANT CURSOR_TYPE = 0x08 ; Enable/disable / 'mode' for cursor ; these should be OR'd with above to 'enable' the feature CONSTANT OPT_CURTYP_DISPON = 0x04 ; turn display on ? CONSTANT OPT_CURTYP_ON = 0x02 ; turn cursor on CONSTANT OPT_CURTYP_BLINK = 0x01 ; turn on cursor blink CONSTANT CURSOR_MOVE = 0x10 ; moves the cursor or shifts the disp ; these should be OR'd with above to 'enable' the feature CONSTANT OPT_CURMOV_SHIFT = 0x08 ; display shift ( moves whole disp contents) CONSTANT OPT_CURMOV_RIGHT = 0x06 ; direction is right ( else left :-) ) CONSTANT SET_INTERFACE = 0x20 ; these should be OR'd with above to 'enable' the feature CONSTANT OPT_INTERFACE_8BIT = 0x10 ; set interface to 8 bit mode CONSTANT OPT_INTERFACE_2LINE = 0x08 ; set interface to 2 line mode CONSTANT OPT_INTERFACE_5x10FONT = 0x04 ; use 5x10 font(*not used*) ; these should be OR'd with the desired adress CONSTANT MOVE_CURSOR_RAM = 0x40 ; move cursor into CGRAM + adress CONSTANT MOVE_CURSOR_DISP = 0x80 ; move cursor into display + adress CONSTANT LINE_1_HOME = 0x00 ; adress for first line first char CONSTANT LINE_2_HOME = 0x40 ; adress for second line first char CONSTANT LINE_3_HOME = 0x20 ; adress for third line first char CONSTANT LINE_4_HOME = 0x60 ; adress for fourth line first char ; the following are the BASE adresses for the user generated char ram area ; should be followed by eight byte containing bit/pixel info for each char ; the first byte is the top pixels in the char: ; Byte/bits x=don't care Offset Example ( 'light' bulb :-) ; xxx00000 0 00100 0x04 ; xxx00000 1 01110 0x0A ; xxx00000 2 10001 0x11 ; xxx00000 3 10001 0x11 ; xxx00000 4 01110 0x0E ; xxx00000 5 01110 0x0E ; xxx00000 6 00100 0x04 ; xxx00000 7 - Note the cursor 'line' normally not used for char ; Sequence - Set cursor to ram at the desired base adr. then send eight consecutive ; bytes contaning the pixels. When used on display there are at ; character codes 0x00 - 0x07 OR 0x08 - 0x0F ( they are 'mirrored' 0x00 = 0x08 ) CONSTANT CHAR_1_RAM = 0x00 ; adress for 1 user byte in ram CONSTANT CHAR_2_RAM = 0x10 ; adress for 2 user byte in ram CONSTANT CHAR_3_RAM = 0x18 ; adress for 3 user byte in ram CONSTANT CHAR_4_RAM = 0x20 ; adress for 4 user byte in ram CONSTANT CHAR_5_RAM = 0x28 ; adress for 5 user byte in ram CONSTANT CHAR_6_RAM = 0x30 ; adress for 6 user byte in ram CONSTANT CHAR_7_RAM = 0x38 ; adress for 7 user byte in ram CONSTANT CHAR_8_RAM = 0x40 ; adress for 8 user byte in ram ;***** CONSTANT DEFINITIONS ( pins ) ; For connection with LCD #define lcdDATA PORTA,0 ; Output lcd data to shift register #define lcdCLOCK PORTA,1 ; Output clock to shift register #define lcdReadWrite PORTA,2 ; 'E' on lcd performs read/write when low->high #define clearShiftReg PORTA,3 Then onto the startup procedure: ;--------------------------------------------------------------------------- ; ; Initialisation ; ;--------------------------------------------------------------------------- INIT: ;+++ ; Set up the ports etc.. ; Clear all registers on bank 0 ( memory ) ;+++ ; Set up Timer 0. ;--------------------------------------------------------------------------- ; ; the main entry point ; ;--------------------------------------------------------------------------- MAIN: ; NOTE ! the delay routine we are using here are messing with interrupt ; timer module so the timers will not be accurate !! ; therefore avoid this delay routine when in main loop ! ; init the lcd display interface MOVLW 0x29 ; make an 20 ms delay ( 20 x 0.5 ms ) CALL DELAY_x500us BCF STATUS,C ; clear carry ( data is instruction ) MOVLW (CLEAR_DISPLAY | CURSOR_HOME) ; reset ( clear/home ) CALL NYBBLE_OUT MOVLW 0x0B ; make an 5 ms delay ( 10 x 0.5 ms ) CALL DELAY_x500us STROBE_READ_WRITE MOVLW 0x02 CALL DELAY_x500us ; make an 0.5 - 1 ms delay ( note accuracy of timer is +/- 0.5 ms ) STROBE_READ_WRITE MOVLW 0x02 CALL DELAY_x500us ; make an 0.5 - 1 ms delay BCF STATUS,C MOVLW CURSOR_HOME CALL NYBBLE_OUT MOVLW 0x02 CALL DELAY_x500us ; make an 0.5 - 1 ms delay MOVLW (SET_INTERFACE | OPT_INTERFACE_2LINE ) ; setup for two line operation CALL SEND_INSTRUCTION MOVLW CURSOR_TYPE ; turn off cursor CALL SEND_INSTRUCTION MOVLW CLEAR_DISPLAY ; clear display CALL SEND_INSTRUCTION MOVLW (CURSOR_MODE | OPT_CURMOD_INC) ; set cursor to increment after each char CALL SEND_INSTRUCTION ; turn on display, + blinking cursor MOVLW (CURSOR_TYPE | OPT_CURTYP_DISPON | OPT_CURTYP_ON) ; BLINK) CALL SEND_INSTRUCTION ;*** key function to load cgram **** CALL SEND_MY_CHARS ; tranfers the character definitons to LCD ;*** key function to load cgram **** ;MOVLW ( CURSOR_MODE | OPT_CURMOD_INC | OPT_CURMOD_SHIFT) ; increment cursor after each byte ; and shift display when bytes are written ;CALL SEND_INSTRUCTION MAIN_LOOP: GOTO MAIN_LOOP SEND_MY_CHARS ; send the contents of eeprom to lcd cgram MOVLW 0x40 ; eight chars, eight bytes each 8x8 MOVWF CharCnt ; MOVLW 0x00 MOVWF RowCnt ; position the cursor in cgram ( adress 0x00 first byte) MOVLW MOVE_CURSOR_RAM CALL SEND_INSTRUCTION SEND_LOOP MOVF RowCnt,W MOVWF EEADR ; put in adress reg. BANK1 BSF EECON1,RD ; set bit to read BANK0 MOVF EEDATA,W ; move data to W MOVWF LCDChar CALL SEND_CHAR ; send it INCF RowCnt,F ; increase eeprom adress MOVLW 0x02 CALL DELAY_x500us ; make an 0.5 - 1 ms delay DECFSZ CharCnt,F GOTO SEND_LOOP ; check next char SEND_END ; all sent MOVLW 0x10 MOVWF CharCnt MOVLW 0x11 MOVWF RowCnt MOVLW (MOVE_CURSOR_DISP | LINE_1_HOME) CALL SEND_INSTRUCTION RETURN ; else return SEND_CHAR ; character in LCDChar !! ;MOVWF LCDChar ; Save the charater code temporary SWAPF LCDChar,W ; Send the High Nybble BSF STATUS, C ; RS = 1, data is NOT instruction CALL NYBBLE_OUT MOVF LCDChar,W ; Send the Low Nybble BSF STATUS, C CALL NYBBLE_OUT RETURN SEND_INSTRUCTION SEND_STOP_INT ; first stop all interrupts ( as we check the buffer inside int )! BCF INTCON,GIE ; disable global interrupts.. BTFSC INTCON,GIE ; check that is really was disabled GOTO SEND_STOP_INT ; nope try again MOVWF LCDChar SWAPF LCDChar,W ; Send the High Nybble BCF STATUS, C ; RS = 0, data IS instruction CALL NYBBLE_OUT MOVF LCDChar,W ; Send the Low Nybble BCF STATUS, C CALL NYBBLE_OUT MOVLW b'10100000' ; enable global & TMR0 interrupts MOVWF INTCON MOVLW 0x02 CALL DELAY_x500us ; make an 0.5 - 1 ms delay RETURN NYBBLE_OUT ; Send a (4 bit) Nybble to the LCD MOVWF NybbleTemp ; Save the Nybble to Shift Out SWAPF NybbleTemp,F ; Setup to Output to the High Part of the Byte CLEAR_SHIFT_REG ; clears the shift register BTFSC STATUS,C ; check carry, and set instruction bit according BSF lcdDATA ; Put out the RS Bit STROBE_CLOCK ; and strobe it in the shift register BCF lcdDATA MOVLW 4 ; Now, Shift out the Data MOVWF NybbleCount NYBBLE_LOOP RLF NybbleTemp,F ; Shift Through the Nybble to Output BTFSC STATUS,C ; is the carry set ( bit was high ) BSF lcdDATA ; yep put out bit STROBE_CLOCK ; strobe it into shift register BCF lcdDATA ; clear data pin DECFSZ NybbleCount,F GOTO NYBBLE_LOOP STROBE_READ_WRITE ; strobe the 'E' ( send/receive data ) RETURN ; *********************************************************************** ; ; Here starts the EEPROM mem, used for user generated characters. These will be sent ; to LCD CGRAM at start up. Note all eight will be sent.( could be modified ofcource ). ; ORG 0x2100 My_LCDChar_1 EQU 0x00 DW b'00000000' ; Top row char lcd adr. 0x00 DW b'00001110' ; NOTE Only lowest five bits used !! DW b'00011111' ; DW b'00011101' ; DW b'00001110' ; DW b'00001110' ; DW b'00000100' ; DW b'00000000' ; bottom row ( i.e. cursor line ) My_LCDChar_2 EQU 0x08 DW b'00010101' DW b'00001110' DW b'00010001' DW b'00010001' DW b'00001110' DW b'00001110' DW b'00000100' DW b'00000000' My_LCDChar_3 EQU 0x10 DW b'00011111' DW b'00010001' DW b'00010001' DW b'00010001' DW b'00010001' DW b'00010001' DW b'00011111' DW b'00000000' My_LCDChar_4 EQU 0x18 DW b'00011111' DW b'00010001' DW b'00010001' DW b'00010001' DW b'00010001' DW b'00011111' DW b'00011111' DW b'00000000' My_LCDChar_5 EQU 0x20 DW b'00011111' DW b'00010001' DW b'00010001' DW b'00010001' DW b'00011111' DW b'00011111' DW b'00011111' DW b'00000000' My_LCDChar_6 EQU 0x28 DW b'00011111' DW b'00010001' DW b'00010001' DW b'00011111' DW b'00011111' DW b'00011111' DW b'00011111' DW b'00000000' My_LCDChar_7 EQU 0x30 DW b'00011111' DW b'00010001' DW b'00011111' DW b'00011111' DW b'00011111' DW b'00011111' DW b'00011111' DW b'00000000' My_LCDChar_8 EQU 0x38 DW b'00011011' DW b'00010001' DW b'00000000' DW b'00000100' DW b'00000000' DW b'00010001' DW b'00011011' DW b'00000000' END -- http://www.piclist.com hint: PICList Posts must start with ONE topic: [PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads