Reading Data from LK404-55 over I2C serial bus.

LK/ELK/VK/PK/OK/MX/GLK/EGLK/GVK/GLT Series

Moderators: Henry, Mods

kronke
LCD?
Posts: 9
Joined: Thu Nov 11, 2004 3:16 am

Reading Data from LK404-55 over I2C serial bus.

Post by kronke »

I'm looking for an example or better instructions for how to read data from the LK404-55. The info in the User Manual is pretty sketchy and I'm not having any luck getting it to work. For example, for the "Read Module Type" command I use something like the following:

Code: Select all

flag lcd_present()
{  ubyte ec;
    ubyte data;

    lcd_cmd( 55 );    // Send command to read module type.
    ec = ReadCurrent( 0x51, &data );
    if( ec ) return FALSE;      // If we cant read, it's not there.
    else if( data == 10 )      // Is it the correct LCD?
        return TRUE;
    else  return FALSE;
}
Seems like each time I run this I get a different value back for data. Anyone have any insight or experience in this that might help? Thanks in advance!
RussCA
LCD!
Posts: 13
Joined: Tue Nov 09, 2004 9:59 am
Location: Ontario, Canada

Post by RussCA »

Are you sure your I2C driver code is working properly? Or are you actually using a PC serial port?

I can supply my I2C and MOLCD code in Borland C, if this might help. The LCD code is for an LK202-25, but should be easily modified for whatever you want to drive.

Russ
Russ

Have you tried Renewable Energy yet?
kronke
LCD?
Posts: 9
Joined: Thu Nov 11, 2004 3:16 am

Post by kronke »

Russ,

Thanks for the reply. Yes, I'm definitely using I2C and it is working properly. All output to LCD works properly as does I/O to other devices on the I2C buss. This is on an embedded microcontroller, not a PC.

All I need is the high level protocol for the LCD, not the low level I2C stuff. If your code has an example of a command that reads data back from the LCD (like the Read Module Type or Poll Key Buffer commands) that should do it. If you could just send those examples it would be great.

Or, it might be as simple as telling me if I've doing this right...

send command to LCD at address 50h: 255 57 --> LCD@0x50
read response from LCD at address 51h: data <-- LCD@0x51
test dat for proper type: For LK404-55 data should be 10

Do I need to include a delay before the read? Is that the proper address to read?

Thanks!
Chester
RussCA
LCD!
Posts: 13
Joined: Tue Nov 09, 2004 9:59 am
Location: Ontario, Canada

Post by RussCA »

Chester,

Are you able to read from other I2C devices? The protocol is rather particular, I found. I used the pseudo code from the site mentioned in the Matrix Orbital manual. I had trouble reading because I wasn't switching the SDA line to input. (Not mentioned in the pseudo code.)

Here is my read routine:


/* Read num bytes from device adr into buf */
void I2Cread (int adr, int num, char *buf) {
I2Csendadr (adr, 1);

I2Cstart ();
I2Cputbyte (adr | 0x01);
I2Cgetack ();

while (num > 0) {
*buf = I2Cgetbyte ();
buf++;

if (--num > 0) I2Cgiveack ();
}

I2Cstop ();
}


int I2Cgetbyte(void)
{
int i_byte = 0, n;

SetInputPort (SDA);

for (n=0; n<8; n++)
{
I2Chighscl();

if (GetPortBit (SDA, SDABIT))

{
i_byte = (i_byte << 1) | 0x01; /* msbit first */
}
else
{
i_byte = i_byte << 1;
}

I2Clowscl();
}

I2Chighsda ();
return(i_byte);
}
Russ

Have you tried Renewable Energy yet?
kronke
LCD?
Posts: 9
Joined: Thu Nov 11, 2004 3:16 am

Post by kronke »

Russ,

Thanks again for your reply.

Yes, I can and do read from other I2C devices currently. My read function does switch SDA line to input (in the case of my microcontroller this is done simply by leaving the output in the high state and then reading it as an input). I know that portion of I2C interface works because I use it to read other devices and it works fine.

