Great problem with TWI and interrupts

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

Hallo,
I have a problem concerning TWI and interrupts in general and i seems that I am unable to solve it and I am running out of ideas.
This is what I am trying to do:
I have two PCB and each of them has an Atmega32. These to MCUs have to talk to each other by TWI. I have a regular clock and on every pulse I try to send data from one of the MCUs(the Master device). The clock is interrupt driven (Timer1). So is the TWI.
The TWI sends data during the timer interrupt. IS THIS CORRECT?
When I use the step mode of driving the master in AVRStudio it seems to be sending the data, but the slave receives nothing. What is more, the master always receives ACK from the slave!!!!!!
Does anyone have an idea what could be wrong?
Please help.

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

nedsana wrote:
... The clock is interrupt driven (Timer1). So is the TWI.
The TWI sends data during the timer interrupt. IS THIS CORRECT?

Is your TWI function 'blocking' inside the timer interrupt? You shouldn't wait inside the timer interrupt for the TWI transcation to complete.

nedsana wrote:
...
When I use the step mode of driving the master in AVRStudio it seems to be sending the data, but the slave receives nothing.

Are you using a on-chip debugger like JTAGICE mkII?
Do you have an O-scope?

nedsana wrote:
...
What is more, the master always receives ACK from the slave!!!!!!
Does anyone have an idea what could be wrong?
Please help.

It's probable something in your code but since you didn't post your code no one can help you with that part.

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

Always receiving ack probably means the data line (SDA) is low.

Do you have pull up resistors on the SCL and SDA lines?

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

To jlau81: yes I have pull-up resistors R=1.5ohm

To atomicdog: I use JtagIce, handmade, but it is working fine. And yes I have an oscilloscope, and I can see some process communication going for while, but after that it dissaperars. I know why it stops, this is what it was supposed to do(starts an inf loop), but it had to long for a very short time.

Atomicdog, I am not sure what you mean by 'TWI function 'blocking' inside the timer interrupt', but indeed I am waiting for the TWI to complete in the timer interrupt. Actually I don't know how to make it in a different way. Can you suggest something.

And I did not post the code because it is a long one, but if you suggest this I will post later.

Thank you both.

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

I hope you meant 1.5kohm. If it is 1.5 ohm, a 5v supply will pull about 3amps of current!

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

Quote:
R=1.5ohm
Do you mean 1.5K?
You should use 4.7K ohm resistors. 1.5K is normally too low unless you have a special need for it (like if the trace/wire length is long).

Use the O-scope to see if you're really getting an ACK.
Does the Slave ACK its address and data bytes?
Have you tried debugging the Slave side? You can put break points in the Slave ISR to see how far the I2C transaction is really getting.

Quote:
Atomicdog, I am not sure what you mean by 'TWI function 'blocking' inside the timer interrupt', but indeed I am waiting for the TWI to complete in the timer interrupt. Actually I don't know how to make it in a different way. Can you suggest something.

A 'blocking' function is function that waits for data to be transfered before it returns.
You're using timer 1 as a system timer so the timer ISR happens every ###ms, right?
When you enter the timer ISR you check if the previous TWI transaction completed OK. If it did then you can just send the new data. If the transcation failed then you can re-send the data or do whatever is required.
Since all the real work is done in the TWI ISR all you need to do is point to the data array you want to send, set the # of bytes to send, do a Start and exit the timer ISR. The timer ISR will be triggered again in ###ms so you don't need to wait. This will allow your program to do other stuff while the I2C transcation is happening. This way you can take FULL advantage of the TWI interrupt routine.

Quote:
And I did not post the code because it is a long one, but if you suggest this I will post later.
You don't have to post all of the code just parts of it that are about TWI (like the TWI ISR).

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

I think atomicdog is assuming that the TWI interrupt is enabled (as opposed to just the TIMER interrupt), otherwise things are a bit different.

My advice is to do nothing in the TIMER ISR but set a flag (a single bit in a register of your choice will do). The task code should poll (test regularly) this bit, and if set, service the TWI and anything else needing to be done, then clear the bit.

Doing this reduces the amount of time the TIMER ISR takes from your important task code (and is a step towards a 'real time' operating system).

"A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools."
-- Douglas Adams

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

KillerSpud wrote:
I think atomicdog is assuming that the TWI interrupt is enabled (as opposed to just the TIMER interrupt), otherwise things are a bit different...

In the first post nedsana said...

Quote:
...The clock is interrupt driven (Timer1). So is the TWI.

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

Thank you for the answers guis,
indeed I use 1.5k resistors. I picked up this value, because I might need very high speed for the TWI communication (400kHz is the maximum speed as far as I know, although I read that some people run the TWI in higher speeds). Can I achieve 400kHz with 4.7k resistors? I calculated the resistors by using the formulas given in the datasheet.

