ISR(TWI__vect) läuft nicht richtig

Hallo,

ich versuche ein Multi-Master System aufzubauen.

Dafür will ich einen Arduino Mega2560 mit in das Netztwerk nehmen.

Dieser reagiert jedoch nicht richtig bei den Interrupts.

Meine Interrupt Routine wird von den anderen µC richtig ausgeführt:

ISR(TWI__vect)
{
//mach was
}

scheint aber bei dem Arduino nicht zu funktionieren. Woran liegt das?

Senden als Master und Empfangen als Master macht er mit meinem Code wie alle anderen µC auch.
Nur wenn es in die ISR gehen soll macht der Arduino blödsinn.

Weiß jemand Rat?

Wo bleibt der Code?

Die richtige Frage ist:
sschultewolter wo ist Deine Kristallkugel. :wink: :wink: :wink: :wink:
Grüße Uwe

Hallo,
also der relevante Code solte sein:

#include <inttypes.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/twi.h>


//ACK nach empfangenen Daten senden/ ACK nach gesendeten Daten erwarten
#define TWCR_ACK TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);

//NACK nach empfangenen Daten senden/ NACK nach gesendeten Daten erwarten
#define TWCR_NACK TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);

//switch to the non adressed slave mode...
#define TWCR_RESET TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(1<<TWSTO)|(0<<TWWC);


///////////TWI/////////
volatile uint8_t part_slave_send_data[2];
volatile uint8_t part_slave_read_data[2];
volatile uint8_t slave_read_zweck;
volatile uint16_t slave_read_data;
volatile uint16_t slave_send_data=9876;
volatile uint8_t read_parter;
volatile uint8_t send_parter;
/////////////////////