I'll try to digest what you've sent and let you know what I find out later. Thanks!

Chester
RussCA
LCD!
Posts: 13
Joined: Tue Nov 09, 2004 9:59 am
Location: Ontario, Canada

Post by RussCA »

Chester,

Hmmm. I'm at a loss. I haven't tried to read anything other than the keypad, and I don't use the "Poll Keypad" command. I just issue a read from the LCD and I get the characters.

I wonder if your problem has to do with timing. I read in another message that there is a problem with doing the "Load Splash Screen" function via I2C because it takes so long to write each character to the EEPROM. I wonder if there is a similar problem with retrieving data such as the Module Type and Firmware Serial number.

I would expect the ACK to be delayed, but in my case I have a time out in GetACK in case there is a problem (device address not present, busy, malfunctioning, etc.).

Maybe Matrix Orbital can provide us with some insights... <HOPING>

BTW, which microcontroller are you using? <CURIOUS> I'm using a Flashlite 186 SBC from JKMicro. It is essentially an 80186 on a single small board. It uses a RDC8822 clone of the 80186.

Russ
Russ

Have you tried Renewable Energy yet?
Miles
Matrix Orbital
Matrix Orbital
Posts: 1105
Joined: Mon Mar 04, 2002 4:00 pm

Post by Miles »

Please see the following link...the only "thing" you can "receive" back from the module is keypresses in I2C...

http://www.lcdforums.com/forums/viewtopic.php?t=910
Miles Y.
Head of Technical Support
Product Manager
Matrix Orbital
RussCA
LCD!
Posts: 13
Joined: Tue Nov 09, 2004 9:59 am
Location: Ontario, Canada

Post by RussCA »

Miles,

From the link you provided:

[/quote] Q: Do you use ACK? What do I do with it?

A: The idea of ACK is to indicate when the data has been received correctly. ACK does not indicate data incorrectly received. ACK simply "fails to acknowledge" when data is incorrectly received. Clearly, this is of limited usefulness and even less so with Matrix Orbital modules. Matrix orbital modules are not capable of "failing to acknowledge" an incorrectly received byte in response to that byte's transmission. Matrix Orbital displays do not use ACK properly (and cannot, due to the software nature of it's buffers). The display may send out ACK, but you should ignore it at the host system.
[/quote]