Quote:
Use the O-scope to see if you're really getting an ACK.
Does the Slave ACK its address and data bytes?
Have you tried debugging the Slave side? You can put break points in the Slave ISR to see how far the I2C transaction is really getting.

I used the oscilloscope and I saw pulses on both SCL and SDA pins.
As I have another TWI communication going during the main cycle of the master device, I made the fallowing:
I used break points in the slave device while the main cycle of the master device was running. An I saw it received all the data I send. So obviously the TWI is working. The problem arises when I try to send data during the timer1 interrupt. This is why I asked you about how to make timker1 and TWI interrupts work together.

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

Before sending the code I will make some more explanations:
I don't want to use any flags set during the timer interrupt to start the TWI. This would mean checking their state on every cycle of the main loop. The main loop itself has lots of other things to. As I can't say exactly when the Timer interrupt will appear, I fear that the master and the slave devices will loose their synchronization. This can happen because the TWI in this particular case is used to make the slave device execute some code. If the slave receives data too late it will work in a wrong way.
The second note I want to make is: I am not very familiar with the pointers and I avoid using them(I should change this). Atomicdog, you suggested:

Quote:

Since all the real work is done in the TWI ISR all you need to do is point to the data array you want to send, set the # of bytes to send, do a Start and exit the timer ISR. The timer ISR will be triggered again in ###ms so you don't need to wait. This will allow your program to do other stuff while the I2C transcation is happening. This way you can take FULL advantage of the TWI interrupt routine.

Can you show me some example, how to do this? I am going to try this right now, but I am not sure if I will succeed.

Here is the code(the comments are not written in english, sorry, and as I am still working on it, nlt every part of it has been developed properly):
void init(void)
{...
//puskam taimer 0 na prescaler /8. Izpolzva se za 6IM na didoite.
TCCR0=0b00000010;
//nastroivam taimer 2 za funkciqta delay
TCCR2=0b00000010; //8
//Taimer 1 se nastroiva da dava clock kam 595ta i data send kam 595.
//Nastroivam TIMER1 v regim NORMAL
TCCR1A=0x00;
TCCR1B=0b00000010;//x8 prescaler
TCNT1H=0x00;
TCNT1L=0x00;
OCR1AH=0x01;
OCR1AL=CLOCK;
//tozi registyr e ob6t za trite taimera
TIMSK=0b00010000;

//TWI nastroiki
TWBR=0x20;//4estotata na clock-a za predavbane na danni e CPU_frequency/(16+2*TWBR*4^0)=200kHz
//TWBR=0x10;//CPU_frequency(16+2*TWBR*4^0)=400kHz

//adres na MCU1 MASTER
//TWAR=0x02;//Ne iskam general call!!!//master

//adsres na MCU2 SLAVE
TWAR=0x04;// Ne iskam general call!!!

//iztrivam TWWC bita
TWCR=0b01000101;
sei();
}

//FUNKCII ZA NASTROIVANE NA REGISTRITE ZA TWI ZA RAZLI$NITE RAGIMI
////////////////////////////// ZA MASTER !!!!!!!!! ////////////////////////
void start(void)
{
TWCR=TWI_START;//izpra6tam START uslovie
//iz4akvam da polu4a saob6tenie 4e uslovieto start e polu4eno
//while((TWCR & IS_TWINIT)!=IS_TWINIT)
while((TWCR & IS_TWINIT)!=IS_TWINIT && TWSR!=0xf8)
{}
}

//f-q za izvikvane na STOP na TWI za MASTER MCU
////////////////////////////// ZA MASTER !!!!!!!!! ////////////////////////
void stop(void)
{
TWCR=TWI_STOP;
twi_in[0x00]=0x00;
twi_in[0x01]=0x00;
}

//sled polu4avane na stop ot Mastera, zaregdam TWCR po sledniq n4ain
void stop_slave(void)
{
TWCR=TWI_STOP_SLAVE;
}

//ako polu4a NACK sled izpra6tane na ADR+W
void repeat_start(void)
{
start();
}

////////////////////////////// ZA MASTER !!!!!!!!! ////////////////////////
//f-q za izpra6tane na SLA+W
void master_SLA_W(void)
{
if(TWAR==0x02)
{
TWDR=0x04;//ako ustroistvoto e master-a s dres 2, zaregdam adresa na slave-4
}
else
{
TWDR=0x02;
}
TWCR=TWI_SEND;//izpra6tam
while((TWCR & IS_TWINIT)!=IS_TWINIT)
{}
}

