I2C per Interrupt auf Atmega32

OP #2160917
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
OP #2163766
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?
Gast #2179735
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).
OP #2180522
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
}
#2181066
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);
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren