Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
Resource icon

Interfacing a Toshiba T6963C Equipped GLCD with a PIC Microcontroller 2011-12-13

So you happen to have a GLCD with the Toshiba T6963C display controller and you want to interface it with a PIC and write a driver for it. You've scoured the internet, but the only tutorial you've found that shows how to do it is useless. Well, here I will demonstrate just exactly how to go about doing it.

For this tutorial, our example setup is the Microchip 44-pin Demo board, which features the PIC 16F887, driving a Solomon LM6271FWL 240 x 64 GLCD display. This display has 8K VRAM available as well as both a character generator ROM with built in character map as well as an external CG RAM for user defined characters. This display as you've probably figured out also uses the Toshiba T6963C controller chip.

The Hardware Interface

The physical hardware interface is rather simple. On our GLCD, we have an 8 bit bi-directional data port (lines D0-D7) which you can drive with one of the I/O ports on the PIC. Or if you want a serial interface, you can drive it with a 74HC595 serial in/parallel out latch, but the catch to this is that you cannot read from the display if that's all you use. You could just as well use a 74HC165 parallel in/serial out chip in conjunction with the 74HC595 latch if you need to read from the display. In my example, I am using PORTD on the PIC to direct drive the GLCD's data port.

On the control side of the GLCD, you will have the following lines -

C/D - Command/Data mode select (Command High/Data Low)
CE - Chip Enable (active when low)
READ Mode Select (active when low)
WRITE Mode Select (active when Low)
RESET - Resets the GLCD's controller (reset active when low)
Font Select - Allows you to select either a 6x8 or 8x8 font size (6x8 High/8x8 Low)

On my example display, I'm using the lower 3 bits of PORTA and PORTC to drive these lines as follows -

RA0 - CD
RA1 - RESET
RA2 - Font Select
RC0 - WRITE
RC1 - READ
RC2 - CE

Before we get into how to initialize the display we first need to talk about the power up sequence. On your GLCD, you will find that you have connections for two power supplies. The first supply is the +5V logic supply. This supply powers the GLCD's controller, VRAM chips and all of the logic stuff that makes the GLCD work. The second supply is an adjustable 0-12V negative supply that powers the LCD screen itself. The reason it is adjustable is to provide a means of adjusting the display contrast. This supply should not be on until the logic supply has come up to voltage and the display controller has been run through the initialization sequence. Once the display initialization sequence is complete, you can then apply power to the GLCD from the second supply. This prevents latch up of the CMOS LSI (the T6963C and the LCD driver LSI).

Now let's talk about PIC speed. The setup and hold time for all of the control signals as well as data input/output on the T6963C is between 10 and 150nS. So as long as your PIC instruction clock does not run faster than 200nS per instruction (20MHz Fosc, which results in a 5MHz instruction clock), we don't have to worry about needing "nop" instructions or running delay loops in between control line switching/data throughput. On my example PIC, I'm running a 16MHz crystal, which results in an instruction clock frequency of 4MHz, and instructions are executed at a rate of 250nS per instruction (1/4MHz = 250nS). You can run slower than this if you want. It's not a requirement to run the PIC that fast.

Now that the power up sequence and PIC speed is understood, we can get into what needs to happen for the initialization sequence of the T6963C.

PIC Initialization Sequence

First, let's write our code to get our PIC set up and initialized. We will be setting up all of the ports as outputs with the exception of PORTD, which is our data in/out port. This port will be operating as a bidirectional data port so we will be defaulting this port to be an input, then switching it to output once we need to write to the GLCD. For this application, it's best to default the PIC data port to input since we have no way of guaranteeing that the data port on the GLCD won't switch to output mode when we're not directly addressing it. But since we have control of the PIC at all times, we can default the PIC data port to input so that it only appears as a high impedance load on the GLCD's data port, which keeps the PIC port from shorting the GLCD data port should the GLCD data port decide it wants to be an output during the time that we're not addressing it.

The other reason for defaulting the port to input is due to what the T6963C controller expects to see prior to writing data to it as per its timing chart. On the timing chart for the T6963C, it shows the data port floating during the setup time of the CE and read/write signals.

So...let's get the PIC set up. This part should be familiar -

Code:
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**				16F887 Graphic LCD Driver					**
;**			     For Toshiba T6963C 240 x 64 GLCD					**
;**				       By Jon Wilder						**
;**			              Date: 12/01/2011						**
;**												**
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**				Header Information/Config Options				**
;**												**
;**												**
;**												**
;** Processor Type: PIC 16F887									**
;** Default Radix: Decimal									**
;** Error Level: -302/Suppress all assembler bank select warnings				**
;** Reference header file P16F887.INC for SFR and Config option labels				**
;**												**
;** Configuration Word 1									**
;**												**
;** In Circuit Debug Off (Default)								**
;** Low Voltage Programming Off									**
;** Fail Safe Clock Monitor Off 								**
;** Internal External Switchover Off								**
;** Brown Out Reset Off										**
;** Data Code Protection Off (Default)								**
;** Code Protection Off (Default)								**
;** RA5 has MCLR Function (Default)								**
;** Power Up Timer On										**
;** Watchdog Timer Off										**
;** High Speed XT Oscillator									**
;**												**
;** Configuration Word 2									**
;**												**
;** Program ROM Write Protection Off (Default)							**
;** Brown Out Reset 4.0V (Default)								**
;**												**
;** Fosc = 16MHz										**
;**												**
;*************************************************************************************************
;*************************************************************************************************

		list		p=16F887, r=dec, w=-302
		include		<P16F887.INC>
		__config	_CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _PWRTE_ON & _WDT_OFF & _FOSC_HS

;*************************************************************************************************
;**												**
;**				RAM Location Constants						**
;**												**
;*************************************************************************************************

		cblock		0x20
				TEMP			;temp buffer
				DATAL			;instruction data low byte
				DATAH			;instruction data high byte
				COMMAND			;instruction
				TABLECOUNT		;table counter
		endc

		cblock		0x70
				W_TEMP			;interrupt context save for W
				STATUS_TEMP		;interrupt context save for STATUS
				PCLATH_TEMP		;interrupt context save for PCLATH
				COUNT1			;delay counter 1
				COUNT2			;delay counter 2
				COUNT3			;delay counter 3
				DATA_EE_ADDR		;data EEPROM address buffer
				DATA_EE_DATA		;data EEPROM data buffer
		endc

;*************************************************************************************************
;**												**
;**				Control/Data Line Labels					**
;**												**
;*************************************************************************************************