////////////////////////////// ZA MASTER !!!!!!!!! ////////////////////////
//f-1 za izpra6tane na byte po TWI
void master_send_data(unsigned char data)
{
TWDR=data;//podavam adresa na slave
TWCR=TWI_SEND;//izpra6tam
while((TWCR & IS_TWINIT)!=IS_TWINIT)
{}
}

////////////////////////////// ZA SLAVE !!!!!!!!! ////////////////////////
//polu4il sam SLA+W i kazvam 4e sam gotov da priemam danni
void slave_ready(void)
{
TWCR=TWI_RECEIVE;
while((TWCR & IS_TWINIT)!=IS_TWINIT)
{}
twi_out[0x00]=0x00;
twi_out[0x01]=0x00;
}

////////////////////////////// ZA SLAVE !!!!!!!!! ////////////////////////
//polu4avane na danni
unsigned char slave_receive(void)
{
unsigned char temp;
temp=TWDR;
TWCR=TWI_RECEIVE;
while((TWCR & IS_TWINIT)!=IS_TWINIT)
{}
return temp;
}

////////////////////////////// ZA SLAVE !!!!!!!!! ////////////////////////
//s tazi funkciq izvar6vam priemaneto na dannite ot TWI
void rec_data(void)
{
unsigned char temp;

temp=slave_receive();

twi_got_data[data_index]=temp;
if(data_index<0x80)
data_index++;

if(twi_out[0x00]==0x00)//ako do momenta ne sam polu4aval danni po TWI ili sam priklu4il s komunikaciqta zna4i twi_out[0x00]=0
{
//twi_out[0x00]=slave_receive();
twi_out[0x00]=temp;
}
else//ako ve4e samo polu4il danni, zna4i tova e kontrolen byte. Proverqvam kakva e stoijnsotta mu:
{
switch(twi_out[0x00])
{
case 0x55://'U' => move Up
mode=0x01;
break;

case 0x44://'D' => podava komanda koqto opredlq regim na dvigenie na nadpisa
mode=0x02;
break;

case 0x4c://'L' => 'Left' podava komanda koqto opredlq regim na dvigenie na nadpisa
mode=0x03;
break;

case 0x52://'R' => 'Right' podava komanda koqto opredlq regim na dvigenie na nadpisa
mode=0x04;
break;

case 0x53://'S' => 'Static' podava komanda koqto opredlq regim na dvigenie na nadpisa
mode=0x05;
break;

case 0x43://'C' => 'CLEAR MESSAGE' podavam komanda za iztrivane na saob6tenieto
clear_mess();
break;

case 0x56://'V' => 'Velocity' podavam stojnostta koqto opredelq skorostta na dvigenie na nadpisa
velosity=slave_receive();
break;

case 0x42://'B' => 'Brightness' podavam stojnostta, koqto opredelq qrkostta na nadpisa
brightness=slave_receive();
break;

case 0x48://'H' => 'Hold' podavam stojnostta koqto opredelq zadarganeto na nadpisa
stat=slave_receive();
break;

case 0x49://'I' => Info podavam dukvite polu4eni po USART
twi_out[0x01]=get_char(slave_receive());
break;

case 0x45://'E' => 'End' communication
twi_out[0x01]=slave_receive();
break;

case 0x4D://'M' => 'Masiv' podavam masiv kam neaktivniq procesor za da izpi6em tarsenata kolona
//TWI_data[twi_index]=slave_receive();//popalvam masiva na neaktivniq procesor
TWI_data[twi_index]=temp;
if(twi_index<0x06)
{
twi_index++;
}
else//kogato popalnq tozi masiv, go izpisvam
{
twi_index=0x00;
PORT_ctrl(TWI_data[0x00],TWI_data[0x01],TWI_data[0x02],TWI_data[0x03],TWI_data[0x04],TWI_data[0x05],TWI_data[0x06]);
}
break;

case 0x50://'P' => 'Play' zaredi masivite display_x na neaktivniq procesor
twi_go=0x01;
//temp=slave_receive();
break;

case 0x41://'A' => 'Active' aktivirai drugiq procesor
MCU_on=0x01;
//temp=slave_receive();
break;
}
}
}

////////////////////////////// ZA MASTER !!!!!!!!! ////////////////////////
//funkciq s koqto aktiviram pra6taneto na danni po TWI
void twi_send(unsigned char in, unsigned char command)
{
//zapisvam promenlivata koqto iskam da predavam v bufera za predavane
twi_in[0x00]=command;
twi_in[0x01]=in;
//zapo4vam komunikaciq po TWI
start();
}

