Forum: Mikrocontroller und Digitale Elektronik I2C per Interrupt auf Atmega32


von Phillip H. (philharmony)


Lesenswert?

Hallo

Ich möchte zunächst den PCA9634 von einem Atmega32 aus ansteuern. Dabei 
soll die I2C-Routine aber so flexibel wie möglich gehalten werden, damit 
ich später ohne größeren Aufwand auch andere I2C Chips benutzen kann.
Da der Contoller noch jede Menge anderen Kram zu tun hat, möchte ich das 
ganze per Interrupt machen.
Den UART habe ich schon per Ringpuffer interruptbasiert aufgebaut, daher 
erscheint es mir recht sinnvoll, den I2C-Bus entsprechend zu 
realisieren, daß der Aufrufer einfach sein Datenpaket in den Puffer 
legt, der dann von der ISR abgearbeitet wird.
Allerdings ist der UART ASCII-basiert, und ich kann einzelne Blöcke sehr 
schön mit dem Terminatorzeichen (0x00) von einander trennen. I2C ist 
aber ja Byte-Basiert und ich kann keine Zeichen reservieren. Daher hatte 
ich jetzt die Idee, jedem Datenpaket ein Byte voranzustellen, in dem die 
Anzahl der zu diesem Paket gehörenden Daten steht. Eventuell zur 
Überprüfung ans Ende eine Checksumme oder doch ein bestimmtes Zeichen. 
Nun möchte ich aber das Rad auch nicht neu erfinden, daher die Fragen, 
hat jemand sowas in der Art schonmal gemacht und könnte ein C Beispiel 
posten, bzw. bin ich mit meiner Idee völlig auf dem Holzweg?

Viele Grüße

Phil

: Verschoben durch User
von Phillip H. (philharmony)


Lesenswert?

Ok um das nochmal auszugraben, ich hab das jetzt so gemacht wie oben 
beschrieben. Die Ringpuffer-Sache funktioniert auch soweit, allerdings 
ist mir bei der Ansteuerung der TWI-Schnittstelle eine Sache nicht ganz 
klar.
Laut Datenblatt ist ja im TWCR das TWINT-Bit der Trigger, um die nächste 
Aktion durchzuführen. TWEN möchte ich eigentlich beim initialisieren 
einmal setzen und es dann gesetzt lassen. Die restlichen Bits TWSTA, 
TWSTO etc. wollte ich dann jeweils einzeln setzen und zuletzt das TWINT 
Bit setzen (um die Interrupt-Flag zu "clearen").
Problem an der Sache: Es funktioniert nicht.
Wenn ich allerdings das TWCR explizit mit dem gesamten Bitmuster setze
(TWCR = (1<<TWINT)|(0<<TWSTA)|...), dann funktioniert es plötzlich. 
Warum?

Das zweite Problem betrifft die Stop-Condition. Diese löst nach 
Abschluss ja kein weiteres Interrupt aus. Da der angesprochene Chip aber 
per Default das soeben angewiesene erst nach Empfang der Stop-Condition 
ausführt, habe ich jetzt ein Problem, wenn ich mehrere Datenblöcke aus 
dem Puffer verschicken will.
(Also STA | SLA+W | OFFSET1 | VALUE1 | STOP | STA | SLA+W | OFFSET2 
|...)
Eine Möglichkeit wäre, die Stop-Condition direkt in der ISR anzuweisen, 
und dort zu warten, bis sie ausgeführt wurde, aber das widerspricht ja 
ein wenig der Idee einer Interrupt-Routine. Ich möchte auch nicht in der 
Mainfunction ständig nachschauen, ob der TWI grade nicht mehr läuft, 
aber noch Datenblöcke vorhanden sind, auch das widerspricht der Idee 
eines möglichst eigenständigen Hardware-Treibers. Habt Ihr da einen Tip 
für mich?

von Peter D. (peda)


Lesenswert?

Forum: Codesammlung
Wenn ihr eigene Programme oder Anleitungen geschrieben habt könnt ihr 
sie hier posten. Fragen werden gelöscht!

von Phillip H. (philharmony)


Lesenswert?

Oh sorry, da bin ich wohl in die falsche Rubrik gerutscht. Soll ich den 
thread neu aufmachen oder könnte ein Mod ihn verschieben?

von Andreas (Gast)


Lesenswert?

hallo was ist aus der geschichte  geworden ????

von Phillip Hommel (Gast)


Lesenswert?

Ich habe es wie oben beschrieben gemacht. Ringpuffer mit Anzahl der 
Folgenden Daten und einfacher Plausibilitäts-Checksumme am Ende.
Für die Stop-Condition prüfe ich in der ISR einfach, ob ein weiteres 
Paket im Puffer liegt (per mitlaufendem Zähler) und sende entweder nur 
eine Stop-Condition, oder Stop und danach Start sende (Stop ist ja nur 
ein Flankenwechsel der innerhalb eines Taktes geschieht).