;nemonics used for read/write enable/disable

WR_EN		EQU		2			;write mode enable
WR_DIS		EQU		7			;write mode disable
RD_EN		EQU		1			;read mode enable
RD_DIS		EQU		7			;read mode disable

;control lines

#define		CD		PORTA,RA0		;GLCD command/data
#define		RST		PORTA,RA1		;GLCD reset
#define		FONT		PORTA,RA2		;GLCD font select (6x8 or 8x8)
#define		WRITE		PORTC,RC0		;GLCD write enable/disable (active low)
#define		READ		PORTC,RC1		;GLCD read enable/disable (active low)
#define		CE		PORTC,RC2		;GLCD chip enable/disable (active low)

;data port

#define		D0		PORTD,RD0		;GLCD data port bit 0
#define		D1		PORTD,RD1		;GLCD data port bit 1
#define		D2		PORTD,RD2		;GLCD data port bit 2
#define		D3		PORTD,RD3		;GLCD data port bit 3
#define		D4		PORTD,RD4		;GLCD data port bit 4
#define		D5		PORTD,RD5		;GLCD data port bit 5
#define		D6		PORTD,RD6		;GLCD data port bit 6
#define		D7		PORTD,RD7		;GLCD data port bit 7


;*************************************************************************************************
;**												**
;**				Start of Main Code						**
;**												**
;*************************************************************************************************

		org		0x000			;reset vector
		goto		START			;jump to start of main code

		org		0x004			;interrupt vector
		goto		ISR			;jump to start of interrupt handler

;*************************************************************************************************
;**												**
;**				Initialization Routine						**
;**												**
;*************************************************************************************************

START		clrf		PORTA			;init ports
		clrf		PORTB
		clrf		PORTC
		clrf		PORTD
		clrf		PORTE
		banksel		ANSEL			;bank 3
		clrf		ANSEL			;all ports digital I/O
		clrf		ANSELH
		banksel		TRISA			;bank 1
		clrf		TRISA			;PORTA, PORTB, PORTC, and PORTE outputs
		clrf		TRISB			;PORTD defaults to input
		clrf		TRISC
		clrf		TRISE
		banksel		0			;bank 0

So now we have the basic setup of the PIC itself down. All port latches have been cleared, all ports have been configured for digital I/O, and all ports have been assigned as outputs with the exception of PORTD, which is defaulted to input mode.

Notice in my PIC code I have also pre-defined some RAM constants to use as buffers. DATAL and DATAH are the registers we will use for sending data bytes to the GLCD, COMMAND is the register that will be used to send command bytes to the GLCD, while TEMP and TABLECOUNT are general purpose working buffers that we will use throughout this bit of test code. In common RAM, I have pre-defined the buffers used for our software stack for interrupt context saving, delay counters, and buffers for the data EEPROM address and data buffers. These last two buffers I decided to place in common RAM to minimize the amount of required bank selecting for writing to/reading from the PIC's internal data EEPROM. By placing the delay counters in common RAM, this enables you to run delay loops no matter which bank happens to be the active bank.

The #defines you'll see I have equated to labels which correspond with the name of each of the GLCD's control lines. This makes things easier for two reasons. Reason 1 is that you don't have to remember the physical port/bit number to address everytime you want to set/clear that control line. Reason 2 is that it makes life much easier should we have to reassign that line to a different pin on the PIC. Rather than having to change every instruction which addresses that line, we can simply change the port/bit number that the label points to in the #define statement.

The WRT_EN, WRT_DIS, RD_EN and RD_DIS labels are equated to values which will pull the CE as well as the read/write lines up/down simultaneously upon loading these values into the control PORTC. According to the timing diagram for the T6963C, this is how these lines should be driven.

GLCD Initialization Sequence

Now that we have the base initialization of the PIC down, let's talk about the initialization of the T6963.

Upon power up, the GLCD must be pulled into hard reset for a period of not less than 1mS after the logic supply voltage reaches 4.75V. According to the T6963C data sheet, you have two ways in which to reset the controller. Method 1 is to use a hardware reset comprised of an external 10K pull up resistor and a 0.1uF pull down capacitor on the reset pin of the GLCD. Method 2 is to connect the reset pin of the GLCD to one of the I/O lines on the PIC, and have the PIC pull the GLCD into hard reset in the PIC code. I chose to use Method 2.

In my code, I have a fixed 50mS delay loop that I run after pulling the GLCD into reset, which more than satisfies the "pull into reset for 1mS after Vlogic = 4.75V" requirement. While I have the GLCD in reset, I also take the time to get the control lines set up to their default state while the GLCD is in hard reset mode. I set all of the control lines high with the exception of RESET with 6x8 font selected (font line high). I then run the 50mS delay loop, then disable reset. This initial bit of code will reside directly after our PIC initial setup routine and will look something like this -

Code:
GLCD_INIT	bcf		RST			;reset GLCD
		movlw		5 			;Command mode and 6x8 font
		movwf		PORTA
		movlw		7 			;disable read, write, and chip enable
		movwf		PORTC
		call		Delay50mS		;wait 50mS
		bsf		RST			;disable reset

OK...so far, we have the PIC and the GLCD logic circuit powered on, the PIC has been setup, and the GLCD has been pulled into hard reset, our control lines are set to default states (all high w/6x8 font size), and the reset has now been disabled. Now we need to start thinking about the initialization sequence of the T6963C. The initialization sequence will involve us sending a series of data and instructions to the GLCD to set things like the graphics RAM area, text RAM area, setting the RAM address pointer, etc etc. So before we can begin to do that, we need to first know a little bit about the T6963C instruction set as well as the instruction protocol.

The T6963C Instruction Set and Protocol

The T6963C has an instruction set which is comprised of 3 types of instructions -

Two data byte instruction
One data byte instruction
Instruction Only

On the instruction only instructions, the high nibble of the hex instruction byte is the instruction itself while the low nibble contains the settings for the instruction. On the one and two data byte instructions, the data bytes themselves are the settings that will be internally set by the instruction byte.

Now the data bytes and the instruction bytes must be sent to the T6963C in a specific order. On the instructions which have data bytes, you will send the 1st data byte first, then the second data byte, then the instruction byte last. The CD line must be driven low when sending the data bytes (data mode), and must be driven high when sending instruction bytes (command mode).

Prior to sending each byte, it is required that you check the lower 2 bits of the T6963C's status register to ensure that it is ready to receive data. These bits are known as STA0 and STA1 and both of these bits must be high before sending data to the GLCD. Having said that, the order of operations will go as such -

