i2c keypad problem not getting ACK after STA+R

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

I am trying to make an i2c keypad by using atmega8 (master receive and slave transmit mode). here is the master code

#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
void I2C_INIT()
{
	TWBR = 0x00; //CLEAR STATUS
	TWBR = 0x0C; //SET BIT RATE
}

void I2C_START()
{
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTA);
	while((TWCR & (1<<TWINT)) == 0);
	while((TWSR & (0xF8)) != 0x08);
}
void I2C_READAdress(/*unsigned char adress*/)
{
	TWDR = 0b01000001;
	TWCR = (1<<TWINT)|(1<<TWEN);
	while((TWCR & (1<<TWINT)) == 0);
	while((TWSR & (0xF8)) != 0x40);
}
unsigned char i2c_read_data()
{
	TWCR = (1<<TWINT)|(1<<TWEN);
	while((TWCR & (1<<TWINT)) == 0);
	while((TWSR & (0xF8)) != 0x58);
	return TWDR;
}
void I2C_stop()
{
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}
int main(void)
{
	DDRD=0xFF;
	I2C_INIT();
    while (1)
    {
		I2C_START();
		_delay_ms(1000);

		I2C_READAdress();
		_delay_ms(1000);
		PORTD=i2c_read_data();
		_delay_ms(1000);

		I2C_stop();
		_delay_ms(1000);
    }
}

here is slave code.

 

#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>

////////////////////////	KEYAPD	/////////////////////////////
#define KEY_PRT 	PORTD
#define KEY_DDR		DDRD
#define KEY_PIN		PIND

unsigned char keypad[4][4] = {
	{'A','3','2','1'},
	{'B','6','5','4'},
	{'C','9','8','7'},
	{'D','#','0','*'}};

	unsigned char colloc, rowloc;

	char keyfind()
	{
		while(1)
		{
			KEY_DDR = 0xF0;           /* set port direction as input-output */
			KEY_PRT = 0xFF;

			do
			{
				KEY_PRT &= 0x0F;      /* mask PORT for column read only */
				asm("NOP");
				colloc = (KEY_PIN & 0x0F); /* read status of column */
			}while(colloc != 0x0F);

			do
			{
				do
				{
					_delay_ms(20);             /* 20ms key debounce time */
					colloc = (KEY_PIN & 0x0F); /* read status of column */
					}while(colloc == 0x0F);        /* check for any key press */

					_delay_ms (40);	            /* 20 ms key debounce time */
					colloc = (KEY_PIN & 0x0F);
				}while(colloc == 0x0F);

				/* now check for rows */
				KEY_PRT = 0xEF;            /* check for pressed key in 1st row */
				asm("NOP");
				colloc = (KEY_PIN & 0x0F);
				if(colloc != 0x0F)
				{
					rowloc = 0;
					break;
				}

				KEY_PRT = 0xDF;		/* check for pressed key in 2nd row */
				asm("NOP");
				colloc = (KEY_PIN & 0x0F);
				if(colloc != 0x0F)
				{
					rowloc = 1;
					break;
				}

				KEY_PRT = 0xBF;		/* check for pressed key in 3rd row */
				asm("NOP");
				colloc = (KEY_PIN & 0x0F);
				if(colloc != 0x0F)
				{
					rowloc = 2;
					break;
				}

				KEY_PRT = 0x7F;		/* check for pressed key in 4th row */
				asm("NOP");
				colloc = (KEY_PIN & 0x0F);
				if(colloc != 0x0F)
				{
					rowloc = 3;
					break;
				}
			}

			if(colloc == 0x0E)
			return(keypad[rowloc][0]);
			else if(colloc == 0x0D)
			return(keypad[rowloc][1]);
			else if(colloc == 0x0B)
			return(keypad[rowloc][2]);
			else
			return(keypad[rowloc][3]);
		}

void i2c_slaveinit()
{
	TWAR = 0b01000000;
	TWCR = (1<<TWEN)|(1<<TWEA)|(1<<TWINT);
}
void i2c_ACK()
{
	TWCR = (1<<TWEN)|(1<<TWINT);
	while((TWCR & (1<<TWINT)) == 0);
	while((TWSR & (0xF8)) != 0xA8);
	/*TWDR = data;
	TWCR &= ~(1<<TWEA);
	TWCR = (1<<TWEN)|(1<<TWEA);*/
}
void i2c_transmit(unsigned char data)
{
	TWDR = data;
	TWCR &= ~(1<<TWEA);
	TWCR = (1<<TWEN)|(1<<TWINT);
	while((TWCR & (1<<TWINT)) == 0);
	while((TWSR & (0xF8)) != 0xC0);
	TWCR = (1<<TWEN)|(1<<TWEA)|(1<<TWINT);
}
void i2c_Match_ACK()
{
	while((TWSR & (0xF8)) != 0xA8)
	{
		TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
		while((TWCR & (1<<TWINT))==0);
	}
}

int main(void)
{
	char x;
	i2c_slaveinit();
    while (1)
    {
		//i2c_ACK();
		i2c_Match_ACK();
		x=keyfind();
		i2c_transmit(x);
    }
}