von Andreas E. (andrease112)


Lesenswert?

Würdest Du mir deinen Code mal posten ?

von Phillip H. (philharmony)


Lesenswert?

Alles was Master Reciever ist, habe ich noch leer weil ichs im Moment 
nicht brauche. Geht bestimmt vieles auch eleganter, aber so tuts 
schonmal.
1
/*TWI Interrupt Routine*/
2
ISR(TWI_vect)
3
{
4
  static uint8_t mode;              //Betriebsmodus
5
  static uint8_t ct_data;             //Datenzähler
6
  static uint8_t data;              //Interner Datenpuffer
7
  static uint8_t checksum;            //Checksumme
8
//  static uint8_t input_buffer[128];        //Interner Pufferspeicher
9
10
  mode = (TWSR & TW_STATUS_MASK);          //Aktueller Modus aus Status Reg.
11
12
  if (number_i2c_out_frames == 0)          //wenn keine Daten im Puffer liegen
13
  {
14
    twi_stop();                  //TWI stoppen
15
    return;                    //Routine verlassen
16
  }
17
18
  switch (mode)                  //Abhängig von vorheriger Aktion
19
  {
20
    case TW_ARB_LOST:              //Wenn Kontrolle über Bus verloren (Anderer Master, Fehler)
21
    {
22
      twi_error(mode);            //Fehlerbehandlung ausführen  
23
      break;                  //Abfrage verlassen
24
    }
25
26
    case TW_START:                //Start Condition gesendet
27
    {
28
      ;                      //Weiter zu TW_REP_START, gleiche Anweisung!
29
    }
30
31
    case TW_REP_START:              //Repeated Start Condition gesendet
32
    {
33
      ct_data = get_from_ringbuffer(&i2c_out_buffer); //1. Element ist Anzahl der folgenden Daten
34
      checksum = ct_data;            //Checksumme beginnen
35
36
      if (ct_data-- == 0)            //Wenn Null, bedeutet das entweder einen Fehler, oder daß der Puffer Leer ist
37
      {                    //Decrement, da erstes Element nicht mit gesendet wird
38
        twi_error(mode);          //Fehlerbehandlung ausführen
39
        return;
40
      }
41
                          //Kein Break!!! Weiter zu TW_MT_ADR_ACK
42
    }
43
44
    case TW_MT_ADR_ACK:              //Wenn SLA + W gesendet und bestätigt wurde
45
    {
46
      ;                    //Kein Break!!! Weiter zu TW_MT_DATA_ACK
47
    }
48
49
50
    case TW_MT_DATA_ACK:            //Wenn Daten gesendet und bestätigt wurden
51
    {
52
      data = get_from_ringbuffer(&i2c_out_buffer); //Daten aus Puffer lesen
53
      checksum += data;            //Checksumme aufaddieren
54
55
      if (ct_data == 0)            //Wenn Datenzähler = 0
56
      {
57
        number_i2c_out_frames--;      //Zahl der Frames reduzieren
58
        if (checksum) twi_error(mode);    //Wenn Checksumme ungleich 0 dann Fehlerbehandlung (eigentlich direkt Puffer reset)
59
        else
60
        {
61
          twi_stop();            //Stop Condition senden
62
          if(number_i2c_out_frames)     //Wenn Zahl der Vorhandenen Frames nicht null
63
          {
64
            twi_start();        //Neue Übertragung starten
65
          }
66
        }
67
        return;                //Routine verlassen
68
      }
69
70
      ct_data--;                //Datenzähler verringern
71
      TWDR = data;              //Slave Adress + Datenrichtung in Senderegister schreiben
72
      TWCR = TW_SET_DATA;            //Senden
73
      break;
74
    }
75
76
    case TW_MT_ADR_NACK:            //Wenn SLA + W gesendet, aber nicht bestätigt wurde
77
    {  
78
      ;                    //Weiter zu TW_MT_DATA_NACK
79
    }
80
81
    case TW_MT_DATA_NACK:            //Wenn Daten gesendet, aber nicht bestätigt wurden
82
    {
83
      twi_error(mode);            //Fehlerbehandlung
84
      break;
85
    }
86
87
    case TW_MR_ADR_ACK:
88
    {
89
      break;
90
    }
91
92
    case TW_MR_ADR_NACK:
93
    {
94
      break;
95
    }
96
97
    case TW_MR_DATA_ACK:
98
    {  
99
      break;
100
    }
101
102
    case TW_MR_DATA_NACK:
103
    {
104
      break;
105
    }
106
107
    case TW_NO_STATE:
108
    {
109
      break;
110
    }
111
112
    case TW_BUS_ERROR:
113
    {
114
      break;
115
    }
116
117
    default:
118
    {
119
      break;
120
    }
121
122
    return;
123
  }
124
}