For Two Data Byte Instructions

STA0 & STA1 = 1 ---> Send 1st Data Byte ---> STA0 & STA1 = 1 ---> Send 2nd Data Byte ---> STA0 & STA1 = 1 ---> Send Instruction Byte

For One Data Byte Instructions

STA0 & STA1 = 1 ---> Send Data Byte ---> STA0 & STA1 = 1 ---> Send Instruction Byte

For Instruction Only Instructions

STA0 & STA1 = 1 ---> Send Instruction Byte

How To Send Instructions/Data to the GLCD

Since this is a function that we will be performing repeatedly, it would be best to can these code routines as a subroutine that will get called from main code. This is where our DATAL, DATAH, and COMMAND buffers will come in. We will load these buffers with the data and the instructions to be written to the GLCD in main code, then the subroutine will access these buffers when we call it to send the data/instructions.

On two data byte instructions, the first variable to get sent to the GLCD will be DATAL while the second variable to get sent will be DATAH. COMMAND will be the last to get sent.

Since we also need to do a check on status bits STA0 and STA1 in the GLCD's status register prior to sending each byte, it would be best to also can this routine as a subroutine that we can call repeatedly as well. As much as we will have to perform these tasks while making the GLCD work, this will save us LOTS of code space.

First we will write 2 subroutines to enable/disable status read mode -

Code:
StatReadEn	bsf		CD			;command mode
		movlw		RD_EN			;enable read
		movwf		PORTC
		return

StatReadDis	movlw		RD_DIS			;disable read
		movwf		PORTC
		return

Then we will code up the status check routine -

Code:
;check GLCD status

LCDStat		call		StatReadEn		;enable status read
		btfsc		D0			;is GLCD ready?
		btfss		D1
		goto		$-2			;no, check again
		call		StatReadDis		;disable status read
		return

The first instruction in LCDstat calls the subroutine that places the GLCD in command mode and drives the CE and READ lines low simultaneously with the RD_EN operand that we created in our equates table, which places the GLCD in read mode. When the GLCD is in command mode while in read mode, this tells the T6963C that you want to read the status register in the T6963C. The T6963C then places the current state of its status bits on the GLCD's data port where they are available to be read.

The next 3 instructions continuously poll data lines D0 and D1, which is where the current state of the T6963C's status bits STA0 and STA1 are placed. It continuously polls these lines until they are both high. Once they are both high, the next instruction calls the status read disable subroutine, which disables read mode by setting both READ and CE high simultaneously with the RD_DIS operand, then the return instruction returns back to the code segment that called the LCDStat subroutine and our status check is complete.

Now let's take a look at our data/instruction write routine -

Code:
;command/data write

Command		bsf		CD			;command mode
		goto		$+2
DWrite		bcf		CD			;data mode
		movwf		PORTD			;place write data on data port latch
		movlw		WR_EN			;chip enable low
		movwf		PORTC			;enable write
		banksel		TRISD			;bank 1
		clrf		TRISD			;RD0-RD7 output
		banksel		0			;bank 0
		movlw		WR_DIS			;disable write
		movwf		PORTC			;chip enable high
		banksel		TRISD			;bank 1
		comf		TRISD,F			;RD0-RD7 input
		banksel		0			;bank 0
		return

You will notice that there are two labels in this subroutine: Command and Dwrite. We can call this routine using both of these labels depending on whether we are writing a data byte or a command byte. This way we have one subroutine for both rather than having to can two write routines just for the sake of setting up the CD line, which again saves code space.

Now the way this works is that the data to be written to the GLCD must first be preloaded in the W register prior to calling either Command or DWrite.

Let's walk through the code. Assume we are writing the data byte that resides in DATAL, we would execute these instructions -

Code:
		movfw		DATAL
		call		DWrite

The first instruction at the label DWrite sets the CD line low, which places the GLCD in data mode. The next instruction takes the data in W that came from the DATAL buffer and drops it into the PORTD output latch. Then the CE and WRITE lines are dropped simultaneously by dropping the WR_EN value into the PORTC control port. We are almost ready to write, but since our data port driver PORTD is defaulted to input mode, we must first set it to output mode.

So the next instruction selects RAM bank 1. We then clear the TRISD register, which sets PORTD up as an output. Since we preloaded the PORTD output latch prior to doing this, the data we are sending is available on the PORTD pins immediately after switching PORTD to output mode, thereby providing a clean data write. We then bank select back to bank 0.

Once in bank 0, we raise both CE and WRITE back high simultaneously by dropping the value of WRT_DIS into the PORTC control register. We then bank select back to bank 1, compliment the TRISD register which switches all of the TRISD bits back to all 1's, making PORTD an input again. We then bank select back to bank 0, then return back to the code segment that called the subroutine.

Now, let's assume that we're writing a command. Our command will be preloaded in the COMMAND buffer. We will first preload W with the data in the COMMAND buffer, then we would call Command with these two instructions -

Code:
		movfw		COMMAND
		call		Command

The first instruction at the label "Command" sets the CD line high, placing the GLCD in command mode. The next instruction tells the program counter to jump ahead two lines of code ($+2 = Current line + 2), thereby skipping over the instruction that would otherwise clear the CD line to place it in data mode. It then drops the data that is in W that came from the COMMAND buffer into the PORTD output latch, and resumes in the same fashion that it does for a data byte write. Clever isn't it?

Now...we need a final subroutine that we will call in main code that will call the status check, command write and data write subroutines above in order to make all of this work. However, since we have 3 types of instructions (two data byte, one data byte and command only), we need to make this subroutine work for all 3 instruction types. This subroutine should look like this -

Code:
TwoData		call		LCDStat
		movfw		DATAL
		call		DWrite
OneData		call		LCDStat
		movfw		DATAH
		call		DWrite
NoData		call		LCDStat
		movfw		COMMAND
		call		Command
		return

Now how we will use this is we will first load our data bytes into the DATAL and DATAH buffers. DATAL will get preloaded with the first data byte while DATAH will get loaded with the second data byte. If it is a one data byte instruction, we will treat the data byte as if it were a second data byte by loading it into the DATAH buffer. We will then preload the instruction into the COMMAND buffer.