//obrabotvane na prekasvaneto za TWI
ISR(TWI_vect)
{
unsigned char stat_reg=0x00;

stat_reg=SREG;
cli();

switch(TWSR)
{
case 0x08://izpraten e START
master_SLA_W();
break;

case 0x10://izpraten e REPEATED START
master_SLA_W();
break;

case 0x18://izpraten e WRITE to SLAVE i e polu4en ACK
master_send_data(twi_in[twi_i]);//izpra6tam parviq byte ot saob6tenieto. Pri sledva6toto vlizane v prekasvaneto trbva da otida na case 0x28
if(twi_i<0x01)
{
twi_i++;
}
else
{
stop();
twi_i=0x00;
}
break;

case 0x20://izpraten e WRITE to SLAVE i ne e polu4en ACK
stop();
break;

case 0x28://izpraten e parviq byte na saob6tenieto i sledva da podam nov byte
master_send_data(twi_in[twi_i]);
if(twi_i<0x01)
{
twi_i++;
}
else
{
stop();
twi_i=0x00;
}
break;

case 0x30://izpraten e DATA BYTE no ne e polu4en ACK
stop();
break;

case 0x38://arbitration lost
stop();
break;

case 0x40://izpraten e READ from SLAVE i e polu4en ACK
break;

case 0x48://izpraten e READ from SLAVE no ne e polu4en ACK
break;

case 0x50://polu4eni sa danni i e izpraten ACK
break;

case 0x58://polu4eni sa danni no ne e izpraten ACK
break;

case 0x60://polu4en e WRITE to SLAVE i e varnat ACK
slave_ready();
break;

case 0x68://arbitration lost po vreme na WRITE/READ to/from SLAVE. Polu4en e WRITE to SLAVE i e izpraten ACK
break;

case 0x70://poluchen e GENERAL CALL i e zipraten ACK
break;

case 0x78://arbitration lost po vreme na WRITE/READ to/from SLAVE. Polu4en e GENERAL CALL i e izpraten ACK
break;

case 0x80://adresiran e predi tova, polu4en e WRITE to SLAVE, polu4ena e data i e izpraten ACK
rec_data();
break;

case 0x88://adresiran e predi tova, polu4en e WRITE to SLAVE, polu4ena e data i NE E izpraten ACK
break;

case 0x90://adresiran e predi tova, polu4en e GENERAL CALL, polu4ena e data i e izpraten ACK
break;

case 0x98://adresiran e predi tova, polu4en e WRITE to SLAVE, polu4ena e data no ne e izpraten ACK
break;

case 0xA0://polu4en e STOP ili REPATED START dokato o6te e adresiran kato SLAVE
stop_slave();
break;

case 0xA8://polu4en e sobstveniq READ from SLAVE i e izpraten ACK
break;

case 0xB0://arbitration lost po vreme na WRITE/READ to/from SLAVE. Polu4en e READ from SLAVE i e izpraten ACK
break;

case 0xB8://predadena e data ot TWDR i e polu4en ACK
break;

case 0xC0://predadena e data ot TWDR no ne e polu4en ACK
break;

case 0xC8://izpraten e posleden byte ('TWEA=0'), polu4en e ACK
break;

case 0xf8://TWI iz4akva vklu4vane
break;

case 0x00://Gre6ka pri inicializarento na komunikaciqta
break;
}

SREG=stat_reg;
sei();
}

void leds_on(unsigned char content[0x07][0x40])
{
unsigned char i;

//izpra6tam sadarganieto na kolonata koqto izpisvam na aktivniq ekran po TWI, za da moge vtorata platka, da doizpi6e sadarganieto na kolonata
for(i=0x00;i<0x07;i++)
{
twi_send(content[i][write],0x4d);
}

PORT_ctrl(content[0x00][write],content[0x01][write],content[0x02][write],content[0x03][write],content[0x04][write],content[0x05][write],content[0x05][write]);

//ako write stigne do 64 smenqme aktivniq procesor i nuliram write
if(write==0x40)
{
MCU_on=0x00;
twi_send(0x01,0x41);
write=0x00;
}
}