void init_twi_slave(uint8_t adresse)
{
  TWAR= adresse; //Adresse setzen
  TWCR &= ~(1<<TWSTA) | (1<<TWSTO);
  TWCR|= (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
}


void setup()
{
 init_twi_slave(8);  
TWBR = 72;                            //100kHz
}

void loop()
{
  sei ();  
 while(1) 
{
  
if (slave_read_zweck!=0)
{
  if (slave_read_zweck==1)  
  {
   //mache was 
  }  
  slave_read_zweck=0;  
}

}
}

ISR(TWI__vect)
{   
//mach was <-- das was hier steht wird schon nicht mehr ausgeführt
switch (TW_STATUS) //TWI-Statusregister prüfen und nötige Aktion bestimmen
  {
    case TW_SR_SLA_ACK: // 0x60 Slave Receiver, Slave wurde adressiert
    TWCR_ACK; // nächstes Datenbyte empfangen, ACK danach senden
    break;    
    case TW_SR_DATA_ACK: // 0x80 Slave Receiver, ein Datenbyte wurde empfangen
    part_slave_read_data[read_parter]=TWDR; //Empfangene Daten wegschreiben
    read_parter++;
    TWCR_ACK; // nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern
    if (read_parter==3)
    {
      read_parter=0;
      slave_read_zweck=part_slave_read_data[0];
      slave_read_data=(part_slave_read_data[1]<<8)+part_slave_read_data[2];
    }
    break;
    //Slave transmitter
    case TW_ST_SLA_ACK: //0xA8 Slave wurde im Lesemodus adressiert und hat ein ACK zurückgegeben.
    //Hier steht kein break! Es wird also der folgende Code ebenfalls ausgeführt!   
    case TW_ST_DATA_ACK: //0xB8 Slave Transmitter, Daten wurden angefordert  
    part_slave_send_data[0]=(uint8_t)slave_send_data;
    part_slave_send_data[1]=slave_send_data>>8; 
    TWDR = part_slave_send_data[send_parter]; //Datenbyte senden
    send_parter++;
    if(send_parter==2)send_parter=0;
    TWCR_ACK;
    break;
    case TW_SR_STOP:
    TWCR_ACK;
    break;    
    case TW_ST_DATA_NACK: // 0xC0 Keine Daten mehr gefordert
    case TW_SR_DATA_NACK: // 0x88
    case TW_ST_LAST_DATA: // 0xC8  Last data byte in TWDR has been transmitted (TWEA = “0”); ACK has been received
    default:
    TWCR_RESET;
    break;
  }
}

Wenn ich irgendwas an den anfang in der ISR schreibe wird das schon nicht mehr ausgeführt und das System hängt sich auf, sobald ich den Arduino von außen anspreche.

€:
Achso, und die Anderen Busteilnehmer kommen auch nicht dazu einen Fehler an zu zeigen, so als wenn dann jemand auf dem Buss steht und alles darauf wartet, dass es weiter geht muss aber auch nicht sein.

Schon mal einen Logikanalyzer an den Bus gehängt?

volatile uint8_t part_slave_send_data[2];
volatile uint8_t part_slave_read_data[2];

Sind die nicht ein bisschen klein, wenn du read_parter die Werte { 0 1 2 } annehmen lässt ?

Hallo,

ElEspanol:
Schon mal einen Logikanalyzer an den Bus gehängt?

ein Logikanalyzer hab ich leider nicht.

michael_x:
Sind die nicht ein bisschen klein, wenn du read_parter die Werte { 0 1 2 } annehmen lässt ?

Stimmt, interessanterweise geht es auf den Anderen auch so. Wenn ich das korrigiere ändert sich das Verhalten jedoch nicht :frowning:

€:
Ich hab mir ein Osziloskop ausgeliehen, leider ohne Speicher, daher ist es etwas schwer zu erkennen was genau passiert.
Aber ich würde sagen, dass auf der Taktleitung nur ein Wechsel von high auf low stattfindet, sobald ich die ISR des Arduinos anspreche und dann hängt sich der Arduino auf und hält die Taktleitung dauerhaft auf low und blockiert dadurch die anderen weiter zumachen. Sobald ich dann den Arduino abnehme, erkennen die anderen einen Fehler und machen wie gedacht weiter.

Bist du sicher, dass das macht was du willst:

 TWCR &= ~(1<<TWSTA) | (1<<TWSTO);

Die Konstruktion ist etwas seltsam. Das | hat Vorrang vor dem &= !

Hallo,

Serenifly:
Die Konstruktion ist etwas seltsam. Das | hat Vorrang vor dem &= !

Ja das kann man auch getrost weg lassen, das hatte ich nur gemacht, um eine Stopbedingung im Statusregister zu haben, falls bei der ersten Übertragung irgendwas unvorhergesehenes passiert, damit dann die Übertragung gleich beendet wird und wider irgendwas definiertes passieren kann.

Ich hab das ohne probiert, da verändert sich am Verhalten nichts.

Ok, aber der Punkt war das man das anders machen muss wenn du das TWSTO Bit gesetzt haben oder löschen willst. :slight_smile:

Ja der sollte einmal löschen und ein mal setzen. Wenn ich beide Lösche, dann geht der Bus gar nicht.

Dshing:
Ja der sollte einmal löschen und ein mal setzen.

Macht es aber nicht. Du machst erst mal ~(1<<TWSTA) | (1<<TWSTO) und das Ergebnis wird dem Inhalt des Register verundet. Dadurch hat das Oder gar keine Auswirkung auf das Register.

Du musst da schon zwei Schritte draus machen:

TWCR &= ~(1<<TWSTA);
TWCR |= (1<<TWSTO);

Test Code:

void setup()
{
  Serial.begin(9600);
  delay(1000);

  byte reg = TWCR;
  printByte(TWCR);
  TWCR &= ~(1 << TWSTA) | (1 << TWSTO);
  printByte(TWCR);

  Serial.println("---");
  TWCR = reg;
  TWCR &= ~(1 << TWSTA);
  TWCR |= (1 << TWSTO);
  printByte(TWCR);
}

void loop()
{
}

void printByte(byte b)
{
  for (int i = 7; i >= 0; i--)
    Serial.print(bitRead(b, i));
  Serial.println();
}

Ergebnis:

00000000
00000000
---
00010000

Nur bei der zweiten Version ist Bit 4 gesetzt

Du kannst auch mal spaßhalber TWSTA (Bit 5) am Anfang per Hand setzen. Da wird es vielleicht noch deutlicher:

00100000
00000000
---
00010000

Das einzige was mich wundert ist dass der Compiler das diesmal nicht in CBI und SBI Assembler Befehle umgesetzt hat...

Achso ja stimmt, du hast natürlich recht.

Aber das ist letztlich genauso als wenn ich nichts hinschreibe und damit leider immer noch nicht des Pudels Kern.

Ich hab es dennoch mal in der richtigen Schreibweise probiert, aber es ändert sich auch nichts am Verhalten.

TWCR &= ~(1<<TWSTA);
TWCR |= (1<<TWSTO);