[TUT][SOFT] Using the KS0713/S6B1713/ST7565 LCD driver chips

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Introduction

These three chips are generally posted as equivalent. Looking at the datasheets, they're not *quite* equivalent but all use the same instruction set and the only significant difference is in the contrast voltage settings. For brevity I'll refer to the KS chip throughout (though Samsung have now stopped making small LCD displays and drivers).

The KS0713 is used as a single controller chip for LCD monochrome graphic displays of up to 65 by 128 pixels. Each pixel is individually addressable. The chip can be used in parallel mode - either 8080 flavour with ~RD and ~WR lines, or 6800 flavour with E and R/~W lines - or as a serial device, though the latter does not provide read capability. The choice of serial or parallel interface is often selected using a jumper or solder-pad on the display PCB while the 8080/6800 choice is extended to the user connections. Before you start playing, have a good look at the circuit diagram for the display to find out what you've got.

This tutorial deals only with using the Displaytech 64128E - http://uk.rs-online.com/web/sear... - in the 8080 parallel mode. That is, the MI ('C86' on the ST chip) pin is tied low. For other displays, you'll have to do some research. I also restrict myself to *writing* to the display.

Display Specifications

Before you can do anything else, there are a few critical things you need to know. In particular, the duty cycle, the bias, and the working voltage (Vdd-Vo) for the display.

The Duty Cycle is set by the manufacturer and is hard wired onto the controller - you're unlikely to be able to change this. For a 64*128 display this will be either 1/64 or 1/65 - for smaller displays this will be different.