Let's assume that we want to send the "Set Text Home Address instruction. This is a two data byte instruction that sets the starting address of the text RAM. The first data byte is the low byte of the address while the second data byte is the high byte of the RAM address. Let's say for example that we want to set the text home address to 0x1700. We would first load 0x00 into DATAL, then we would load 0x17 into DATAH. We would then load 0x40 into the COMMAND buffer, which is the hex instruction for Set Text Home address. We would then call the TwoData subroutine -

Code:
TextHome	movlw		0x00
		movwf		DATAL
		movlw		0x17
		movwf		DATAH
		movlw		0x40
		movwf		COMMAND
		call		TwoData

The first two instructions preload buffer DATAL with the low address byte of the text RAM start address. The next two instructions preload buffer DATAH with the high byte of the text RAM start address. The next two preload buffer COMMAND with the hex value of the "Set Text RAM Home Address" instructions while the last instruction calls the above subroutine TwoData.

The first instruction in TwoData calls our status bit check subroutine labeled LCDStat above. Once the status bits are checked and it returns from this subroutine, the next instruction preloads W with the data in the DATAL buffer, then calls the DWrite subroutine, which writes the data byte to the GLCD. Upon completing the write, it returns and then calls LCDStat again to check the GLCD status bits again. Once done with this, it returns, preloads W with the data in buffer DATAH, then calls Dwrite again. Upon completing the write, it returns to run LCDStat again to check the status bits. Upon completion of the status bit check, it returns, preloads W with the instruction code in buffer COMMAND, then calls Command, which placed the LCD in command mode, then writes the instruction to the GLCD. Upon completing the write, it returns back, then returns back to our main code.

Now, if this were a one data byte instruction, we would follow the same routine in main code, but we would preload the data byte into DATAH, then call "OneData", which skips the routine that would send the data in DATAL to the GLCD. If this were a command only instruction, we would call "NoData" instead, which skips the part of the subroutine which writes the data bytes to the GLCD. Again, clever isn't it?

Now imagine if everytime we wanted to write to the GLCD we had to code out all of those instructions? This would take up LOTS of program ROM space really quick. By having these 3 subroutines, they only have to reside in program ROM once, which saves us valuable code space on the chip.

So we now have some code that will send data and instructions to the GLCD. Now we will talk about the instructions which will be required to initialize the GLCD.

T6963C Initialization Routine & Instructions

There are 5 instructions which we will use to initialize the T6963C. In order in which we will send them, they are -

Mode Set (0x80) - This instruction is an "instruction only" instruction which sets the operating mode of the GLCD. The high nibble (8) tells the T6963C that it is a mode set instruction while the low nibble will tell it the operating mode. The available operating modes are -

* Low Nibble = 0 - Text logically "OR'ed" with graphics with CG ROM on
* Low Nibble = 1 - Text logically "XOR'ed" with graphics with CG ROM on
* Low Nibble = 3 - Text logically "AND'ed" with graphics with CG ROM on
* Low Nibble = 4 - Text Attribute mode with CG ROM on
* Low Nibble = 8 - Text logically "OR'ed" with graphics with CG RAM on
* Low Nibble = 9 - Text logically "XOR'ed" with graphics with CG RAM on
* Low Nibble = B - Text logically "AND'ed" with graphics with CG RAM on
* Low Nibble = C - Text Attribute mode with CG RAM on

When CG ROM is on, character data values 0x00-0x7F correspond with the built in character map of the T6963C while character data vaules of 0x80-0xFF correspond with user defined characters which reside in the external CG RAM. When CG RAM mode is on, character codes 0x00 - 0xFF correspond only with user defined characters which reside in the external CG RAM.

As or goal here is to init the display and get it ready to display something and it would take a whole other article to explain all of the operating modes in detail, for now we are going to set Text Attribute mode with CG ROM on for the sake of simplicity. So for the first code routine, we will send the mode set instruction value of 0x84, which will place the GLCD in Text Attribute Mode with CG ROM on. Turning on the CG ROM gives us access to the T6963C's built in character map while also allowing us to program in 128 user defined characters -

Code:
ModeSet		movlw		0x84
		movwf		COMMAND
		call		NoData

Graphics Home Address Set (0x42) - This is a two data byte instruction that sets the starting address of the graphics RAM in VRAM. The T6963C is hardwired to allocate 5KB (5120 bytes) of the VRAM space for graphics RAM while allowing us to send the address where we want this space to start at. In our code, we will start the graphics RAM space at the first address 0x0000. So we will first load the address low byte into DATAL as the first data byte. We will then load DATAH with the address high byte. Lastly, we will load COMMAND with 0x42, then call the TwoData subroutine -

Code:
;graphics RAM start address 0x0000

GraphicsHome	movlw		0
		movwf		DATAL
		movlw		0
		movwf		DATAH
		movlw		0x42
		movwf		COMMAND
		call		TwoData

Now our graphics RAM space occupies the address range of 0x0000 - 0x13FF in VRAM.

Graphics Area Set (0x43) - This is a two data byte instruction which sets the number of columns of graphic data the display will support. The required data value for this instruction will vary with display size. Since our display is 240x64, there are a total of 40 columns. This means that we will send the hex value of 0x28 (decimal 40) as the first data byte. On this instruction, the second data byte is always 0x00.

Code:
;graphics area 40 rows for 240x64 display

GraphicsArea	movlw		0x28
		movwf		DATAL
		movlw		0
		movwf		DATAH
		movlw		0x43
		movwf		COMMAND
		call		TwoData

Text Home Address Set (0x40) - This is a two data byte instruction that sets the starting address of the text RAM in VRAM. The T6963C is hardwired to allocate 1.2KB (1280 bytes) of the VRAM space for text RAM while allowing us to send the address where we want this space to start at. In our code, we will start the text RAM space at address 0x1700. So we will first load the address low byte into DATAL as the first data byte. We will then load DATAH with the address high byte. Lastly, we will load COMMAND with 0x40, then call the TwoData subroutine -

Code:
;text RAM start address 0x1700

TextHome	movlw		0
		movwf		DATAL
		movlw		0x17
		movwf		DATAH
		movlw		0x40
		movwf		COMMAND
		call		TwoData

Text Area Set (0x42) - This is a two data byte instruction which sets the number of columns of text data the display will support. The required data value for this instruction will vary with display size. Since our display is 240x64, there are a total of 40 columns. This means that we will send the hex value of 0x28 (decimal 40) as the first data byte. On this instruction, the second data byte is always 0x00.

Code:
;text area 40 rows for 240x64 display

TextArea	movlw		0x28
		movwf		DATAL
		movlw		0
		movwf		DATAH
		movlw		0x41
		movwf		COMMAND
		call		TwoData