so I followed datasheet of atmega8 and first try to transmit a byte from slave and it works fine I get ACK after STA+R. then I try to put keypad code and I just replaced that byte with keyfind(); function which is also a char and now I don't get ACK after STA+R. is there any relation between them? can anyone help? here is pic of what is get

 

Last Edited: Mon. Jul 9, 2018 - 08:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Hassan lets continue in this thread.......

In that case it can be used for future reference by others too, and that will benefit the forum.

 

first of all we will be curious as to why you have decided to create your own TWI(I2C)  driver routines and not have taken one of the many standard libraries that can be used?

 

Second how big is your knowledge of the I2C protocol? specially with starts/repeated start/stop/ACK and NACK conditions that can be given by both the master and the slaves.

specially the ack and Nack bits willl have different meaning at different times. To fully understand those you need to do a bit of reading.

this is a link to the specification if you have not already downloaded it:

https://www.nxp.com/docs/en/user...

 

As also said in the other thread, best thing to do is to break your big problem up into smaller chunks.

First concentrate on the master as there you are fully in control and a lot of slave devices can be used.

 

COPIED FROM MY OTHER POST AS REFERENCE:

Till you get an ack from the slave all other things are pointless as your slave will not be responding to anything else you send out.

 

edit:

suggest you work on one side of the code at a time. Or you might be playing ping-pong all the time and keep running around in circles.

The master is the easiest to implement as there you are in full control of what you are doing.

what I always do is have for instance a PCF8574 + a AT24C02. first you get the PCF to work. that is all single byte oriented and should be up and running quickly.

then you extend with also using the 2402 that is more work as more data is to be send to the chip before you get anything back, also writing data to it for storage takes more information to be send.

When you can successfully talk to these 2 chips you know that in the basis your master code is working and is sending out the data you want it to be send.

Then you can concentrate on getting the slave part to work.

 

Note that there are a lot of pitfalls with respect to the ACK and NACK states transmitted and received. Also with regards on how you need to set-up/adjust the TWI interface in advance and during data reception. (single byte, multi byte / ack nack transmit / 7 bits address versus 8 bits address )

Personally I started by reading and understanding how the I2C interface works ( note that the spec is changing a lot but not in the basic operation, so if you look it up there will be a version online) then I just programmed the TWI interfacy by bitbanging and seeing that that worked, so I knew the hardware was OK. Then I moved over to start using the TWI peripheral and getting that to work. But there are also libraries like the peter fleury library that you can use and not care about what is below that to get things going.

 

END OF COPIED DATA

 

as said, take small steps at a time and make sure you understand what you are doing and what is going on.

Tip on the PFC8574 the pull-downs are much stronger than the pull-ups. from the 8 pins make 4 connect to a switch ( preferably 0-3 or 4-7 ) and make 4 leds+ small series resistor from the supply to the pins of the PCF8574

Note that by making the pin low then the LED will be ON (so inverted logic there).

 

once you can successfully write to the slave chip multiple times and retrieve the status of the switches from it, you have the basics of a read and write operation done.

next is then adding a memory chip. it is still just reading and writing, but with multiple bytes and thus other behavior of the ack and nack signal.

 

when you have that done and can successfully write to the memory chip and readback from it you basically have everything you need at hand, and have also gained valuable knowledge on how I2C works.

 

Then you can stop working on the master code and start working on the slave code, knowing you do not have to do anything with the master code.

 

 

 

Last Edited: Mon. Jul 9, 2018 - 10:52 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

after reading your post I feel i2c is a very difficult thing. anyways, so i am using datasheet of atmega 8 and it is written there when to use NACK and ACK used in my modes (master transmit). but the point is we should always get ACk after STA+R. so as i have written before i have successfully sent a byte from slave and received on the master by this code but when i put keyfind which is a function that waits for you to press a keypad button and then return char of pressed button and then it does not ACK. i find that error weird because there I don't think there is any relation between them.

 

and can u send link of libraries I don't find any good library though

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

Peter Fluery's code is recommend my most freaks:  http://homepage.hispeed.ch/peter...

 

All three of his library code is worth having.  One of the things that can be confusing is knowing if the I2C address you have is 7bit or 8bit.

8bit is just 7 bit shifted one to the left with the r/w bit added on.  Some DS's will use the 7 bit address, others will use the 8 bit address.

IIRC, Fluery uses 8 bit address, Arduino uses 7 bit address.  Start with the library above, once you have your project working, you can write your own code, as you now have something to compare/troubleshoot using known working code.

One more hint, avoid "void" functions, return an error code so you know something went wrong when it does and can test for and handle errors in your main() code.

 

Jim

 

Click Link: Get Free Stock: Retire early!

share.robinhood.com/jamesc3274

 

 

 

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

Hassan,

 

all things you do not know will be difficult.

In general I2C is rather simple in its use, but as with all things there are rules to keep an eye on.

knowing those few rules are key to understanding and to quickly see if things are OK or not.

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

Yeah i totally agree with that.
I just tried that by using library but it did not works too.
One question : does master waits to get data from slave or slave has to send data immediatly?
And can you help me debug my code because i am totally blank on that one

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