ISR(TIMER1_COMPA_vect)
{
unsigned char hold_SREG;
unsigned char temp=0x00;

hold_SREG=SREG;//zapazvam status registara
//cli();//zabranqvam prekasvaniqta
sei();

if(MCU_on==0x01)
{
//1. Proverqvam sastoqnieto na PORTD 5
temp=PORTD;
temp=temp & 0b00100000;

if(temp==0x00)//ako do momenta e bil v nisko nivo podavam impuls i go slagam vav visoko nivo
{
clocks++;

if(clocks>=0x02 && clocks<=0x41)
{
write++;
}

if(clocks>=0x42)//podavam bita koito obhogda kolonite
{
//vdigam bit na DATA SEND
PORTD = PORTD & 0b11101111;
PORTD = PORTD | 0b00010000;
//4akam
delay(SERIAL);
//vdigam bit na CLOCK
PORTD=PORTD & 0b11011111;
PORTD=PORTD | 0b00100000;
//4akam
delay(SERIAL);
//svalqm bit na DATA SEND
PORTD = PORTD & 0b11101111;
PORTD = PORTD | 0b00000000;

clocks=0x00;
}
else
{
PORTD=PORTD & 0b11011111;
PORTD=PORTD | 0b00100000;

PORTA=0x00;
PORTB=0x00;

if(disp==0x01)
{
leds_on(display_1);
}
else
{
leds_on(display_2);
}
}
}
else//ako do momenta sam bil vav visoko nivo, minavam v nisko
{
PORTD=PORTD & 0b11011111;
PORTD=PORTD | 0b00000000;
}

TCNT1H=0x00;
TCNT1L=0x00;
}
// else
// {
// if(clocks==0x41)
// {
// clocks=0x42;
// }
// }

SREG=hold_SREG;
//sei();
}

NOTE: the function twi_send() is used to initiate the TWI. The START condition of the TWI is called in twi_send().

Thank you.

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

Oh, I forgot the definitions:
//TWI difinitions
#define TWI_START 0b10100101
#define TWI_STOP 0b10010101
#define IS_TWINIT 0b10000000//setnat li e TWINT
#define TWI_SEND 0b10000101
#define TWI_RECEIVE 0b11000101
#define TWI_STOP_SLAVE 0b11000101

unsigned char MCU_on;//pokazva koi MCU zadava clock kam 595. Toi e aktivniq i kazva na drugoq kakvo da izpisva. 1=aktiven, 0=neaktiven
unsigned char TWI_data[0x07]={0x00,0x00,0x00,0x00,0x00,0x00,0x00};//tova e masivat koito trbva da se izpi6e v momenta
unsigned char twi_i=0x00;//promenliva koqto se izpolzva samo za popalvane na TWI_data
unsigned char twi_index=0x00;//izpolzva se za popalvane na TWI_data
unsigned char twi_in[0x02]={0x00,0x00};//prez tozi masiv se predavat danni po TWI. Na poziciq nula e regimat u sled tova dannite
unsigned char twi_out[0x02]={0x00,0x00};

I am not using the avr_libc.

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

Please can you re-edit your post to use CODE tags.

No-one minds if your comments are not in English.
They do mind if they cannot read the code because there are no spaces.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void init(void)
{...
//puskam taimer 0 na prescaler /8. Izpolzva se za 6IM na didoite.
TCCR0=0b00000010;
//nastroivam taimer 2 za funkciqta delay
TCCR2=0b00000010; //8
//Taimer 1 se nastroiva da dava clock kam 595ta i data send kam 595.
//Nastroivam TIMER1 v regim NORMAL
TCCR1A=0x00;
TCCR1B=0b00000010;//x8 prescaler
TCNT1H=0x00;
TCNT1L=0x00;
OCR1AH=0x01;
OCR1AL=CLOCK;
//tozi registyr e ob6t za trite taimera
TIMSK=0b00010000;

//TWI nastroiki
TWBR=0x20;//4estotata na clock-a za predavbane na danni e CPU_frequency/(16+2*TWBR*4^0)=200kHz
//TWBR=0x10;//CPU_frequency(16+2*TWBR*4^0)=400kHz

//adres na MCU1 MASTER
//TWAR=0x02;//Ne iskam general call!!!//master

//adsres na MCU2 SLAVE
TWAR=0x04;// Ne iskam general call!!!

//iztrivam TWWC bita
TWCR=0b01000101;
sei();
}

//FUNKCII ZA NASTROIVANE NA REGISTRITE ZA TWI ZA RAZLI$NITE RAGIMI
////////////////////////////// ZA MASTER !!!!!!!!! ////////////////////////
void start(void)
{
     TWCR=TWI_START;//izpra6tam START uslovie
      //iz4akvam da polu4a saob6tenie 4e uslovieto
      //start e polu4eno
      //while((TWCR & IS_TWINIT)!=IS_TWINIT)
      while((TWCR & IS_TWINIT)!=IS_TWINIT && ...
TWSR!=0xf8)
      {}
}

//f-q za izvikvane na STOP na TWI za MASTER MCU
////////////////////////////// ZA MASTER !!!!!!!!! ////////////////////////
void stop(void)
{
     TWCR=TWI_STOP;
     twi_in[0x00]=0x00;
     twi_in[0x01]=0x00;
}

//sled polu4avane na stop ot Mastera, zaregdam TWCR po sledniq n4ain
void stop_slave(void)
{
     TWCR=TWI_STOP_SLAVE;
}