Offset Register Set (0x22) - This is a two data byte instruction that sets the starting address of the CG RAM space. The T6963C is hard wired to allocate 1KB (1024 bytes) of the VRAM for character generator RAM space and writing a value to the offset register sets the starting address of this space. The values to write to this register will reserve the following address spaces for CG RAM -

0x00 - CG RAM occupies address locations 0x0000 - 0x07FF
0x01 - CG RAM occupies address locations 0x0800 - 0x0FFF
0x02 - CG RAM occupies address locations 0x1000 - 0x17FF
0x03 - CG RAM occupies address locations 0x1800 - 0x1FFF

We are going to allocate locations 0x1800 - 0x1FFF for our CG RAM space. So the first data byte will be 0x03. The second data byte on this instruction is always 0 -

Code:
;set offset register

CGROMSet	movlw		0x03
		movwf		DATAL
		movlw		0
		movwf		DATAH
		movlw		0x22
		movwf		COMMAND
		call		TwoData

Once we have the graphics and text home addresses set, graphics and text area set for the correct size screen, and the CG RAM space allocated, the base display initialization is complete and we are now ready to write data to the display.

Clearing The GLCD Screen

The one thing I had to figure out the hard way was that the VRAM locations in both the text and graphics memory spaces comes up in random states when the GLCD powers up. So we need to code up a routine that will zero out the VRAM. If we don't do this, your display will come up with just a bunch of gibberish and garbage characters.

But before we do this, we need to cover the address pointer set as well as the data auto read/write instructions.

Address Pointer Set (0x24) - This is a two byte instruction that allows you to place the address pointer at a specific VRAM address. Any data read/write instructions will read from/write to the VRAM memory location that the address pointer is set to. The first data byte is the address low byte while the second data byte is the address high byte.

With the exception of the GLCD's status register, the address pointer must be set to the address of the VRAM location you want to read from/write to prior to executing a read or a write instruction. Since setting the address pointer is a task that we will have to do repeatedly, it's best to can it as a subroutine as well. Such a subroutine should look as such -

Code:
ADDR_PTR	movlw		0x24
		movwf		COMMAND
		call		TwoData
		return

Then in main code, you would load DATAL and DATAH with the low byte and high byte of the address that you're setting the pointer to respectively, then call the ADDR_PTR subroutine. For example, say we want to position the address pointer to the first VRAM location of the text memory space, which is addres 0x1700 -

Code:
ADDRPointer	movlw		0x00
		movwf		DATAL
		movlw		0x17
		movwf		DATAH
		call		ADDR_PTR

This will then set the address pointer to the first location in text RAM, and writing a character code to this location will position the character in the very top left hand corner of the display.

The only time we really have to play with the address pointer though is when we're starting a new line of data on the screen. The T6963C has instructions which will autoincrement the pointer as you write data to the display, which makes things convenient.

Data Auto Read/Write (0xB0 - 0xB2) - These instructions are a command only instruction but it works different from all of the other T6963C instructions. What this instruction does is allows us to continuously send data bytes to display consecutively without having to send a "data write" instruction in between each byte. It writes the first byte of data to the display, then autoincrements the address pointer, then writes the next data byte that we send to the display. It continuously does this until we send the Data Auto Reset instruction.

There are 3 instructions in this category -

0xB0 - Data Auto Write Set
0xB1 - Data Auto Read Set
0xB2 - Data Auto Reset

When we want to use auto write mode, we first need to set the address pointer to the starting location in the section of VRAM that we will be writing to, check status bits STA0 and STA1 in the GLCD's status register, then we send the Data Auto Write Set instruction. We then need to check bits STA0 and STA1 again. Once they go high, we then need to check GLCD status bit 3, which is the Data Auto Write Ready status bit DAWRDY. Once this bit is high, we can send the first display data to write, then continuously send the bytes to be written consecutively, checking the DAWRDY bit in between each byte sent, without having to send a write instruction or reposition the address pointer in between each one. In between each data byte that we send to the display in auto write mode, we will be checking the DAWRDY bit instead of STA0 and STA1, which are invalid in Data Auto Read or Data Auto Write mode.

For data auto read mode, the same thing applies but we would want to check bit 2 in the GLCD's status register, which is the DARRDY bit.

So now we need subroutines that will check the DAWRDY and DARRDY bits -

Code:
DARStat		call		StatReadEn		;enable status read
		btfss		D3			;auto write ready?
		goto		$-1
		call		StatReadDis		;disable status read
		return

DARStat		call		StatReadEn		;enable status read
		btfss		D2			;auto write ready?
		goto		$-1
		call		StatReadDis		;disable status read
		return

Like the LCDStat routine, this one calls StatReadEn to enable status read mode. It then continuously polls bit 3 of the GLCD's status register until it goes high. Once high, it then calls the StatReadDis subroutine to disable status read mode, then returns back to the code that called it. We now have a way to poll the DAWRDY bit in between each byte we send to the display. We can continuously send bytes to write until we send the Data Auto Write Reset instruction.

So...back to our clear display routine. So that we can call this routine whenever we want to clear the display, we will do it up as a subroutine -

Code:
DisplayClear	movlw		160			;init counter
		movwf		COUNT1			;to clear 320 VRAM locations
		call		DAWSet
		call		DAWStat
		movlw		0			;write 0 to display
		call		DWrite
		decfsz		COUNT1,F		;decrement COUNT1 and continue writing
		goto		$-4			;data if COUNT1 > 0
		movlw		160			;re-init COUNT1
		movwf		COUNT1			;decrement COUNT2 and continue writing
		decfsz		COUNT2,F		;data if COUNT2 > 0
		goto		$-8
		call		DAWReset
		return					;done

Then in main code, we will load DATAL and DATAH with the starting address byte, load COUNT2 with the number of times we need to run the DisplayClear routine (20x for graphics memory as 160 x 20 = 5120 bytes/8 times for text memory as 160 x 8 = 1280 bytes), then call this routine -

Code:
;clear graphics RAM

		movlw		0			;set address pointer to 0x0000
		movwf		DATAL
		movlw		0x00
		movwf		DATAH
		call		ADDR_PTR
		movlw		20
		movwf		COUNT2
		call		DisplayClear

;clear text RAM

		movlw		0			;set address pointer to 0x1700
		movwf		DATAL
		movlw		0x17
		movwf		DATAH
		call		ADDR_PTR
		movlw		8
		movwf		COUNT2
		call		DisplayClear