The Bias controls the working voltages delivered to the display (LCDs work by switching various levels of DC to rows and columns; it's not just an on-off thing). For a 64*128 this will be either 1/7 or 1/9.

The Operating Voltage provides the contrast for the display; too low or too high and you will see an all-black or all-white display. This voltage will be in the display data sheet.

The chip has an on-board DC-DC converter which will generate the negative voltages required. In fact, it has three separate sections which can be used as required; the voltage booster, the voltage regulator, and a voltage follower. Each can be individually switched.

I would expect most current displays - at least, those mounted on PCBs - to use the internal booster. The giveaway is whether there are a group of three to five capacitors placed on the various 'CAPx' pins: if they're there, the maker is being nice to you and you will not need an external voltage. Refer to the data sheet, and you'll see that the 64128E does in fact use this method. (Note - the KS chip can do *5 voltage boosting, the ST only *4. This is not a problem as they run from 3v and the displays will expect around nine volts Vo).

If you need to run the display from externally generated voltages, I'm afraid you're on your own. Please read the datasheets...

The Commands
I won't go into detail here - in fact, I won't cover all the commands available - but there should be enough here to explain the more confusing options.

Commands are written to the chip when the RS pin (A0 on the ST) is high; data is written when the pin is low. Other than that, writing commands or data are identical. For the hex values to use, refer to the tables in the datasheets.

Display On/Off does what it says on the label. You will generally want the display 'On'.

Initial Display Line will usually be zero, but you can scroll the display by changing this value.

Set Reference Voltage Mode and Set Reference Voltage are two instructions which must be used sequentially. The first puts the chip into the reference mode; the second instruction is the data which must be sent to select the voltage. This is one of the voltages which you will use to change the fine contrast of the display.

Set Page Address The display memory is organised as nine strips of 128 bytes across the display, with each byte occupying one column and eight horizontal rows. Bit zero is at the top. The ninth strip only has bit zero and is only displayed on 65-row displays. These strips are referred to as pages by the makers; setting the page address tells the chip which row of eight to use. Row zero is at the top of the display - provided that the initial display line is zero. Otherwise, it will move down the screen as commanded.

Set Column Address Low and High These two commands are used independently to set the column which will be written to or read from by the next read or write data instruction. Each holds four bits of the eight bit address, though only the lower (total) seven bits are significant.

ADC Select To simplify manufacture of the display, the chip can be configured to scan from left to right or right to left. The ADC Select command selects which should be used. If you get this wrong, your display will be left-right mirrored. For the 64128E, it should be set for normal display.

Reverse Display On/Off sets whether a '1' in a data byte will be displayed as a bright spot or a dark spot. The default is for bright pixels against a dark ground.

Entire Display On/Off Try not to touch this one. If it's 'on' then every pixel is lit on the display. If it's off, then the display is normal. If this is set to 'on' and Display On/Off is set to 'off' the display will go into a power save mode.

LCD Bias Select This needs to be set to suit the specification in the data sheet. For the 64128E it needs to be 1/9. Note that there's only a single bit to select the bias; the options for that bit depend on the hardware defined duty cycle.

SHL Select This behaves in the same way as ADC Select, but it operates vertically rather than horizontally.

Power Control Configures the voltage booster, regulator, and follower. Refer to the data sheet for more precise details; I merely note that the 64128E requires all three to be turned on.

Regulator Resistor Select This is the 'coarse' control for the contrast. Each chip datasheet has a graph which indicates the range of voltages for each resistor select value; these graphs are different for each chip and I'm not sure which is correct. For the 64128E the correct value is '5' - but you may have to experiment.

There are other control commands; they are all detailed in the data sheets. These are all you need to get most displays running.

Writing to the display
As noted above, writing to the screen memory is a matter of selecting the page, followed by the column - usually both column select instructions. Then, simply write your data: bit 0 will be at the top of the display page, if ADC and SHL are normal. The column pointer is incremented automatically after each write, so if you are moving bulk data to the display it is only a matter of repeated writes. This is also very useful if you are writing a text character; simply send the character coded as vertical slices.

Reference code

//***************************************************************************
//* Port Pin Assignments
//***************************************************************************

// pc0-7 ->	d0-7

// note that we have wired the display in 8080 mode
// so wr and rd rather than r/~w and e
#define	cs  PA3		/* chip select  */
#define	rst PA4		/* chip reset   */
#define	rs  PA5		/* data/control */
#define	wr  PA6		/* write        */
#define	rd  PA7		/* read         */
	
void lcd_control(unsigned char control)
{
	// write a control value to the KS0713
	// rst must remain high throughout
	// rs goes low
	// cs and wr go low 
	// data written to port c
	// cs and wr go high
	// rs goes high (rs moves before and after
	// the other lines to meet timing constraints)

	PORTA &= ~_BV(rs);
	PORTA &= ~_BV(cs);
	PORTA &= ~_BV(wr);
	PORTC = control;
	PORTA |= _BV(wr);
	PORTA |= _BV(cs);
}
	
void lcd_write(char data)
{
	// writes arg0 to the currently selected column of the display
	// rs high for data

	PORTA |= _BV(rs);
	PORTA &= ~_BV(cs);
	PORTA &= ~_BV(wr);
	PORTC = data;
	PORTA |= _BV(wr);
	PORTA |= _BV(cs);
}

void lcd_init(void)
{
	// reset the display and clear it
int q;
	
	// first, force a hardware reset
	// there isn't a spec for how long this requires 
	// - it's suggested to tie it to the CPU reset -
	// but it has to wait until power is stable
	// so we'll delay for 100ms

	// set the ports to be outputs
	DDRA = 0xff;
	DDRC = 0xff;

	// with control lines all high except reset
	PORTA = (~(_BV(rst)));
	// startup delay of 100ms
	for (q=0; q<4; q++)
		_delay_loop_2(50000);
	// and rst high again
	PORTA = 0xff;

	lcd_control(0xA2);		// <- Bias 1/9
	lcd_control(0xA0);		// <- ADC Direction L-R
	lcd_control(0xC0);		// <- SHL Direction 0-64
	lcd_control(0x25);		// <- Voltage ref
	lcd_control(0x81);		// <- Volume mode
	lcd_control(0x20);		// <- Volume set
	lcd_control(0x00);		// <- This probably isn't required here - it's set below
	lcd_control(0x2F);		// <- Vf, Vr, Vc on
	lcd_control(0x40);		// <- Initial display line
	lcd_control(0xA7);		// <- Normal display
	lcd_control(0xAF);		// <- turn display on

	lcd_control(0xB0);		// <- page address = 0
	lcd_control(0x10);		// <- column address high = 0
	lcd_control(0x00);		// <- column address low = 0

	// clear screen
	lcd_cls();
		
	return;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

whats shematikc board ?
tnks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
// pc0-7 ->   d0-7

// note that we have wired the display in 8080 mode
// so wr and rd rather than r/~w and e
#define   cs  PA3      /* chip select  */
#define   rst PA4      /* chip reset   */
#define   rs  PA5      /* data/control */
#define   wr  PA6      /* write        */
#define   rd  PA7      /* read         */ 

The data lines are on port C, the control lines on port A. If yours is different, you'll have to change the code slightly to accomodate.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was wrapping my head around the pixel addressing. I was thinking it was

111111112222222233333333
hhhhhhhhjjjjjjjjkkkkkkkk
where h=16
j=17
k=18

that is, each byte written in "continuous byte mode" would be a set of bits laid onto the screen horizontally, like I would expect on a PC.

But as I look at the data sheet more... I think it's the other way. As in:

1234
1234
1234
1234
1234
1234
1234
1234

that is, each byte you write is laid on the screen horizontally.

So, I was originally thinking that setting a bit at (3,1) would be a byte of 0b00010000 written at 0,1 but now I see that it would actually be a byte of ob01000000 written at 3,0.

Interesting twist! :)

Cheers,
Anders

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anders, you're right; each byte is laid down as eight vertical pixels.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

By request, here's a more complete example. This is for four lines of 5*11 characters on a 128*64 display...

This is still not complete, but should help. I only have this single example and it won't make a huge amount of sense but see how you go.

void lcd_control(unsigned char control);

void lcd_set_cursor(unsigned char cx, unsigned char cy);

void lcd_set_column(unsigned char cx, unsigned char cy);

void lcd_write(char data);

void lcd_init(void);

void lcd_cls(void);

void lcd_char (char);

void lcd_print (char *);

void lcd_pprint (prog_char * text);

void lcd_picture (prog_char *);

void lcd_bar (char, char, char, char, char);

void lcd_tbar (char, char, char, char, char);

void lcd_bbar (char, char, char, char, char);


const unsigned char font[] PROGMEM = {

	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 	// ' '
	0x00, 0x00, 0x00, 0x00, 0x0B, 0xF8, 0x00, 0x00, 0x00, 0x00, 	// '!'
	0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 	// '"'
	0x02, 0x20, 0x0F, 0xF8, 0x02, 0x20, 0x0F, 0xF8, 0x02, 0x20, 	// '#'
	0x04, 0x60, 0x08, 0x90, 0x1F, 0xF8, 0x09, 0x10, 0x06, 0x20, 	// '$'
	0x06, 0x10, 0x01, 0x28, 0x04, 0x90, 0x0A, 0x40, 0x04, 0x30, 	// '%'
	0x07, 0x30, 0x08, 0xC8, 0x09, 0x30, 0x06, 0x00, 0x09, 0x00, 	// '&'
	0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 	// '''
	0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x10, 0x04, 0x00, 0x00, 	// '('
	0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x0F, 0xF8, 0x00, 0x00, 	// ')'
	0x01, 0x10, 0x00, 0xA0, 0x03, 0xF8, 0x00, 0xA0, 0x01, 0x10, 	// '*'
	0x01, 0x00, 0x01, 0x00, 0x07, 0xC0, 0x01, 0x00, 0x01, 0x00, 	// '+'
	0x00, 0x00, 0x2C, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 	// ','
	0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 	// '-'
	0x0C, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 	// '.'
	0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x38, 0x00, 0x00, 	// '/'
	0x07, 0xF0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0xF0, 	// '0'
	0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x0F, 0xF8, 0x00, 0x00, 	// '1'
	0x0C, 0x10, 0x0A, 0x08, 0x09, 0x08, 0x08, 0x88, 0x08, 0x70, 	// '2'
	0x04, 0x10, 0x08, 0x08, 0x08, 0x88, 0x08, 0x88, 0x07, 0x70, 	// '3'
	0x03, 0x00, 0x02, 0xC0, 0x02, 0x30, 0x0F, 0xF8, 0x02, 0x00, 	// '4'
	0x04, 0xF8, 0x08, 0x48, 0x08, 0x48, 0x08, 0x48, 0x07, 0x88, 	// '5'
	0x07, 0xF0, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x07, 0x10, 	// '6'
	0x00, 0x08, 0x0E, 0x08, 0x01, 0x88, 0x00, 0x68, 0x00, 0x18, 	// '7'
	0x07, 0x70, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x07, 0x70, 	// '8'
	0x04, 0x70, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x07, 0xF0, 	// '9'
	0x0C, 0xC0, 0x0C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 	// ':'
	0x00, 0x00, 0x2C, 0xC0, 0x1C, 0xC0, 0x00, 0x00, 0x00, 0x00, 	// ';'
	0x01, 0x00, 0x02, 0x80, 0x04, 0x40, 0x08, 0x20, 0x00, 0x00, 	// '<'
	0x02, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x80, 	// '='
	0x08, 0x20, 0x04, 0x40, 0x02, 0x80, 0x01, 0x00, 0x00, 0x00, 	// '>'
	0x00, 0x10, 0x00, 0x08, 0x0B, 0x08, 0x00, 0x88, 0x00, 0x70, 	// '?'
	0x01, 0xE0, 0x02, 0x10, 0x04, 0xC8, 0x05, 0x28, 0x02, 0xF0, 	// '@'
	0x0F, 0x00, 0x02, 0xE0, 0x02, 0x18, 0x02, 0xE0, 0x0F, 0x00, 	// 'A'
	0x0F, 0xF8, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x07, 0x70, 	// 'B'
	0x07, 0xF0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, 0x10, 	// 'C'
	0x0F, 0xF8, 0x08, 0x08, 0x08, 0x08, 0x04, 0x10, 0x03, 0xE0, 	// 'D'
	0x0F, 0xF8, 0x08, 0x88, 0x08, 0x88, 0x08, 0x08, 0x00, 0x00, 	// 'E'
	0x0F, 0xF8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x08, 0x00, 0x00, 	// 'F'
	0x07, 0xF0, 0x08, 0x08, 0x08, 0x88, 0x04, 0x88, 0x0F, 0x90, 	// 'G'
	0x0F, 0xF8, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x0F, 0xF8, 	// 'H'
	0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 	// 'I'
	0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x07, 0xF8, 0x00, 0x00, 	// 'J'
	0x0F, 0xF8, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x10, 0x0C, 0x08, 	// 'K'
	0x0F, 0xF8, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 	// 'L'
	0x0F, 0xF8, 0x00, 0x70, 0x01, 0x80, 0x00, 0x70, 0x0F, 0xF8, 	// 'M'
	0x0F, 0xF8, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0F, 0xF8, 	// 'N'
	0x07, 0xF0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0xF0, 	// 'O'
	0x0F, 0xF8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x70, 	// 'P'
	0x07, 0xF0, 0x08, 0x08, 0x0A, 0x08, 0x0C, 0x08, 0x17, 0xF0, 	// 'Q'
	0x0F, 0xF8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x0F, 0x70, 	// 'R'
	0x04, 0x70, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x07, 0x10, 	// 'S'
	0x00, 0x08, 0x00, 0x08, 0x0F, 0xF8, 0x00, 0x08, 0x00, 0x08, 	// 'T'
	0x07, 0xF8, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x07, 0xF8, 	// 'U'
	0x00, 0x38, 0x03, 0xC0, 0x0C, 0x00, 0x03, 0xC0, 0x00, 0x38, 	// 'V'
	0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 	// 'W'
	0x0E, 0x38, 0x01, 0x40, 0x00, 0x80, 0x01, 0x40, 0x0E, 0x38, 	// 'X'
	0x00, 0x38, 0x00, 0x40, 0x0F, 0x80, 0x00, 0x40, 0x00, 0x38, 	// 'Y'
	0x0C, 0x08, 0x0B, 0x08, 0x08, 0x88, 0x08, 0x68, 0x08, 0x18, 	// 'Z'
	0x00, 0x00, 0x1F, 0xFC, 0x10, 0x04, 0x00, 0x00, 0x00, 0x00, 	// '['
	0x00, 0x00, 0x00, 0x38, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 	// '\'
	0x00, 0x00, 0x10, 0x04, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, 	// ']'
	0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 	// '^'
	0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 	// '_'
	0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 	// '`'
	0x06, 0x00, 0x09, 0x40, 0x09, 0x40, 0x09, 0x40, 0x0F, 0x80, 	// 'a'
	0x0F, 0xF8, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x07, 0x80, 	// 'b'
	0x07, 0x80, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x04, 0x80, 	// 'c'
	0x07, 0x80, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x0F, 0xF8, 	// 'd'
	0x07, 0x80, 0x09, 0x40, 0x09, 0x40, 0x09, 0x40, 0x05, 0x80, 	// 'e'
	0x0F, 0xF0, 0x00, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x10, 	// 'f'
	0x27, 0x80, 0x28, 0x40, 0x28, 0x40, 0x28, 0x40, 0x1F, 0xC0, 	// 'g'
	0x0F, 0xF8, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x0F, 0x80, 	// 'h'
	0x00, 0x00, 0x00, 0x00, 0x0F, 0xC8, 0x00, 0x00, 0x00, 0x00, 	// 'i'
	0x20, 0x00, 0x20, 0x00, 0x1F, 0xC8, 0x00, 0x00, 0x00, 0x00, 	// 'j'
	0x0F, 0xF8, 0x01, 0x00, 0x02, 0x80, 0x04, 0x40, 0x08, 0x00, 	// 'k'
	0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 	// 'l'
	0x0F, 0xC0, 0x00, 0x40, 0x0F, 0x80, 0x00, 0x40, 0x0F, 0x80, 	// 'm'
	0x0F, 0xC0, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x0F, 0x80, 	// 'n'
	0x07, 0x80, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x07, 0x80, 	// 'o'
	0x3F, 0xC0, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x07, 0x80, 	// 'p'
	0x07, 0x80, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x3F, 0xC0, 	// 'q'
	0x0F, 0xC0, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 	// 'r'
	0x04, 0x80, 0x09, 0x40, 0x0A, 0x40, 0x04, 0x80, 0x00, 0x00, 	// 's'
	0x07, 0xF0, 0x08, 0x40, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 	// 't'
	0x07, 0xC0, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0F, 0xC0, 	// 'u'
	0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0, 	// 'v'
	0x01, 0xC0, 0x0E, 0x00, 0x01, 0xC0, 0x0E, 0x00, 0x01, 0xC0, 	// 'w'
	0x0C, 0xC0, 0x03, 0x00, 0x03, 0x00, 0x0C, 0xC0, 0x00, 0x00, 	// 'x'
	0x20, 0x00, 0x23, 0xC0, 0x1C, 0x00, 0x04, 0x00, 0x03, 0xC0, 	// 'y'
	0x0C, 0x40, 0x0A, 0x40, 0x09, 0x40, 0x08, 0xC0, 0x00, 0x00, 	// 'z'
	0x00, 0x00, 0x00, 0x80, 0x0F, 0x78, 0x10, 0x04, 0x00, 0x00, 	// '{'
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 	// '|'
	0x00, 0x00, 0x10, 0x04, 0x0F, 0x78, 0x00, 0x80, 0x00, 0x00, 	// '}'
	0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 	// '~'
	0x00, 0x38, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x38, 	// ''
	};

///////////////////////////////////////////////////////////////////////////////////////////

//

// LCD control functions

//

// we're controlling a Displaytech 64128 driven by a single KS0713 chip

// we use a static variable 'column' to define which column we're writing

// the device is hard wired to use the 8080 interface - i.e. separate ~rd and ~wr

//

// void lcd_control(unsigned char)		- write to display control chip

// void lcd_set_column(unsigned char)	- set the start address for the display

// void lcd_set_cursor(unsigned char x, unsigned char y)	

//										- position the text cursor

// void lcd_write(unsigned char)		- write to the display based on 'column'

// lcd_read								- return the data from the display (not yet implemented)

// void lcd_init(void)					- reset the display and clear it

// void lcd_cls(void)					- clear the display

// void lcd_scroll(void)				- move the cursor to the next line

// void lcd_char(char)					- write a character to the display

// void lcd_string(* char)				- write a zero-terminated string to the display

// void lcd_bar(unsigned char width,

//				unsigned char value,

//				unsigned char separation,

//				unsigned char row)		- draw a bar-graph (sideways)

//

///////////////////////////////////////////////////////////////////////////////////////////



static uint8_t column;

static uint8_t cursorx;

static uint8_t cursory;



char tmp[20];



//***************************************************************************

//* Port Pin Assignments

//***************************************************************************



// pc0-7 ->	d0-7



// note that we have wired the display in 8080 mode

// so wr and rd rather than r/~w and e

#define	cs  PA3		/* chip select  */

#define	rst PA4		/* chip reset   */

#define	rs  PA5		/* data/control */

#define	wr  PA6		/* write        */

#define	rd  PA7		/* read         */

	

void lcd_control(unsigned char control)

{

	// write a control value to the KS0713

	// rst must remain high throughout

	// rs goes low

	// cs and wr go low 

	// data written to port c

	// cs and wr go high

	// rs goes high (rs moves before and after

	// the other lines to meet timing constraints)



	PORTA &= ~_BV(rs);

	PORTA &= ~_BV(cs);

	PORTA &= ~_BV(wr);

	PORTC = control;

	PORTA |= _BV(wr);

	PORTA |= _BV(cs);

}

	

void lcd_write(char data)

{

	// writes arg0 to the currently selected column of the display

	// rs high for data



	PORTA |= _BV(rs);

	PORTA &= ~_BV(cs);

	PORTA &= ~_BV(wr);

	PORTC = data;

	PORTA |= _BV(wr);

	PORTA |= _BV(cs);

}



void lcd_set_cursor(unsigned char cx, unsigned char cy)

{

	// put the cursor on a specified cell, and set the column value at the same time

	// we have eight rows to play with, 128 columns but we use double-height characters

	// so we have four by twenty rows of characters

	cursorx = cx;

	cursory = cy;

	

	lcd_set_column(cx*6, cy*2);

}



void lcd_set_column(unsigned char cx, unsigned char cy)

{

	// set the controller chip data to match the required column and row

	// as all the code expects 120 pixels, and we have 128

	// we will offset four to the right to centralise stuff

	// and give a bit of a border to improve legibility

	column = cx;

	// the column must be set in two writes, with four bits each

	// lsb

	lcd_control((cx+4) & 0x0f);									// <-0000 3210

	// msb

	lcd_control((((cx+4) & 0xf0) >> 4) | 0x10);	// <-0001 7654



	// only a single write for the row 

	// there's actually a ninth row of one bit which we don't use

	lcd_control((cy & 7) | 0xb0);						// <-1011 3210

	return;

}



void lcd_init(void)

{

	// reset the display and clear it

int q;

	

	// first, force a hardware reset

	// there isn't a spec for how long this requires 

	// - it's suggested to tie it to the CPU reset -

	// but it has to wait until power is stable

	// so we'll delay for 100ms



	// set the ports to be outputs

	DDRA = 0xff;

	DDRC = 0xff;



	// with control lines all high except reset

	PORTA = (~(_BV(rst)));

	// startup delay of 100ms

	for (q=0; q<4; q++)

		_delay_loop_2(50000);

	// and rst high again

	PORTA = 0xff;



	lcd_control(0xA2);		// <- Bias 1/9

	lcd_control(0xA0);		// <- ADC Direction L-R

	lcd_control(0xC0);		// <- SHL Direction 0-64

	lcd_control(0x25);		// <- Voltage ref

	lcd_control(0x81);		// <- Volume mode

	lcd_control(0x18);		// <- Volume set

	//lcd_control(0xF8);		// <- Booster ratio = 4

	lcd_control(0x00);		// <- ??

	lcd_control(0x2F);		// <- Vf, Vr, Vc on

	lcd_control(0x40);		// <- Initial display line

	lcd_control(0xA7);		// <- Normal display

	lcd_control(0xAF);		// <- turn display on



	lcd_control(0xB0);		// <- page address = 0

	lcd_control(0x10);		// <- column address high = 0

	lcd_control(0x00);		// <- column address low = 0



	// clear screen

	lcd_cls();

		

	return;

}



void lcd_cls (void)

{

int r, q;

	for (r=0; r<8; r++)

	{

		lcd_set_column(-4,r);		// set_column adds 4 to the position

		for (q=0; q<128; q++)

		{

			lcd_write(0);

		}

	}

}



void lcd_char (char ch)

{



uint16_t ptr;

int q;



	// write ch to the screen at the current cursor position

	// update the cursor position and scroll to the next line

	// if required

	// to avoid scrolling the screen if we need to use 19,3 the 

	// cursor is moved to the top of the screen again

	// when we arrive, the cursor is already set

	//

	// note that although we have eight lines on the display we

	// only have four lines of text - 0-3. set_cursor sorts out the conversion

	//

	// Our font is sixteen deep (with some leading) and six wide including the

	// separating space

	

	// we set the cursor to where we already are, which sets up the column

	lcd_set_cursor(cursorx, cursory);		



	// now we work out where in the font array the character lives, get the ten bytes

	// that define the character, and stuff them to the display, followed by a zero for space

	// the characters start with 0x20

	ptr = (uint16_t)(((ch - 0x20)*10)+1);						// point to low byte of font data

	

	for (q = 0; q < 5; q++)

	{

		// first the top row

		//lcd_write(0xff);

		lcd_write(pgm_read_byte(&font[ptr]));

		ptr+=2;

	}

	lcd_write(0);																		// the space between the words



	ptr = (uint16_t)(((ch - 0x20)*10));						// point to high byte of font data

	//ptr -=9;					// point back to the start of the char

	lcd_set_column(cursorx*6, (cursory*2)+1);



	for (q = 0; q < 5; q++)

	{

		// the bottom row

		//lcd_write(0xff);

		lcd_write(pgm_read_byte(&font[ptr]));

		ptr+=2;

	}

	lcd_write(0);



	// increment the cursor to the next place

	cursorx++;

	if (cursorx == 20)

	{

		cursorx = 0;

		cursory++;

	}

	if (cursory > 3)

		cursory = 0;

		

}



void lcd_print (char * text)

{

int q;



	// print a string to the lcd

	for (q=0; q
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thanks a lot

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How does the schematic look?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

How does the schematic look?

Neil already answered that above:

https://www.avrfreaks.net/index.p...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

:)

Attachment(s): 

Last Edited: Mon. Aug 6, 2012 - 05:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Can you help me to find the fault in this code.

This is not the right place to ask - this thread is simply to make comments/suggestions for the tutorial article in the first post. If you simply want to diagnose some code you have written yourself for a similar controller suggest you start a new thread about this in AVR Forum.

Moderator.