Subject: Graphic LCD (Dec 02 Readout) DISPLAYING LATERAL THINKING Dear EPE, It may interest you to know that I have modified John Becker's PIC World Clock to run from a PIC16F84, and using shift register chips to drive the graphics l.c.d. (GLCD) it uses. I did it out of interest and to explore GLCDs and thought you would appreciate the lateral thinking. Being relatively new to electronics and PICs it would be so easy to just use other people's hard work (e.g. John's) and not know what was going on. Having built successful motor racing engines for some 12 years, I am used to getting the max from the min and still push for more! Like programming? As you will appreciate, using shift registers slow things up a tad, some two seconds passing before the l.c.d. shows a picture. This made me look for ways to speed things up and some experimentation took place (flow bench and dyno time). I was intrigued by the OUTDATA routine and its use - how was its present form arrive at? By applying my interpretation of Toshiba's data sheet (and a couple of accidents!) I found a huge saving in program and processor time/space. For instance, goodbye AUTOWRITE. I was also using an I2C EEPROM as a table store, the routine to access it was fun to complete, it is only in 8-bit form at present but it works. A lot more pictures/tables could be accessed this way, without using program space or having to b e careful using PCLATH commands. Weather pictures, moving maps, that sort of thing. I have finished the displaying of the "world map", stored on EEPROM. The program is minimal and uses 192 commands total, 88 of which are shift register and EEPROM routines. All 1024 bytes are sent to the l.c.d. without a break. The GLCD works every time, no erroneous displays or characters. Status "checks" are performed but hidden. Your PIC Tutorial said "think default"... so I did. Graham Card, via email Congratulations Graham on what you are achieving - and yes, "max from min" is a pretty good analogy for PIC programming! I can't offer any more info on how I arrived at my GLCD routines - what is in the text is all that I managed to extract from Toshiba's info, and some of those routines were developed using blood and tears - brilliant of you to find a way to simplify some! ; graphic-16F84.asm 08-09-2002 18:00:57 ; show world map on graphic l.c.d. on pic 16F84, using shift register interface #DEFINE PAGE0 BCF 3,5 #DEFINE PAGE1 BSF 3,5 W: .EQU 0 F: .EQU 1 PORTA: .EQU 5 TRISA: .EQU 5 PORTB: .EQU 6 TRISB: .EQU 6 LOOP: .EQU 13 UNIT: .EQU 19 TEMP: .EQU 12 COUNT1: .EQU 20 LOOPB: .EQU 32 ; general loop LOOPC: .EQU 33 ; general loop TEMP1: .EQU 18 ; temp store STORE: .EQU 14 ; temp store STORE1: .EQU 36 ; temp store TEMPA: .EQU $25 ; temp store ADRLSB: .EQU $27 ; low address ADRMSB: .EQU $28 ; high address COUNT: .EQU 17 ; counter COLUMN: .EQU $2D ; column length holder LINE: .EQU $2E ;**************************fixed values for commands************************************ TXHOME: .EQU 64 ; text home address command TXAREA: .EQU 65 ; text area (columns) address command GRHOME: .EQU 66 ; graphics home address command GRAREA: .EQU 67 ; graphic area (columns) address command AWRON: .EQU 176 ; autowrite on command AWROFF: .EQU 178 ; autowrite off command OFFSET: .EQU 34 ; offset command ADPSET: .EQU 36 ; address set command .ORG 4 .ORG 5 clrf PORTA clrf PORTB PAGE1 movlw %00001000 movwf TRISA ; porta 3 input & the rest outputs clrf TRISB ; bits 3 & 4 used for I2C eeprom PAGE0 bsf PORTB,3 bsf PORTB,4 ;set for bus ready clrf LINE clrf LOOP movlw 4 ;number of pages to read movwf TEMP1 ;******************************************init tables************************************ TEXTHOME: ; ** set text home address ** clrf ADRMSB ; text home address $0000 clrf ADRLSB call CMDADR ; send command address movlw TXHOME call SENDCMD ; send command ;return GRAPHHOME: ; ** set graphic home address ** movlw 2 ; graphic home address $0200 (512) movwf ADRMSB ;clrf adrlsb,--- already done call CMDADR ; send command address movlw GRHOME call SENDCMD ; send command ;return TEXTAREA: ; ** set text area ** clrf ADRMSB ;movf column,w ; columns length movlw 16 movwf ADRLSB ; call CMDADR ; send command address movlw TXAREA ; text area command call SENDCMD ; send command ;return GRAPHAREA: ; ** set graphic area ** ;clrf adrmsb movf COLUMN,W ; columns length movwf ADRLSB ; call CMDADR ; send command address movlw GRAREA ; graphic area command call SENDCMD ; send command ;return SETMODE: ; ** set mode - many options, see epe text ** movlw %10000000 ; (or mode, internal cg mode) call SENDCMD ; send command ;return SETOFFSET: ; ** set offset register ** ;clrf adrmsb ; movlw 2 ; movwf ADRLSB ; call CMDADR ; send command address movlw OFFSET ; call SENDCMD ; send command ;return SETDISPLAY: ; ** display mode ** some options: movlw %10011100 ; text on, graphic off, cursor & blink off call SENDCMD ; send command ;return CLRTXT: ; ** clear text area ($0000) ** ;clrf adrmsb ; clear all text screen lines, length as set clrf ADRLSB ; call SCREENADR ; set screen write address movlw AWRON ; auto write on call SENDCMD ; send command movlw 8 ; number of lines movwf LOOPC CLR2: movf COLUMN,W ; column length movwf LOOPB ; CLR3: movlw 0 ; write 0 call OUTDATA ; <----- was autowrite decfsz LOOPB,F ; goto CLR3 ; decfsz LOOPC,F ; goto CLR2 ; movlw AWROFF ; auto write off call SENDCMD ; send command ;return MAP: ;clrf adrlsb ; set column (first) movlw 2 ; set graphic base address ($02xx) movwf ADRMSB call SCREENADR movlw AWRON ; auto write on call SENDCMD ; send command call WRITE movlw AWROFF call SENDCMD LED: ;bsf portb,7 goto LED CMDADR: movf ADRLSB,W ; write data d1 call OUTDATA ; movf ADRMSB,W ; write data d2 call OUTDATA ; return ;******************************************data output**************************************** OUTDATA: movwf TEMPA call SHIFT ; rst cd ce rd wr fs data chk movlw %10010010 ; 1 0 0 1 0 0 1 0 call SHIFT ; call STROBE ; output data from shift registers movf TEMPA,W ; write data call SHIFT call ZERO return ; ;**************************************command output***************************************** SCREENADR: ; ** set address for write/read to/from screen movf ADRLSB,W ; write address lsb call OUTDATA ; movf ADRMSB,W ; write address msb call OUTDATA ; movlw ADPSET ; set address pointer ;call sendcmd ; send command ;return ; SENDCMD: movwf TEMPA ; value brought in on w movf TEMPA,W ; write command call SHIFT ; rst cd ce rd wr fs data chk send to command shift register movlw %11010010 ; 1 1 0 1 0 0 1 0 call SHIFT ; call STROBE ; movf TEMPA,W ; write command call SHIFT ZERO: movlw %11101010 ; 1 1 1 1 1 0 1 0 call SHIFT ; call STROBE ; output data from shift register return ; ;******************************************shift register************************************* SHIFT: movwf TEMP ;load temp with byte to send. make sure w contains byte to send bsf COUNT,3 ;set count to shift 8 bits (count cleared & tested in pausit) SEND0: btfsc TEMP,7 ;is msb set? goto SEND1 ;yes, send a one bcf PORTA,1 ; goto CLOCK ;no, send a 0 data bit SEND1: bsf PORTA,1 ;send a 1 data bit CLOCK: bsf PORTA,2 ;clock on bcf PORTA,2 ;clock off rlf TEMP,F ;move next bit to send decfsz COUNT,F ;is count zero, all bits sent? goto SEND0 ;no, send next bit return ;yes return to call STROBE: bsf PORTA,0 ;move shift register to storage register bcf PORTA,0 ;latch output for buffer return ;************************************* i2c section ******************************************* WRITE: call START ;initialise i2c & prepare to receive memory address movlw 160 ;eeprom slave address, because bit 0 is 0 this is a write command call MEM movlw 0 ;set eeprom address (page) pointer call MEM ;eeprom memory allocation movlw 0 ;set eeprom address (line) pointer call MEM ;eeprom memory allocation ;return READ: ;call write ;send address bytes bsf PORTB,4 ;free bus call START ; movlw 161 ;eeprom slave address, because bit 1 is 1 this is a read command call MEM ;send command MEMIN: call IN ;set port,3 to read data movlw 8 movwf COUNT1 ;set count to shift 8 bits FETCH: bsf PORTB,4 ;clock pin high, release bit for transfer rlf UNIT,F ;move bits one place left & store new value in unit bcf UNIT,0 ;set 0 value before porta,3 bit test btfss PORTB,3 ;is bit 3 set ? goto NEXT1 ;no, then leave unit bit 0 value as is bsf UNIT,0 ;yes, set bit 0 of unit NEXT1: bcf PORTB,4 ;clear clock pin decfsz COUNT1,F ;is count zero? goto FETCH ;no, get another bit! movf UNIT,W call OUTDATA ;send character to lcd incfsz LINE,F goto ACK1 decfsz TEMP1,F goto ACK1 ;get another call STOP return ACK1: call OUT bcf PORTB,3 ;ack data bit bsf PORTB,4 ;9th clock cycle ;nop bcf PORTB,4 call IN goto MEMIN ;************************************send data to eeprom*********************************** MEM: movwf STORE movlw 8 ;set count for 8 bits movwf COUNT1 TX: btfss STORE,7 ;test bit to send goto SEN0 ;clear so send a 0 bsf PORTB,3 ;set so send 1 goto SEN1 SEN0: bcf PORTB,3 ;send 0 SEN1: bsf PORTB,4 ;set clock ;nop bcf PORTB,4 rlf STORE,F ;move next bit to send decfsz COUNT1,F ;have 8 bits been sent? goto TX ;no ACK: call IN bsf PORTB,4 ;9th clock cycle AGAIN: btfsc PORTB,3 ;is ack set prog stops until eeprom ready goto AGAIN bcf PORTB,4 ;end of 9th clock cycle call OUT return ;send start condition to eeprom START: bcf PORTB,3 ;bring sda line low with clock high to generate start condition. ;nop bcf PORTB,4 ;clock line brought low to enable data line change return STOP: bcf PORTB,3 bsf PORTB,4 ;nop bsf PORTB,3 ;clock & data lines left high to free bus ;call pausit return IN: PAGE1 ;change gender of porta,3 bsf TRISB,3 ;make porta 3 an input to read ack PAGE0 return OUT: PAGE1 bcf TRISB,3 ;reset porta 3 to output PAGE0 bsf PORTB,3 ;data line left high to free bus return ;*****************************************timing section*********************************** ;pausit: bsf loop,5 ;delay seems to work ok at up to 6mhz crystal, adjust to suit yours ;time: clrf count ;again1: decfsz count,f ;goto again1 ;decfsz loop,f ;goto time ;return .end