The first group of instructions loads DATAL and DATAH with 0x0000, then calls the display clear routine. This will clear out our graphics RAM. Then once it returns, it loads DATAL and DATAH with address 0x1700, which is the start address of the text RAM space, then calls the display clear routine once more.

Now let's walk through the display clear routine itself. Our 240x64 display has a total of 320 character spaces when using a 6x8 font (40 columns x 8 rows). So we need to clear out a total of 320 RAM locations in both the graphics and text memory space. So I have two counters...COUNT1 and COUNT2. COUNT1 is loaded with the value of 160 while COUNT2 is loaded with the value of 2. COUNT1 tells the code to repeat what it's doing 160 times. Once the code has ran 160 times, COUNT1 is preloaded with 160 again and COUNT2 is decremented, then makes it loop back once more to clear out an additional 160 VRAM locations.

Now, DATAL and DATAH should already have the address bytes loaded. So we then call the address pointer set subroutine to set our address pointer to the starting location of the VRAM space that we're clearing.We then send the Data Auto Write instruction.

Once we have sent the Data Auto Write instruction, we then check status bits STA0 and STA1 again. Once those are both high, we then check the DAWRDY bit in the GLCD status register. Once this bit is high, we send the value of 0 to be written to the GLCD, which will write just a blank character to the first display location.

Then COUNT1 gets decremented, and the code continuously loops...checking DAWRDY, then sending a 0 to the GLCD and decrementing COUNT1 each time. Once COUNT1 = 0, it reloads COUNT1 with 160 again, decrements COUNT2, then loops back to continue writing all 0's to each memory location sequentially.

Once COUNT2 = 0, then we check DAWRDY once more, then we send the Data Auto Write Reset instruction, which cancels data auto write mode.

So now the graphics RAM is cleared, but we still need to clear out text RAM. So in main code, we load DATAL with 0x00 and DATAH with 0x17, then call the display clear routine again. The address pointer will then be positioned at the starting location of text RAM.

Cursor

Now we'll get a cursor up on our display. The first instruction to cover with this is the "Cursor Pattern Set" instruction.

Cursor Pattern Set (0xA0 - 0xA7) - This is an instruction only instruction that sets the cursor thickness. You can have a single line cursor, a 2 line cursor, a 3 line cursor, etc etc...all the way up to an 8 line cursor which is a "block" style cursor. The 0xA is the instruction while the 0-7 is the cursor pattern -

Low Nibble = 0 - 1 line cursor
Low Nibble = 1 - 2 line cursor
Low Nibble = 2 - 3 line cursor
Low Nibble = 3 - 4 line cursor
Low Nibble = 4 - 5 line cursor
Low Nibble = 5 - 6 line cursor
Low Nibble = 6 - 7 line cursor
Low Nibble = 7 - 8 line cursor

So let's set our cursor pattern -

Code:
;single line cursor

CSRPattern	movlw		CSR1
		movwf		COMMAND
		call		NoData

Cursor Pointer Set (0x21) - This is a two byte instruction that allows us to tell the GLCD where to position the cursor on the display. The first data byte controls the horizontal movement of the cursor and should be the column number you wish to place the cursor in. The second data byte controls the vertical movement of the cursor and should be the row/line number you wish to place the cursor in. We will be positioning the cursor at the top left corner of the screen so we would set DATAL and DATAH both to 0.

Since we will be moving the cursor around the screen repeatedly, I have canned the cursor pointer set instruction as a subroutine as well -

Code:
CSR_PTR		movlw		CSR_POINTER
		movwf		COMMAND
		call		TwoData
		return


Now we will load DATAL and DATAH with the column/row numbers to position the cursor, then call the cursor pointer set subroutine -

Code:
;set cursor pointer on line 0 column 0

CSRPointer	movlw		0
		movwf		DATAL
		movlw		0
		movwf		DATAH
		call		CSR_PTR

The next thing we need to do is set our address pointer to a default memory location. We'll set it to the start of text RAM at address 0x1700 -

Code:
;position address pointer at start of line 2

ADDRPointer	movlw		0x0
		movwf		DATAL
		movlw		0x0
		movwf		DATAH
		call		ADDR_PTR

Now there is one more thing we need to do before our display will work. We need to set the display mode with the Display Mode Set instruction.

Display Mode Set (0x90 - 0x9F) - This is an instruction only instruction that controls the active/inactive state of the text RAM, graphics RAM, cursor, and cursor blink. The available display modes are as follows -

Low Nibble = 0 - Display Off
Low Nibble = 4 - Text RAM Only, no cursor
Low Nibble = 6 - Text RAM only, cursor on, cursor blink off
Low Nibble = 7 - Text RAM only, cursor on, cursor blink on
Low Nibble = 8 - Graphics RAM only, no cursor
Low Nibble = A - Graphics RAM only, cursor on, cursor blink off
Low Nibble = B - Graphics RAM only, cursor on, cursor blink on
Low Nibble = C - Text and Graphics RAM, no cursor
Low Nibble = E - Text and Graphics RAM, cursor on, cursor blink off
Low Nibble = F - Text and Graphics RAM, cursor on, cursor blink on

In text attribute mode, both the graphics and text RAM must be on since the T6963C stores the attribute data in graphics RAM. No graphics are possible in this mode. So we will be setting this up for mode F, which will turn on both text and graphics RAM as well as the cursor and the cursor blink -

Code:
DisplayOn	movlw		0x9F
		movwf		COMMAND
		call		NoData

		goto		$

Now the last things to get added to our code are our delay subroutines and the starting template for the interrupt handler just in case we plan to do interrupts. The way I do it is that I have one delay routine that is a fixed 50mS delay loop. Then I have a 2nd delay loop that is variable that calls the 50mS delay multiple times for longer delays. How it works is that you preload W with the number of times you need to run the 50mS delay loop, then you call the variable delay subroutine. Once in that subroutine, the preloaded value in W gets dropped into a third counter that counts how many times you call the 50mS delay loop -

Code:
;fixed 50mS delay

Delay50mS	movlw		0xFF
		movwf		COUNT1
		movwf		COUNT2
		decfsz		COUNT1,F
		goto		$-1
		decfsz		COUNT2,F
		goto		$-3
		return

;*************************************************************************************************

;variable delay

Delay		movwf		COUNT3
		call		Delay50mS
		decfsz		COUNT3,F
		goto		$-2
		return