[color=#][/color]

Not quite correct. In the I2C world, as I understand it, ACK merely indicates that data has been received, with no value judgement on the validity of that data. NAK indicates that there was some problem while receiving data.

To comply with the I2C protocol, the display MUST send ACK when data has been received, as must the host. I believe the MO displays (at least the LK202-25) do this properly.

Russ
Russ

Have you tried Renewable Energy yet?
Miles
Matrix Orbital
Matrix Orbital
Posts: 1105
Joined: Mon Mar 04, 2002 4:00 pm

Post by Miles »

RussCA,

You are absolutely correct...I believe this should be in regards to the NACK. Our devices will always ACK, however in respect to how our units work, they are not capable of failing to acknowledge an incorrectly received byte in response to that bytes transition. They are only capable of failing to acknowledge the bytes following the byte, which was not received. Basically the reason why a Matrix Orbital module might fail to receive a byte correctly is that it was unable to process the byte previous before the failed byte was transmitted. Because the module cannot possibly know that it would be unable to store the byte before the next byte was received it cannot know to not ACK.
Miles Y.
Head of Technical Support
Product Manager
Matrix Orbital
kronke
LCD?
Posts: 9
Joined: Thu Nov 11, 2004 3:16 am

Post by kronke »

(Russ, See below.)

Miles,

Thanks for the link to the FAQ. I had search forum for this info unsuccessfully. Guess I used the wrong keywords.

When I had issues with loading Splash screen over I2C I was told by MO support that the command was not compatible with I2C (turns out it actually does work). So, I asked if there were any other commands that do not work on I2C and the answer was no, that everything else should work fine. The fact that only the key presses can be read on I2C buss and all other commands that read back data are not available when using I2C really should be spelled out in the Users Manual.

Anyway, thanks for your help!


Russ,

Looks like the issue is explained. I guess all I can do to test to see if module is connected is to see if write attempts are successful. I can not tell what type of module is connected which really limits where I can take the application of the MO modules. I'm using an Atmel 89C51RD2 which is an 8052 derivative (8-bit). My use of the LCD and Keypad is as a configuration, testing, debugging, and manual operation tool. It is not normally connected so my application has to detect when it is connected to know when to handle it. It would have been nice to determine which particular module is connected so that my software could handle different screen sizes. Since it can't I'll be stuck with using only one display model.

Thanks for all your help!

Best regards,
Chester
Miles
Matrix Orbital
Matrix Orbital
Posts: 1105
Joined: Mon Mar 04, 2002 4:00 pm

Post by Miles »

Chester,

Thank you for your input as we take our customers opinions and suggestions very seriously at Matrix Orbital. I can understand how not being able to read the module type limits your applications ability, however each device does require an unique address therefore giving you some breathing room.
Miles Y.
Head of Technical Support
Product Manager
Matrix Orbital
kronke
LCD?
Posts: 9
Joined: Thu Nov 11, 2004 3:16 am

Post by kronke »

Miles,

Good suggestion. I hadn't considered that. Thanks!

Chester
RussCA
LCD!
Posts: 13
Joined: Tue Nov 09, 2004 9:59 am
Location: Ontario, Canada

Post by RussCA »

Chester,

Well, there IS a way you can test to see if you have a MO display connected!

Use the output pins! Connect one or more of the output pins of the MO display to input pins of your microprocessor. Set the MO output pins, read from the processor to make sure you have the same bit pattern coming back. Use a different bit pattern for each type of dicplay you have. Actually you probably will be mostly concerned with knowing how many lines and how long they are. Only takes two bits for the most common situations:

.......Rows Columns
00 2 20
01 2 40
10 4 20
11 4 40


If you can't join 'em, fight 'em! :)

Russ
Russ

Have you tried Renewable Energy yet?
kronke
LCD?
Posts: 9
Joined: Thu Nov 11, 2004 3:16 am

Post by kronke »

Hey Russ,

Not really an option in my case. Since I use the LCD/Keypad pair as a maintenance & diagnostic tool that I normally don't have connected, I want to minimize the connection requirements and also I/O is already at a premium in my design.

The addressing scheme Miles suggests should work great. If I decide to support different screen sizes, I can make sure each particular size unit has a different address. The one that acknowledges write attempts is the winner.

Say, could you possibly post your I2Cgetbyte source. When I read keypad it works fine as long as I'm using ICE and stop to examine each character read. When I let it run full speed if gives erroneous results.

BTW, don't know if your familar with Code button in "Post a reply" but it helps preserve the indenting. You might want to try that.

Cheers,
Chester
RussCA
LCD!
Posts: 13
Joined: Tue Nov 09, 2004 9:59 am
Location: Ontario, Canada

Post by RussCA »

Code: Select all

================================
/*
 *  MOLCD.h
 *
 *  Definitions for interfacing to the LCD.
 *
 */

#ifndef MOLDC_h
#define MOLCD_h

#define DEFLCDADR 0x50

void LCDsetadr (int adr);  /* Set the address of the LCD to be accessed */
void LCDwrite (int dat);
void LCDstr (char *dat);

int LCDread (void);

void LCDpos (int row, int col);
void LCDcursor (int type, int state);
void LCDhome (void);
void LCDleft (void);
void LCDright (void);
void LCDcls (void);

#define LCDBLOFF 0
#define LCDBLON  0xFF
void LCDbacklight (int state, int minutes);

#define LCDGPOON 0xFF
#define LCDGPOFF 0
void LCDgpo (int state, int chan);

void LCDcontrast (unsigned char val); 

#endif /* LCD.h */
=======================================
/*
 * Routines to access a Matrix Orbital LK202-25 2x20 LCD.
 * They also work for a LK204-25, and probably most MO displays.
 * These routines utilize the I2C interface.
 */

#include <string.h>
#include "\JKMicro\H\MOLCD.H"
#include "\JKMicro\H\I2C.H"

#define LCDCOMMAND 0xFE

static int LCD_adr = DEFLCDADR;   

void LCDcommand (int cmd, int num, int arg1, int arg2) {
	 char buf[6];

	 buf[0] = LCDCOMMAND;
	 buf[1] = cmd;
	 buf[2] = arg1;
	 buf[3] = arg2;
	 I2Cwrite (LCD_adr, num + 1, buf);
}

/*
 * Read the next (if any) character from the keypad.
 */
			/*    A   B   C   D   E   F   G   H   I   J   K   L   M */
char map[] = {'?','D','#','0','*','?','C','9','8','7','?','B','6',
			/*    N   O   P   @   R   S   T   U   V   W   X   Y   Z */
				  '5','4','?','A','3','2','1','?','?','?','?','?','?'};
		
int LCDread (void)
{
	char c[2];

	I2Cread (LCD_adr, 1, c);

	if (c[0] == 0) return 0;

	return(map [c[0] - 'A']);
}


void LCDwrite (int dat) {
	char buf [2];

	buf [0] = (char) dat;
	I2Cwrite (LCD_adr, 1, buf);
}   

void LCDstr (char *dat) {
	 I2Cwrite (LCD_adr, strlen (dat), dat);
}

void LCDpos (int row, int col) {
	 LCDcommand (0x47, 3, col, row);
}

void LCDcursor (int type, int state) {
	 char c;

	 switch (type*2 + state) {
	 case 0:  c = (0x4B);    /* underline off */
	 case 1:  c = (0x4A);    /* underline on */
	 case 2:  c = (0x54);    /* block off */
	 case 3:  c = (0x53);    /* block on */
	 }

	 LCDcommand (c, 1, 0, 0);
}


void LCDhome (void) {
	 LCDcommand (0x48, 1, 0, 0);
}


void LCDleft (void) {
	 LCDcommand (0x4C, 1, 0, 0);
}


void LCDright (void) {
	 LCDcommand (0x4D, 1, 0, 0);
}


void LCDcls (void) {
	 LCDcommand (0x58, 1, 0, 0);
}


void LCDbacklight (int state, int minutes) {
	 if (state == 0)
		  LCDcommand (0x46, 1, 0, 0);
	 else {
		  LCDcommand (0x42, 2, minutes, 0);
	 }
}


void LCDgpo (int state, int chan) {
	if ((chan >= 1) && (chan <= 6)) {
		if (state == 0) LCDcommand (0x57, 2, chan, 0);
		else LCDcommand (0x57, 2, chan, 0);
		}
}  	


void LCDcontrast (unsigned char val) {
	LCDcommand (0x50, 2, val, 0);
}


========================================
/*
 * I2C.H
 *
 * Prototypes and definitions for I2C.C
 *
 */

#ifndef I2C_H
#define I2C_H

#include "\JKMicro\H\ports186.h"

/* standard I2C routines */
int  I2Cgetbyte(void);
void I2Cputbyte(int o_byte);
void I2Cnack(void);
void I2Cgiveack(void);
void I2Cinit (void);
void I2Cstart(void);
void I2Cstop(void);
void I2Chighsda(void);
void I2Clowsda(void);
void I2Chighscl(void);
void I2Clowscl(void);

void I2Cread (int adr, int num, char *buf);
void I2Cwrite (int adr, int num, char *buf);
#endif
=====================================
/*
 * Routines to drive a standard I2C bus for the JKMicro
 * Flashlite 186 Single Board Computer.
 */


#include <stdio.h>
#include "\JKMicro\H\i2c.h"
#include "\JKMicro\H\Ports186.h"

  #define SCL     PORTA
  #define SCLBIT  0x00
  #define SDA     PORTB
  #define SDABIT  0x00

/*
 * I2C Routines derived from the pseudo code by V. Himpe,
 * and are likewise released into the Public Domain by:
 *
 * Russell W. Ranshaw
 *
 * For more information, visit:
 *  http://www.ping.be/~ping0751/i2cfaq/i2cindex.htm
 *
 */

/* 
 * I2Cinit is to be called immediately after power up.
 * This is normally the only I2C routine to be called 
 * directly by the application.
 */

void I2Cinit (void) {
	int n;

	I2Chighsda ();
	I2Clowscl ();

	for (n = 0; n < 3; n++) {
		I2Cstop ();
	}
}

void I2Cstart (void) {
	I2Chighscl ();
	I2Chighsda ();
	I2Clowsda ();
	I2Clowscl ();
	I2Chighsda ();
}


void I2Cstop (void) {
	I2Clowsda ();
	I2Clowscl ();
	I2Chighsda ();
}

int I2Cgetbyte(void)
{
	int i_byte = 0, n;

	SetInputPort (SDA);

	for (n=0; n<8; n++)
	{
		I2Chighscl();
		
		if (GetPortBit (SDA, SDABIT))
		{
			i_byte = (i_byte << 1) | 0x01; /* msbit first */
		}
		else
		{
			i_byte = i_byte << 1;
		}

		I2Clowscl();
	}


	I2Chighsda ();
	return(i_byte);
}

void I2Cputbyte(int o_byte)
{
	int n;

	for(n=0; n<8; n++)
	{
		if(o_byte&0x80)
		{
			I2Chighsda();
		}
		else
		{
			I2Clowsda();
		}

		I2Chighscl();
		I2Clowscl();
		o_byte = o_byte << 1;
	}

	I2Chighsda();
}


void I2Cgiveack(void)
{
	I2Clowsda(); 
	I2Chighscl();
	I2Clowscl();
	I2Chighsda();
}

void I2Cgetack (void) {
	int n;

	I2Chighsda ();
	I2Chighscl ();

	SetInputPort (SDA);

	/* Wait until Slave pulls SDA low */
	/* Note:  The pseudocode does not have the for loop.  
	 * It was added to prevent a lock up situation. 
	 */

	for (n = 1; n < 100; n--) {
		if (GetPortBit (SDA, SDABIT) == 0)
			break;
	}

	I2Clowscl ();

	SetOutputPort (SDA);
}


void I2Chighsda(void)
{
	SetOutputPort (SDA);
	SetPortBit (SDA, SDABIT);
}

void I2Clowsda(void)
{
	SetOutputPort (SDA);
	ClearPortBit (SDA, SDABIT);
}

void I2Chighscl(void)
{
	SetOutputPort (SCL);
	SetPortBit (SCL, SCLBIT);
}

void I2Clowscl(void)
{
	SetOutputPort (SCL);
	ClearPortBit (SCL, SCLBIT);
}

/*
 * Routine to send an address on the I2C bus.  To indicate
 * adr is a 10-bit address, set the high-order bit (0x8000).
 */
void I2Csendadr (int adr, int rw) {
	I2Cstart ();

	if ((adr & 0x8000) != 0) {
		/* Send two MSBs of the ten bit address */
		I2Cputbyte (0xF0 | rw | ((adr > 7) & 0x06));
		I2Cgetack ();
		I2Cputbyte (adr & 0xFF);
	} else {
		I2Cputbyte (adr | rw);
	}

	I2Cgetack ();
}

/* Read num bytes from device adr into buf */
void I2Cread (int adr, int num, char *buf) {

	I2Csendadr (adr, 1);

	while (num > 0) {
		*buf = I2Cgetbyte ();

		buf++;

		if (--num > 0) I2Cgiveack ();
	}

	I2Cstop ();
}


/* Write num bytes to device adr from buf */
void I2Cwrite (int adr, int num, char *buf) {
	I2Csendadr (adr, 0);

	while (num > 0) {
		I2Cputbyte (*buf++);
		I2Cgiveack ();
		num--;
	}

	I2Cstop ();
}


===============================

The low-level port bit routines have a small delay built in, about a microsecond.

Russ

Have you tried Renewable Energy yet?
Post Reply