TWI comunication beetween two Atmega16

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

Hello,

I'm trying to communicate 2 atmega16 using TWI, I'm using code from this site http://www.electronicwings.com/a...

Master should send data witch affects LED's connected to slave after that master should receive values read by slave's ADC and print them via USART.

Slave device receives data with no problems, LED's blink as they should, serial monitor shows data as it should, but when slave tries to send data to master nothing happens.

I noticed that if on slave in I2C_Slave_Listen() function i replace (status == 0xA8 || status == 0xB0) to (status == 0xA8 || status == 0xB0 || status == 0x80) slave sends data but there is about one second delay between turning on LED connected to PC3 on slave and data appearing in serial monitor, also if I change Slave_Read_Address on Master side to any other value, LED on PC3 turns on for about second then it turns off with no data send to master.

Can anyone tell me how to fix this?

 

Master:

#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>	
#include <stdlib.h>
#include "USART/USART.h"
#include "I2C_M/I2C_Master_H_file.h"

#define Slave_Write_Address		0x20
#define Slave_Read_Address		0x21


int main(void)
{
	char strBuffer[10];
	uint8_t datBuff[4] = {0xA1,0xB2,0xC3,0xD4};
	uint8_t count = 100;
	uint8_t rVal =0;
        initUSART(9600);
	I2C_Init();
	printString("I2C Master initialized.\n");
	
	
	
    while (1) 
    {
		printString("Sending data...\n");
		I2C_Start_Wait(Slave_Write_Address);/* Start I2C communication with SLA+W */
		_delay_ms(5);
		for(uint8_t i =0;i<4;i++)
		{
			I2C_Write(datBuff[i]);
			itoa(datBuff[i], strBuffer,16);
			printString(strBuffer);
			printString("\n");
			_delay_ms(1000);
		}
		
		printString("Receiving data...\n");

		I2C_Repeated_Start(Slave_Read_Address);
		_delay_ms(5);
		
		printString("verif\n");		
		for (uint8_t i = 0; i<count;i++)
		{
			if(i < count - 1)
			{
				rVal = I2C_Read_Ack();/* Read and send Acknowledge of data  ack is used if you wish to recive next data*/					
				itoa(rVal, strBuffer,10);

			}
			
			else
			{
				rVal = I2C_Read_Nack();/* Read and Not Acknowledge to data, nack is used to recive data and terminate slave transmision */	
				itoa(rVal, strBuffer,10);
			}
			
			printString(strBuffer);
			printString("\n");
			_delay_ms(100);
			
		}
		I2C_Stop();
		
		
		
    }
}

I2C_Master_H_file.c

#include "I2C_Master_H_file.h"							/* Include I2C header file */

void I2C_Init()									/* I2C initialize function */
{
	TWBR = BITRATE(TWSR = 0x00);						/* Get bit rate register value by formula */
}	


uint8_t I2C_Start(char write_address)						/* I2C start function */
{
	uint8_t status;								/* Declare variable */
	TWCR = (1<<TWSTA)|(1<<TWEN)|(1<<TWINT);					/* Enable TWI, generate start condition and clear interrupt flag */
	while (!(TWCR & (1<<TWINT)));						/* Wait until TWI finish its current job (start condition) */
	status = TWSR & 0xF8;							/* Read TWI status register with masking lower three bits */
	if (status != 0x08)							/* Check weather start condition transmitted successfully or not? */
	return 0;								/* If not then return 0 to indicate start condition fail */
	TWDR = write_address;							/* If yes then write SLA+W in TWI data register */
	TWCR = (1<<TWEN)|(1<<TWINT);						/* Enable TWI and clear interrupt flag */
	while (!(TWCR & (1<<TWINT)));						/* Wait until TWI finish its current job (Write operation) */
	status = TWSR & 0xF8;							/* Read TWI status register with masking lower three bits */	
	if (status == 0x18)							/* Check weather SLA+W transmitted & ack received or not? */
	return 1;								/* If yes then return 1 to indicate ack received i.e. ready to accept data byte */
	if (status == 0x20)							/* Check weather SLA+W transmitted & nack received or not? */
	return 2;								/* If yes then return 2 to indicate nack received i.e. device is busy */
	else
	return 3;								/* Else return 3 to indicate SLA+W failed */
}