//ako polu4a NACK sled izpra6tane na ADR+W
void repeat_start(void)
{
    start();
}

////////////////////////////// ZA MASTER !!!!!!!!! ////////////////////////
//f-q za izpra6tane na SLA+W
void master_SLA_W(void)
{
     if(TWAR==0x02)
     {
         TWDR=0x04;
     }
     else
     {
         TWDR=0x02;
     }
     TWCR=TWI_SEND;//izpra6tam
     while((TWCR & IS_TWINIT)!=IS_TWINIT)
     {}
}

////////////////////////////// ZA MASTER !!!!!!!!! ////////////////////////
//f-1 za izpra6tane na byte po TWI
void master_send_data(unsigned char data)
{
     TWDR=data;//podavam adresa na slave
     TWCR=TWI_SEND;//izpra6tam
     while((TWCR & IS_TWINIT)!=IS_TWINIT)
     {}
}

////////////////////////////// ZA SLAVE !!!!!!!!! ////////////////////////
//polu4il sam SLA+W i kazvam 4e sam gotov da priemam danni
void slave_ready(void)
{
     TWCR=TWI_RECEIVE;
     while((TWCR & IS_TWINIT)!=IS_TWINIT)
     {}
     twi_out[0x00]=0x00;
     twi_out[0x01]=0x00;
}

////////////////////////////// ZA SLAVE !!!!!!!!! ////////////////////////
//polu4avane na danni
unsigned char slave_receive(void)
{
     unsigned char temp;
     temp=TWDR;
     TWCR=TWI_RECEIVE;
     while((TWCR & IS_TWINIT)!=IS_TWINIT)
     {}
     return temp;
}


////////////////////////////// ZA SLAVE !!!!!!!!! ////////////////////////
//s tazi funkciq izvar6vam priemaneto na dannite ot TWI
void rec_data(void)
{
    unsigned char temp;
    temp=slave_receive();
    twi_got_data[data_index]=temp;
    if(data_index<0x80)
        data_index++;

    if(twi_out[0x00]==0x00)
    {
        //twi_out[0x00]=slave_receive();
        twi_out[0x00]=temp;
    }
     else
    {
        switch(twi_out[0x00])
        {
            case 0x55://'U' => move Up
                mode=0x01;
                break;
            case 0x44:
                mode=0x02;
                break;
            case 0x4c:
                mode=0x03;
                break;
            case 0x52:
                mode=0x04;
                break;
            case 0x53:
                mode=0x05;
                break;
            case 0x43:
                clear_mess();
                break;
            case 0x56:
                velosity=slave_receive();
                break;
            case 0x42:
                brightness=slave_receive();
                break;
            case 0x48: 
                stat=slave_receive();
                break;
            case 0x49:
                twi_out[0x01]=get_char...
                              (slave_receive());
                break;
            case 0x45:
                twi_out[0x01]=slave_receive();
                break;
            case 0x4D:
                TWI_data[twi_index]=temp;
                if(twi_index<0x06)
                {
                    twi_index++;
                }
                else
                {
                    twi_index=0x00
                    PORT_ctrl(TWI_data[0x00]...,
                    TWI_data[0x01],TWI_data[0x02]...
                    ,TWI_data[0x03],TWI_data[0x04]...
                    ,TWI_data[0x05],TWI_data[0x06]);
                } 
                break;
            case 0x50:
                twi_go=0x01; 
                break;
            case 0x41:
                MCU_on=0x01;
                break;
         }
    }
}

////////////////////////////// ZA MASTER !!!!!!!!!
//funkciq s koqto aktiviram pra6taneto na danni po TWI
void twi_send(unsigned char in, unsigned char command)
{
    twi_in[0x00]=command;
    twi_in[0x01]=in;
    //zapo4vam komunikaciq po TWI
    start();
}


//obrabotvane na prekasvaneto za TWI
ISR(TWI_vect)
{
    unsigned char stat_reg=0x00;
    stat_reg=SREG;
    cli();
    switch(TWSR)
    {
        case 0x08://izpraten e START
           master_SLA_W();
           break;
        case 0x10://izpraten e REPEATED START
           master_SLA_W();
           break;
        case 0x18://WRITE to SLAVE i NOACK
           master_send_data(twi_in[twi_i]);
           if(twi_i<0x01)
           {
              twi_i++;
           }
           else
           {
              stop();
              twi_i=0x00;
           }
           break;
        case 0x20:
           stop();
           break;
        case 0x28:
           master_send_data(twi_in[twi_i]);
           if(twi_i<0x01) 
           {
               twi_i++;
           }
           else
           {
               stop(); 
               twi_i=0x00;
           }
           break;
           
           case 0x30:
               stop();
               break;

           case 0x38://arbitration lost
               stop();
               break;

           case 0x60:
               slave_ready();
               break;

           case 0x80:
               rec_data();
               break;

           case 0xA0:
               stop_slave();
               break;
      }
      SREG=stat_reg;
      sei();
}