This delay code as written will yield a 50mS delay loop assuming a 16MHz Fosc (4MHz instruction clock). To adjust it for slower clock speeds, add an instruction that loads buffer COUNT2 with the following values -

Fosc = 4MHz - 0x40
Fosc = 8MHz - 0x7F
Fosc = 12MHz - 0xC0

And last but not least, here is a suitable template to build on for an interrupt handler -

Code:
;*************************************************************************************************
;**												**
;**				Interrupt Handler						**
;**												**
;*************************************************************************************************

;interrupt context save

ISR		movwf		W_TEMP
		swapf		STATUS,W
		banksel		0			;bank 0
		movwf		STATUS_TEMP
		movfw		PCLATH
		movwf		PCLATH_TEMP

;interrupt code goes here

;interrupt context restore

ISRExit		movfw		PCLATH_TEMP
		movwf		PCLATH
		swapf		STATUS_TEMP,W
		movwf		STATUS
		swapf		W_TEMP,F
		swapf		W_TEMP,W
		retfie

The Complete Code

OK...if you've been following along with the code examples and typing out your own version of this code, you should end up with a completed code file that resembles this -

Code:
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**				16F887 Graphic LCD Driver					**
;**			     For Toshiba T6963C 240 x 64 GLCD					**
;**				       By Jon Wilder						**
;**			              Date: 12/01/2011						**
;**												**
;*************************************************************************************************
;*************************************************************************************************
;**												**
;**				Header Information/Config Options				**
;**												**
;**												**
;**												**
;** Processor Type: PIC 16F887									**
;** Default Radix: Decimal									**
;** Error Level: -302/Suppress all assembler bank select warnings				**
;** Reference header file P16F887.INC for SFR and Config option labels				**
;**												**
;** Configuration Word 1									**
;**												**
;** In Circuit Debug Off (Default)								**
;** Low Voltage Programming Off									**
;** Fail Safe Clock Monitor Off 								**
;** Internal External Switchover Off								**
;** Brown Out Reset Off										**
;** Data Code Protection Off (Default)								**
;** Code Protection Off (Default)								**
;** RA5 has MCLR Function (Default)								**
;** Power Up Timer On										**
;** Watchdog Timer Off										**
;** High Speed XT Oscillator									**
;**												**
;** Configuration Word 2									**
;**												**
;** Program ROM Write Protection Off (Default)							**
;** Brown Out Reset 4.0V (Default)								**
;**												**
;** Fosc = 16MHz										**
;**												**
;*************************************************************************************************
;*************************************************************************************************

		list		p=16F887, r=dec, w=-302
		include		<P16F887.INC>
		__config	_CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _PWRTE_ON & _WDT_OFF & _FOSC_HS

;*************************************************************************************************
;**												**
;**				RAM Location Constants						**
;**												**
;*************************************************************************************************

		cblock		0x20
				TEMP			;temp buffer
				DATAL			;instruction data low byte
				DATAH			;instruction data high byte
				COMMAND			;instruction
				TABLECOUNT		;table counter
		endc

		cblock		0x70
				W_TEMP			;interrupt context save for W
				STATUS_TEMP		;interrupt context save for STATUS
				PCLATH_TEMP		;interrupt context save for PCLATH
				COUNT1			;delay counter 1
				COUNT2			;delay counter 2
				COUNT3			;delay counter 3
				DATA_EE_ADDR		;data EEPROM address buffer
				DATA_EE_DATA		;data EEPROM data buffer
		endc

;*************************************************************************************************
;**												**
;**				Control/Data Line Labels					**
;**												**
;*************************************************************************************************

;control lines

#define		CD		PORTA,RA0		;GLCD command/data
#define		RST		PORTA,RA1		;GLCD reset
#define		FONT		PORTA,RA2		;GLCD font select (6x8 or 8x8)
#define		WRITE		PORTC,RC0		;GLCD write enable/disable (active low)
#define		READ		PORTC,RC1		;GLCD read enable/disable (active low)
#define		CE		PORTC,RC2		;GLCD chip enable/disable (active low)

WR_EN		EQU 2
WR_DIS		EQU 7
RD_EN		EQU 1
RD_DIS		EQU 7

;data port

#define		D0		PORTD,RD0		;GLCD data port bit 0
#define		D1		PORTD,RD1		;GLCD data port bit 1
#define		D2		PORTD,RD2		;GLCD data port bit 2
#define		D3		PORTD,RD3		;GLCD data port bit 3
#define		D4		PORTD,RD4		;GLCD data port bit 4
#define		D5		PORTD,RD5		;GLCD data port bit 5
#define		D6		PORTD,RD6		;GLCD data port bit 6
#define		D7		PORTD,RD7		;GLCD data port bit 7

;*************************************************************************************************
;**												**
;**				Start of Main Code						**
;**												**
;*************************************************************************************************

		org 		0x000			;reset vector
		goto		START			;jump to start of main code

		org 		0x004			;interrupt vector
		goto		ISR			;jump to start of interrupt handler

;*************************************************************************************************
;**												**
;**				Initialization Routine						**
;**												**
;*************************************************************************************************

START		clrf		PORTA			;init ports
		clrf		PORTB
		clrf		PORTC
		clrf		PORTD
		clrf		PORTE
		banksel		ANSEL			;bank 3
		clrf		ANSEL			;all ports digital I/O
		clrf		ANSELH
		banksel		TRISA			;bank 1
		clrf		TRISA			;PORTA, PORTB, PORTC, and PORTE outputs
		clrf		TRISB			;PORTD defaults to input
		clrf		TRISC
		clrf		TRISE
		banksel		0 			;bank 0

GLCD_INIT	bcf		RST			;reset GLCD
		movlw		5 			;Command mode and 6x8 font
		movwf		PORTA
		movlw		7 			;disable read, write, and chip enable
		movwf		PORTC
		call		Delay50mS		;wait 50mS
		bsf		RST			;disable reset

;set operating mode

ModeSet		movlw		0x84			;text attribute mode with CG ROM on
		movwf		COMMAND
		call		NoData


;graphics RAM start address 0x0000

GraphicsHome	movlw		0
		movwf		DATAL
		movlw		0
		movwf		DATAH
		movlw		0x42
		movwf		COMMAND
		call		TwoData

;graphics area 40 rows for 240x64 display

GraphicsArea	movlw		0x28
		movwf		DATAL
		movlw		0
		movwf		DATAH
		movlw		0x43
		movwf		COMMAND
		call		TwoData

;text RAM start address 0x1700