uint8_t I2C_Repeated_Start(char read_address)				        /* I2C repeated start function */
{
	uint8_t status;								/* Declare variable */
	TWCR = (1<<TWSTA)|(1<<TWEN)|(1<<TWINT);					/* Enable TWI, generate start condition and clear interrupt flag */
	while (!(TWCR & (1<<TWINT)));						/* Wait until TWI finish its current job (start condition) */
	status = TWSR & 0xF8;							/* Read TWI status register with masking lower three bits */
	if (status != 0x10)							/* Check weather repeated start condition transmitted successfully or not? */
	return 0;								/* If no then return 0 to indicate repeated start condition fail */
	TWDR = read_address;							/* If yes then write SLA+R in TWI data register */
	TWCR = (1<<TWEN)|(1<<TWINT);						/* Enable TWI and clear interrupt flag */
	while (!(TWCR & (1<<TWINT)));						/* Wait until TWI finish its current job (Write operation) */
	status = TWSR & 0xF8;							/* Read TWI status register with masking lower three bits */
	if (status == 0x40)							/* Check weather SLA+R transmitted & ack received or not? */
	return 1;								/* If yes then return 1 to indicate ack received */
	if (status == 0x20)							/* Check weather SLA+R transmitted & nack received or not? */
	return 2;								/* If yes then return 2 to indicate nack received i.e. device is busy */
	else
	return 3;								/* Else return 3 to indicate SLA+R failed */
}





void I2C_Stop()									/* I2C stop function */
{
	TWCR=(1<<TWSTO)|(1<<TWINT)|(1<<TWEN);					/* Enable TWI, generate stop condition and clear interrupt flag */
	while(TWCR & (1<<TWSTO));						/* Wait until stop condition execution */
}

void I2C_Start_Wait(char write_address)						/* I2C start wait function */
{
	uint8_t status;								/* Declare variable */
	while (1)
	{
		TWCR = (1<<TWSTA)|(1<<TWEN)|(1<<TWINT);				/* Enable TWI, generate start condition and clear interrupt flag */
		while (!(TWCR & (1<<TWINT)));					/* Wait until TWI finish its current job (start condition) */
		status = TWSR & 0xF8;						/* Read TWI status register with masking lower three bits */
		if (status != 0x08)						/* Check weather start condition transmitted successfully or not? */
		continue;							/* If no then continue with start loop again */
		TWDR = write_address;						/* If yes then write SLA+W in TWI data register */
		TWCR = (1<<TWEN)|(1<<TWINT);					/* Enable TWI and clear interrupt flag */
		while (!(TWCR & (1<<TWINT)));					/* Wait until TWI finish its current job (Write operation) */
		status = TWSR & 0xF8;						/* Read TWI status register with masking lower three bits */
		if (status != 0x18 )						/* Check weather SLA+W transmitted & ack received or not? */
		{
			I2C_Stop();						/* If not then generate stop condition */
			continue;						/* continue with start loop again */
		}
		break;								/* If yes then break loop */
	}
}

uint8_t I2C_Write(char data)							/* I2C write function */
{
	uint8_t status;								/* Declare variable */
	TWDR = data;								/* Copy data in TWI data register */
	TWCR = (1<<TWEN)|(1<<TWINT);						/* Enable TWI and clear interrupt flag */
	while (!(TWCR & (1<<TWINT)));						/* Wait until TWI finish its current job (Write operation) */
	status = TWSR & 0xF8;							/* Read TWI status register with masking lower three bits */
	if (status == 0x28)							/* Check weather data transmitted & ack received or not? */
	return 0;								/* If yes then return 0 to indicate ack received */
	if (status == 0x30)							/* Check weather data transmitted & nack received or not? */
	return 1;								/* If yes then return 1 to indicate nack received */
	else
	return 2;								/* Else return 2 to indicate data transmission failed */
}

char I2C_Read_Ack()								/* I2C read ack function */
{
	TWCR=(1<<TWEN)|(1<<TWINT)|(1<<TWEA);					/* Enable TWI, generation of ack and clear interrupt flag */
	while (!(TWCR & (1<<TWINT)));						/* Wait until TWI finish its current job (read operation) */
	return TWDR;								/* Return received data */
}	

char I2C_Read_Nack()								/* I2C read nack function */
{
	TWCR=(1<<TWEN)|(1<<TWINT);						/* Enable TWI and clear interrupt flag */
	while (!(TWCR & (1<<TWINT)));						/* Wait until TWI finish its current job (read operation) */
	return TWDR;								/* Return received data */
}	

Slave:

#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>							
#include <stdio.h>
#include "I2C_S/I2C_Slave_H_File.h"

#define Slave_Address	0x20 