void leds_on(unsigned char content[0x07][0x40])
{
    unsigned char i;
    for(i=0x00;i<0x07;i++)
    {
        twi_send(content[i][write],0x4d);
    }
    PORT_ctrl(content[0x00][write],content[0x01]... [write],content[0x02][write],content[0x03]...[write],content[0x04][write],content[0x05]...[write],content[0x05][write]);

    if(write==0x40)
    {
        MCU_on=0x00;
        twi_send(0x01,0x41);
        write=0x00;
    }
}

ISR(TIMER1_COMPA_vect)
{
   unsigned char hold_SREG;
   unsigned char temp=0x00;

   hold_SREG=SREG;//zapazvam status registara
   cli();//zabranqvam prekasvaniqta

   if(MCU_on==0x01)
   {
      //1. Proverqvam sastoqnieto na PORTD 5
      temp=PORTD;
      temp=temp & 0b00100000;

      if(temp==0x00)
      {
          clocks++;
          if(clocks>=0x02 && clocks<=0x41)
          {
              write++;
          }

          if(clocks>=0x42)
          {
            //vdigam bit na DATA SEND
              PORTD = PORTD & 0b11101111;
              PORTD = PORTD | 0b00010000;
              //4akam
              delay(SERIAL);
              //vdigam bit na CLOCK
              PORTD=PORTD & 0b11011111;
              PORTD=PORTD | 0b00100000;
              //4akam
              delay(SERIAL);
              //svalqm bit na DATA SEND
              PORTD = PORTD & 0b11101111;
              PORTD = PORTD | 0b00000000;

              clocks=0x00;
          }
          else
          {
              PORTD=PORTD & 0b11011111;
              PORTD=PORTD | 0b00100000;
              PORTA=0x00;
              PORTB=0x00;
 
              if(disp==0x01)
              {
                  leds_on(display_1);
              }
              else
              {
                  leds_on(display_2);
              }
          }
      }
      else
      {
          PORTD=PORTD & 0b11011111;
          PORTD=PORTD | 0b00000000;
      } 
      TCNT1H=0x00;
      TCNT1L=0x00;
   }
   SREG=hold_SREG;
   sei();
} 

I hope this is better:)

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

Thankyou for the formatted listing.

I would suggest that you treat the two AVR devices separately. One is always the Master, and the other is always the Slave.

I would also suggest that you NEVER use sei() and cli() inside an ISR.
There are masses of Master implementations using polling, interrupts and even bit-banging. Your Master AVR just concentrates on this one job.

There are Atmel app notes for implementing a Slave. You just have to insert your own functionality. e.g. if the Master makes a request, the Slave replies with whichever data is required. The Master can obviously make periodic requests to the Slave.

The convention with interrupt driven TWI is to build a "packet of data" to send, and then start the sequence. The IRQs then proceed to send the data in the packet. You can build the packet inside a Timer ISR or wherever you like. You NEVER want to wait for completion inside an ISR.

David.

I have never succeeded in having one device be both Master and Slave simultaneously.

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

Thank you for the answer David.
Unfortunately, what I am trying to do is making the devices work as Master and Slave simultaneously.
I know that TWI is supposed to work with packets of data. What I miss is how to organize my code for this purpose.

You said:

Quote:
I would also suggest that you NEVER use sei() and cli() inside an ISR.

Obviously I have some problems with this or with understanding the work of interrupts in general. Please, can you tell me if I think in a correct way:
Timer1 ISR has priority (vector) N8 while TWI ISR is N20. As you know I try to start the TWI during the timer interrupt. This would mean that the TWI will wait until Timer1 ISR is over and will start after that. This is the correct behavior, unless I allow the interrupts by placing SEI()in the Timer1 ISR. IS THIS CORRECT?

I ask this because the last thing I did is:
-> disable the interrupts by CLI() in the beginning of the Time1 ISR
-> used twi_send() function to initiate the start condition

In this case, after Timer1 ISR is over, the master device never starts the TWI ISR!!! Was it not supposed to be in the stack, waiting for the timer to finish?

One more thing I noticed, while debugging the Slave device:
it enters the TWI ISR to receive data and works correctly until the master send the stop condition. It seems the slave does not understand this. TWSR becomes 0x00 and the slave infinitely enters the TWI ISR!!!

I am already out of ideas. I would really appreciate some example code. Thank you.

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

I cannot help you here. I have never got simultaneous operation.