Hassan, I cannot imagine that the library would be faulty.

I strongly suggest getting a couple of I2C devices (see earlier posts on 2 very simple straight forward devices that you can have a lot of fun with) and first play with them and the master code to see what is happening when.

There still can be a zillion small things that can be wrong.

 

 

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

I can't tell if the keypad-read routine works correctly since it is not documented, so I'm going to assume that it doesn't.  There are two ways that it could fail.  There is no change in the returned char after any key is pressed, or, the function returns a wrong value when the key is pressed.  How has this function been tested?  Does the function loop forever inside itself until a key is pressed?

 

  A slave I2C that controls a keypad needs to read the keypad no more than 10 times a second.  It needs to determine if there was any change from the last reading, i.e. if any key is pressed (doing a single read of the keypad every 0.1 sec eliminates the need for any debouncing routines).  Then it needs to determine if the key is pressed or released, and if pressed, what the key value is.  Since there are only 16 keys, it would be nice for the keyread function to turn to raw row-column data into the ASCII value for the key pressed.  Let's assume the key-pad read function returns either 0xff or one of 16 ASCII values when called: the 0xff value meaning that there was no change in keypad status since the last time that the function was called (1/10th of a second ago).

 

All the rest of the time, the Slave I2C is testing for valid START from the Master I2C.  Then, if the SLA matches, AND the R/w bit (bit0) is set (meaning a Read operation on the TWI/I2C), the Slave asserts SDA, which creates the ACK bit for the SLA from the Master.  The Slave receives clock pulses from the Master and puts the latest char received from the keypad-read function onto the TWI/I2C SDA pin.  Then the SLAVE releases the SDA pin.  The Slave (in this case) ignores the ACK from the Master.  The Master releases the SCL pin and then releases SDA, creating a STOP condition.

 

The Master requests a read operation from the key-pad I2C Slave every half-second 0.5 sec or so.

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

I can't tell if the keypad-read routine works correctly since it is not documented, so I'm going to assume that it doesn't. 

I have used this keypad function many times and it works perfectly. 

Does the function loop forever inside itself until a key is pressed?

yeah in the start of the function and I guess (not sure) it is also checks key release in the start of function. 

A slave I2C that controls a keypad needs to read the keypad no more than 10 times a second.

but how can someone press a key in such a less time? or you are saying it waits for you to press a key and after pressing, function has to read that key in less than 0.1 sec.

 

One question: Does the master wait for slave to transmit data or slave have to do it instantly after getting STA+R? 

 

in my code, does it even reach to 

x=keyfind();

i don't think so. so why it send NACK is there any relation.

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

The keypad function should never wait for a key press from the user.  Instead it should test if there has been any change on the PIN register that is used to test for a keypress.  This technique assumes that the key will be held down for at least 1/10 of a second.  Every 1/10th of a second, it should test if the any of the keys are pressed by putting active logic on all four of the columns.  If any key is pressed then there will be active logic  (there is no documentation that says whether the PIN read is high or low when a key is pressed on that row, so 'active logic' means that a key is pressed when the PIN register is read).   The read of the PIN register is compared to the read from the previous 1/10th of a second interval {the value from the previous read gets stored in a variable}.  

 

   If a change has been detected on any key, then scan the keypad to determine which individual key has been pressed or released.  If the keypad function waits for a keypress/release, then the program can do nothing else while waiting for user input.  But the program has to do one other thing.  It must react to a valid Slave Address Received on the TWI (I2C) port.  Usually one of the timers is set aside for making a "heartbeat" for the system.  This is a timed interval where the computer system checks and reacts to the status of all the active peripherals.  On the Arduino, timer0 creates a heartbeat every 0.001 seconds, every millisecond.  The heartbeat routine count 100 heartbeat intervals and then calls the keypad-read function.  If the value returned from the keypad-read function is not 0xff, then a valid new keypress has been detected.  The TWI on the Slave should generate an interrupt when the Slave TWI has received a correct Slave Address and has sent an ACK to the Master.  

 

The Master can not wait for the Slave to transmit data: only the Master can transmit data.  The Slave has no way to signal to the master that it has a new keypress ready.  The Master has to request that the Slave send its current status.  If a new key has been pressed, then the Slave will send the ASCII of the latest keypress when the Master requests its current status.  If there is no change in the Slave's status, i.e. no new keypress, then the Slave sends 0xff when the Master requests the Slave's current status.   The Master does a SLA+R  with the Slave Address of the keypad controller to request the current status from that Slave.   There is one Master, but there can be many Slave devices.

 

You are doing this the very difficult way: you are writing and debugging low-level hardware code yourself.  The easy way is to download and study the already working and debugged code from various C++ libraries that are available on the web for decoding a 4x4 keypad using the Arduino.  Why does everyone in the world insist on doing low-level hardware programming the hard way?  The whole purpose of the Arduino system is to make low-level hardware programming a thing of the past by using "plug and play" pre-written and pre-tested code modules so that users can concentrate on presenting and using the data (that is acquired by low-level AVR hardware) in a useful and productive manner.