void adc_init()
{
	ADMUX = (1<<REFS0);
	ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

uint16_t adc_read(uint8_t ch)
{

	ch &= 0b00000111; 
	ADMUX = (ADMUX & 0xF8)|ch; 
	ADCSRA |= (1<<ADSC);
	while(ADCSRA & (1<<ADSC));
	return (ADC);
}

int main(void)
{	

	uint8_t count = 0;
	uint16_t countBuff= 12;
	uint8_t Ack_status;
	I2C_Slave_Init(Slave_Address);
        adc_init();
  	DDRC |= 0b00111100; // led output
	
    while (1) 
    {

		switch(I2C_Slave_Listen())
		{
			case 0:
			{

				PORTC |= (1<<2); // signalizes receiving on LED
				do{

					count = I2C_Slave_Receive();
					if(count == 0xA1)
					{
						PORTC |= (1<<4);						
					}
					else if (count == 0xB2)
					{
						PORTC &= ~(1<<4);
						PORTC |= (1<<5);
					}
					else if (count == 0xC3)
					{
						PORTC |=(1<<4)|(1<<5);

						
					}
					else if (count == 0xD4)
					{
						PORTC &= ~((1<<4)|(1<<5));
					}
					
				}while(count != 0xD4);
				PORTC &= ~(1<<2);			
				count = 0;
				break;	
				
			}
			case 1: 
			{
				
				PORTC |= (1<<3); //signalizes sending on LED
				do{
					countBuff = adc_read(0);
					count = (countBuff>>2);
					Ack_status = I2C_Slave_Transmit(count);
				
				}while(Ack_status == 0);	
				PORTC &= ~(1<<3);
				break;			
			}
			default:
				break;			

			
			
			
			
			
		}
		
    }
}

 

I2C_Slave_H_File.c

#include "I2C_Slave_H_File.h"

void I2C_Slave_Init(uint8_t slave_address)
{
	TWAR = slave_address;					/* Assign address in TWI address register */
	TWCR = (1<<TWEN) | (1<<TWEA) | (1<<TWINT);	        /* Enable TWI, Enable ack generation, clear TWI interrupt */
}

int8_t I2C_Slave_Listen()
{

	while(1)
	{
		uint8_t status;					/* Declare variable */
		while (!(TWCR & (1<<TWINT)));			/* Wait to be addressed */
		status = TWSR & 0xF8;				/* Read TWI status register with masking lower three bits */
		if (status == 0x60 || status == 0x68)	        /* Check weather own SLA+W received & ack returned (TWEA = 1) */
		return 0;					/* If yes then return 0 to indicate ack returned */
		if (status == 0xA8 || status == 0xB0)	        /* Check weather own SLA+R received & ack returned (TWEA = 1) */
		return 1;					/* If yes then return 1 to indicate ack returned */
		if (status == 0x70 || status == 0x78)	        /* Check weather general call received & ack returned (TWEA = 1) */
		return 2;					/* If yes then return 2 to indicate ack returned */
		else
		continue;					/* Else continue */
	}
}

int8_t I2C_Slave_Transmit(char data)
{
	uint8_t status;
	TWDR = data;						/* Write data to TWDR to be transmitted */
	TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWEA);		        /* Enable TWI and clear interrupt flag */
	while (!(TWCR & (1<<TWINT)));				/* Wait until TWI finish its current job (Write operation) */
	status = TWSR & 0xF8;					/* Read TWI status register with masking lower three bits */
	if (status == 0xA0)					/* Check weather STOP/REPEATED START received */
	{
		TWCR |= (1<<TWINT);				/* If yes then clear interrupt flag & return -1 */
		return -1;
	}
	if (status == 0xB8)					/* Check weather data transmitted & ack received */
		return 0;					/* If yes then return 0 */
	if (status == 0xC0)					/* Check weather data transmitted & nack received */
	{
		TWCR |= (1<<TWINT);				/* If yes then clear interrupt flag & return -2 */
		return -2;
	}
	if (status == 0xC8)					/* If last data byte transmitted with ack received TWEA = 0 */
	return -3;						/* If yes then return -3 */
	else							/* else return -4 */
	return -4;
}

char I2C_Slave_Receive()
{
	uint8_t status;						/* Declare variable */
	TWCR=(1<<TWEN)|(1<<TWEA)|(1<<TWINT);		 /* Enable TWI, generation of ack and clear interrupt flag */
	while (!(TWCR & (1<<TWINT)));				/* Wait until TWI finish its current job (read operation) */
	status = TWSR & 0xF8;					/* Read TWI status register with masking lower three bits */
	if (status == 0x80 || status == 0x90)		        /* Check weather data received & ack returned (TWEA = 1) */
	return TWDR;						/* If yes then return received data */
	if (status == 0x88 || status == 0x98)		        /* Check weather data received, nack returned and switched to not addressed slave mode */
	return TWDR;						/* If yes then return received data */
	if (status == 0xA0)					/* Check weather STOP/REPEATED START received */
	{
		TWCR |= (1<<TWINT);				/* If yes then clear interrupt flag & return 0 */
		return -1;
	}
	else
	return -2;						/* Else return 1 */
}

 

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