von Andreas E. (andrease112)


Lesenswert?

Das ist mal mein code:

es scheint ja auch zu funktionieren aber nur 1x.

hast du da nen Tip für mich ?

Uart vorher abschalten und nachher wieder anmachen ist noch zusätzlich 
drinne.

Gruß Andreas


ISR (TWI_vect )
{
  UCSR0B &= ~(_BV(RXCIE0)|(1<<RXEN0)|(1<<TXEN0));
  if (state_TWI_INT ==  TWI_STATE_IDLE )
  {

  }
  //------------------------------------
  else if (state_TWI_INT ==  TWI_STATE_SEND_START_1 )
  {
         state_TWI_INT++;

    //------------------------------------
    //    TWI_STATE_SEND_CMD    :
         i2cSendByte(0xD2);
         state_TWI_INT++;state_TWI_INT++;
  }
  //------------------------------------
  else if (state_TWI_INT ==  TWI_STATE_WAIT_OK_CMD )
  {
        state_TWI_INT++;
    //------------------------------------
    //     TWI_STATE_SEND_ADR    :
        i2cSendByte(i2c_adr);
         state_TWI_INT++;
  }
  //------------------------------------
  else if  (state_TWI_INT ==  TWI_STATE_WAIT_OK_ADR )
  {
        state_TWI_INT++;
    //------------------------------------
    //   TWI_STATE_SEND_START_2   :
        i2cSendStart();
        state_TWI_INT++;
  }
  //------------------------------------
  else if (state_TWI_INT == TWI_STATE_WAIT_OK_START_2 )
  {
        state_TWI_INT++;
    //------------------------------------
    //    TWI_STATE_REC_BYTE_1     :
        i2cReceiveByte(0);
        state_TWI_INT++;
  }
  //------------------------------------
  else if (state_TWI_INT ==  TWI_STATE_WAIT_REC_BYTE_1)
  {
        i2c_rec_data = 0 + TWDR ;
        state_TWI_INT++;
  }
  //------------------------------------
  else if (state_TWI_INT ==  TWI_STATE_FINISCH )
  {
        i2cSendStop();

       // cbi(TWCR, TWEN);  // Disable TWI
       // sbi(TWCR, TWEN);

        i2c_adr = 0;
        i2c_cmd = 0;

        state_TWI_INT =  TWI_STATE_IDLE;
  }
  //------------------------------------
  else
  {
        state_TWI_INT = TWI_STATE_IDLE;

  }

  sbi(TWCR,TWINT);
  UCSR0B = _BV(RXCIE0)|(1<<RXEN0)|(1<<TXEN0);
}

von Phillip H. (philharmony)


Lesenswert?

Hi Andreas,
Kann es sein, daß Du da was verwechselst? USART und TWI sind zwei 
komplett unterschiedliche Dinge!

von Andreas E. (andrease112)


Lesenswert?

ja ich schalte meine uart ab während ich den Interrupt bearbeite damit 
nix schief geht

von Phillip H. (philharmony)


Lesenswert?

Ach so, ok. Also ich hab beide ohne Probleme parallel laufen, die 
Hardware macht das ja auch selbständig und das Interrupt wird erst 
ausgelöst, wenn die aktuelle ISR abgearbeitet ist.
Was den restlichen Code angeht, wo genau hängts da denn, bzw. bis wohin 
kommt er?

von Andreas E. (andrease112)


Lesenswert?

Im Moment geht gar nix mehr ... ich meld mich wieder sobald ich wieder 
Interrupts bekomme

von Andreas E. (andrease112)


Lesenswert?

es ist gerade das write collition flag das mich im moment stört nachdem 
ich die Übertragung begonnen habe

von Phillip H. (philharmony)


Lesenswert?

Was für einen Chip willste denn ansprechen? Eventuell erwartet der 
erstmal eine Stop-Condotion meckert bei erneutem Start?

von Andreas E. (andrease112)


Lesenswert?

so geht wieder, aber nach die 2te komm geht nicht mehr sprich nach der 
ersten stopp, mit pollen gehts ja in der gleichen reihenfolge wie im 
interrupt handler

von Andreas E. (andrease112)


Lesenswert?

ITG3200 Gyro sensor

von Andreas E. (andrease112)


Lesenswert?

jetzt habe ich noch eingebaut twi disable -> enable am schluss jedes 
telgramms und jetzt gehts

von Phillip H. (philharmony)


Lesenswert?

Also das TWEN-Bit muss ja auch jedesmal mit gesetzt werden. Ich habe 
auch die Erfahrung gemacht, daß das einzelne setzen und löschen des Bits 
im Kontrollregister nicht funktioniert sondern das ganze Register auf 
einen Rutsch gesetzt werden muss. Warum das so ist verstehe ich aber 
auch nicht.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.