However in your case you can just be Master OR Slave. You can set a flag for which is your current role, and use a different state machine accordingly.

You have effectively got only two devices. You never want device#1 to talk to itself.
I was attempting to have one AVR that could communicate with itself as a Slave... with NO success.

David.

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

David, I can't use the devices only in Master or Slave mode. The reason is that I have two PCB and each of them has 8 74HC595 shift registers. In one moment I use the first group of 74HC595s and after I am done with them I activate the other 8. I want to make this transition on TWI command. This is why I need to be able to make the MCUs enter master or slave mode.
Thanks, Nedsana.

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

Surely
#1 sends data to #2
#1 receives data from #2
...
#1 sends a command to swap roles.
#2 sends data to #1
#2 sends a command to swap roles.
...

David.

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

This is exactly what I want to do, but it seems to be more difficult then i thought.

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

nedsana wrote:
Thank you for the answers guis,
indeed I use 1.5k resistors. I picked up this value, because I might need very high speed for the TWI communication (400kHz is the maximum speed as far as I know, although I read that some people run the TWI in higher speeds). Can I achieve 400kHz with 4.7k resistors? I calculated the resistors by using the formulas given in the datasheet.
The amount you can increase the pull-up values depends on the bus capacitance/trace length. If the signal on the O-scope looks good then don't worry about it. Since your running at 400Khz you may need to experiment with different pull-up values to get a clean signal.
4.7K is a good value for 100Khz but the trace/wire length may need to be short for it to work well at 4.7K.

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

nedsana wrote:

... I ask this because the last thing I did is:
-> disable the interrupts by CLI() in the beginning of the Time1 ISR
-> used twi_send() function to initiate the start condition

In this case, after Timer1 ISR is over, the master device never starts the TWI ISR!!! Was it not supposed to be in the stack, waiting for the timer to finish?


You don't need to disable interrupts in the ISR because it's already done for you.
Since twi_send() is 'blocking' inside the timer ISR it will never go to the TWI ISR with interrupts disabled.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void master_send_data(unsigned char data) 
{ 
     TWDR=data;//podavam adresa na slave 
     TWCR=TWI_SEND;//izpra6tam 
     while((TWCR & IS_TWINIT)!=IS_TWINIT) 
     {} 
}

You shouldn't wait in the TWI ISR like this, it defeats the purpose of using interrupts. You can see in the code I sent you that I clear the flag to send the data at the end of the ISR and then leave the ISR.

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

Thank you, Atomicdog,
I will go through your code. I really hope that it will be of use to me.

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

After getting really angry, I deleted all my old TWI functions. Now I use the TWI only in my main loop, I don't try to call it from the timer interrupt yet. It seems to be working fine, but what comes out as a question is:
How long does it take for the TWI to send one byte of data? I mean what is the average time to send the whole packet Start/Ack/Address/Ack/Data/Ack/Stop.
I found an article where they show a time diagram for sending 5 bytes, and it takes about 200us.

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

16MHz AVRs should manage a 400kHz bus with no problem. If you are just doing SVDP where V is the slave address, it is 18 bits or 45us + about 2us for the Start and Stop.

At 100kHz you are talking about ~185us. Your example must have been working at 225kHz to do the 45 bits in 200us.

SPI is considerably faster but you need a separate Enable for each device. The TWI bus can handle 127 devices at on the bus. ( although I would expect some assistance to drive this amount of capacitance )

David.

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

Halo, again. After a few weeks fighting with other stuff, I got back to the TWI. The last time I wrote here it was working fine, but now, I want to make some changes. I need to use a repeated start to send 3 extra bytes, after every TWI activation.
Indeed, I receive TWSR=0x10 after sending the repeated start, but after I send the address of the slave, TWSR becomes 0xf8. I read the datasheet, but I could not understand what I need to do when I receive this value. Can you give me some suggestions.

PS. Actually I don't need this repeated start. I can place these 3 bytes in the main message. But I would like to know how to handle this situation in future.

Thanks, Nedsana.

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

You should get 0x18 / 0x40 for an ACK
or 0x20 / 0x48 for a NACK from the slave.

You can do as many Repeated Starts as you like when the Master is addressing a SLAVE_W. However you must always "stop" a SLAVE_R by issuing a NACK to say that you do not want it to send any more bytes.

So a typical write sequence is:
....
This can be ended with either a or a
A typical Read sequence has already written any command registers and then:
...
followed by a or

If you miss the final the slave will not be happy.

I cannot think why you get a <0xF8> after a successful , unless you are resetting your Master TWI.

David.

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

I've never seen the 0xF8 status code while debugging but maybe...

    TWI is being reset (TWEN)
    TWINT is cleared twice