TextHome	movlw		0
		movwf		DATAL
		movlw		0x17
		movwf		DATAH
		movlw		0x40
		movwf		COMMAND
		call		TwoData

;text area 40 rows for 240x64 display

TextArea	movlw		0x28
		movwf		DATAL
		movlw		0
		movwf		DATAH
		movlw		0x41
		movwf		COMMAND
		call		TwoData

;set offset register

CGROMSet	movlw		0x03
		movwf		DATAL
		movlw		0
		movwf		DATAH
		movlw		0x22
		movwf		COMMAND
		call		TwoData

;clear graphics RAM

		movlw		0			;set address pointer to 0x0000
		movwf		DATAL
		movlw		0x00
		movwf		DATAH
		call		ADDR_PTR
		movlw		20
		movwf		COUNT2
		call		DisplayClear

;clear text RAM

		movlw		0			;set address pointer to 0x1700
		movwf		DATAL
		movlw		0x17
		movwf		DATAH
		call		ADDR_PTR
		movlw		8
		movwf		COUNT2
		call		DisplayClear

;single line cursor

CSRPattern	movlw		0xA0
		movwf		COMMAND
		call		NoData

;set cursor pointer on line 0 column 0

CSRPointer	movlw		0
		movwf		DATAL
		movlw		0
		movwf		DATAH
		call		CSR_PTR

;position address pointer at start of line 2

ADDRPointer	movlw		0x0
		movwf		DATAL
		movlw		0x0
		movwf		DATAH
		call		ADDR_PTR

;text and graphics RAM on, cursor on with cursor blink

DisplayOn	movlw		0x9F
		movwf		COMMAND
		call		NoData

		goto		$

;*************************************************************************************************
;**												**
;**				Delay Loops							**
;**												**
;*************************************************************************************************

;fixed 50mS delay

Delay50mS	movlw		0xFF
		movwf		COUNT1
		movwf		COUNT2
		decfsz		COUNT1,F
		goto		$-1
		decfsz		COUNT2,F
		goto		$-3
		return

;*************************************************************************************************

;variable delay

Delay		movwf		COUNT3
		call		Delay50mS
		decfsz		COUNT3,F
		goto		$-2
		return

;*************************************************************************************************
;**												**
;**				Position Address Pointer					**
;**												**
;*************************************************************************************************

ADDR_PTR	movlw		0x24
		movwf		COMMAND
		call		TwoData
		return

;*************************************************************************************************
;**												**
;**				Position Cursor Pointer						**
;**												**
;*************************************************************************************************

CSR_PTR		movlw		0x21
		movwf		COMMAND
		call		TwoData
		return

;*************************************************************************************************
;**												**
;**				Command Send Routines						**
;**												**
;*************************************************************************************************

TwoData		call		LCDStat
		movfw		DATAL
		call		DWrite
OneData		call		LCDStat
		movfw		DATAH
		call		DWrite
NoData		call		LCDStat
		movfw		COMMAND
		call		Command
		return

;*************************************************************************************************
;**												**
;**				GLCD Read/Write/Status Check					**
;**												**
;*************************************************************************************************

;command/data write

Command		bsf		CD			;command mode
		goto		$+2
DWrite		bcf		CD			;data mode
		movwf		PORTD			;place write data on data port latch
		movlw		WR_EN			;chip enable low
		movwf		PORTC			;enable write
		banksel		TRISD			;bank 1
		clrf		TRISD			;RD0-RD7 output
		banksel		0			;bank 0
		movlw		WR_DIS			;disable write
		movwf		PORTC			;chip enable high
		banksel		TRISD			;bank 1
		comf		TRISD,F			;RD0-RD7 input
		banksel		0			;bank 0
		return

;*************************************************************************************************

;check GLCD status

LCDStat		call		StatReadEn		;enable status read
		btfsc		D0			;is GLCD ready?
		btfss		D1
		goto		$-2			;no, check again
		call		StatReadDis		;disable status read
		return

;*************************************************************************************************

;check data auto write ready bit

DAWStat		call		StatReadEn		;enable status read
		btfss		D3			;auto write ready?
		goto		$-1
		call		StatReadDis		;disable status read
		return

;*************************************************************************************************

;GLCD status read enable

StatReadEn	bsf		CD			;command mode
		movlw		RD_EN			;enable read
		movwf		PORTC
		return

;*************************************************************************************************

;GLCD status read disable

StatReadDis	movlw		RD_DIS			;disable read
		movwf		PORTC
		return

;*************************************************************************************************
;**												**
;**				    Clear Display						**
;**												**
;*************************************************************************************************

DisplayClear	movlw		160			;init counter
		movwf		COUNT1			;to clear 320 VRAM locations
		call		DAWSet
		call		DAWStat
		movlw		0			;write 0 to display
		call		DWrite
		decfsz		COUNT1,F		;decrement COUNT1 and continue writing
		goto		$-4			;data if COUNT1 > 0
		movlw		160			;re-init COUNT1
		movwf		COUNT1			;decrement COUNT2 and continue writing
		decfsz		COUNT2,F		;data if COUNT2 > 0
		goto		$-8
		call		DAWReset
		return					;done

;*************************************************************************************************
;**												**
;**				Interrupt Handler						**
;**												**
;*************************************************************************************************

;interrupt context save

ISR		movwf		W_TEMP
		swapf		STATUS,W
		banksel		0			;bank 0
		movwf		STATUS_TEMP
		movfw		PCLATH
		movwf		PCLATH_TEMP

;interrupt code goes here

;interrupt context restore

ISRExit		movfw		PCLATH_TEMP
		movwf		PCLATH
		swapf		STATUS_TEMP,W
		movwf		STATUS
		swapf		W_TEMP,F
		swapf		W_TEMP,W
		retfie

		end

Now...if you are using the PIC16F887, you go and build the code and load it into your PIC, you have your PIC hooked up as I have described above and you follow the power up sequence exactly as we discussed it above, you should end up with a blank display with a flashing single line cursor in the top left hand corner of the display. If you're not using the PIC16F887, some minor adjustments will need to be made to the config word and init routines to port it over to another PIC, but it shouldn't be too difficult to do. We have just initialized the display and it is now ready to accept data and display text.

As a test, I wrote up some code to display a test message -

100_0054.jpg
  • Like
Reactions: ercan517
Author
Jon Wilder
Views
3,979
First release
Last update
Rating
0.00 star(s) 0 ratings

More resources from Jon Wilder

Latest threads

New Articles From Microcontroller Tips

Back
Top