Best to remove the delay's and print statements out of the middle of the I2C transfers and print status messages after the I2C_stop() functions.

At lease your I2C functions return status values, not like most we see here that return void!!

Use a logic analyzer to trouble shoot transfer functions...

 

It is difficult to design and trouble shoot both sides of a comm channel when both ends are of unknown behavior, its better if one end is known to work!

Can you substitute a nI2C slave (temp sensor, or eeprom) and get your master working first?  Or  use a known good master and get your slave code working first?

 

Simplify your code, have master send only start/address/stop,  does the slave ack its address?

 

Once that works, add master sending one byte, does slave rx data correctly?

 

Once that works, add master receiving one data byte, does slave respond with correct data with master sending NAK and does master display it correctly?

 

Once that works, expand to sending multi-byte data one way, then the other!

 

Jim

 

edit spelling

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274

 

 

 

Last Edited: Mon. Feb 11, 2019 - 02:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I agree 100% with ki0bk.   It is very difficult to debug an unknown Master and an unknown Slave.

 

1.  Always use a proven device for one side e.g. EEPROM chip as Slave.   Arduino Slave sketch  (Wire.h uses 7-bit addresses).

2.  Debug your Master.

3.  Debug your Slave with an Arduino Master sketch.

4.  Finally test your debugged Master with your debugged Slave.

 

You can use any proven Master or proven Slave during development. 

 

The I2C bus requires external pullup resistors.

Always use return values from I2C library code.

 

David.

Last Edited: Mon. Feb 11, 2019 - 03:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for your responses, i will try to implement your suggestions today.

ki0bk sadly I don't have access to logic analyzer, slave revives data with no problem or delay.

david.prentice I don't have any EEPROM but i will try with arduino. I am using 4.7k pull up resistors on both lines.

 

In meantime I tried to do something abut this myself and noticed few things, after sending data and using I2C_Repeated_Start() function master gets stuck in this line: while (!(TWCR & (1<<TWINT)));

Slave gets stuck in I2C_Slave_Listen() function repeating it infinitely, if i change (status == 0xA8 || status == 0xB0 ) to (status == 0xA8 || status == 0xB0 || status == 0x80) in this function value of: status = TWSR & 0xF8; changes after few cycles form 0x80 to 0xA8 and after this change it sends data to master.

 

Once again thank you for responses.  

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

Most likely your not getting an ACK from the slave after the start/address, so it hangs at the repeated start....

 

Take it one verified step at a time.

 

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274

 

 

 

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

If you want help debugging,   ZIP up your AS7 project(s) and attach the ZIP to your post.

This means buildable mega16 and Arduino test sketch.

 

If you make it easy for Forum members to reproduce your problem,   you are more likely to get worthwhile answers.

 

Your code has non-void functions.   So it is starting from a good place.

 

David.

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

Hello,

I finally solved my problem, after adding one line of code everything works. I found another site https://www.engineersgarage.com/...

and after analyzing code I added TWCR = (1<<TWEA)|(1<<TWEN)|(1<<TWINT); line in slave's I2C_Slave_Listen() function and now it works as it's supposed to.

Now I2C_Slave_Listen() looks like this:

int8_t I2C_Slave_Listen()
{

	while(1)
	{
		uint8_t status;				/* Declare variable */
		TWCR = (1<<TWEA)|(1<<TWEN)|(1<<TWINT);	// Get acknowlegement, Enable TWI, Clear TWI interrupt flag
		while (!(TWCR & (1<<TWINT)));		/* Wait to be addressed */
		status = TWSR & 0xF8;			/* Read TWI status register with masking lower three bits */
		if (status == 0x60 || status == 0x68)	/* Check weather own SLA+W received & ack returned (TWEA = 1) */
		return 0;				/* If yes then return 0 to indicate ack returned */
		if (status == 0xA8 || status == 0xB0)	/* Check weather own SLA+R received & ack returned (TWEA = 1) */
		return 1;				/* If yes then return 1 to indicate ack returned */
		if (status == 0x70 || status == 0x78)	/* Check weather general call received & ack returned (TWEA = 1) */
		return 2;				/* If yes then return 2 to indicate ack returned */
		else
		continue;				/* Else continue */
	}
}

 

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

Iradeus wrote:
Check weather

sounds like its clear and sunny now!

 

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274

 

 

 

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

Iradeus wrote:
ki0bk sadly I don't have access to logic analyzer,

For EUR 7 you can buy all the Logic Analyser you need for decoding some 90+ variants of serial protocols.

See my signature for more info.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com