Clock Stretching beim I2C Bus

achim S.

Mitglied
16. Jan. 2010
704
13
18
Berlin Biesdorf
Sprachen
  1. ANSI C
Hallo
Kennt sich jemand mit Clock Stretching aus? So allgemein und dann speziell beim Attiny 841.
Habe den Bus bei Ai841 in den Griff bekommen, ja leider ohne Clock Stretching. Es gibt scheinar kaum Info dazu. Im DB steht drin das es geht und welches Register verwendet wird. Leider ist das fast alles was an Info zu finden ist.
Bruache dringend Hilfe dazu
achim
 
Grundsätzlich sind die Leitungen beim TWI wired-and, jeder Busteilnehmer kann sie low ziehen; high gehen sie nur wenn keiner sie low zieht über die pullups.

Im idle sind beide Leitungen high.

Eine Flanke an SDA während SCL high ist wird als Start/Stop interpretiert, dazwischen darf SDA nur wechseln, wenn SCL low ist.

Versuchen mehrere Master quasi-gleichzeitig den Bus in Besitz zu nehmen, bedienen sie jeder für sich die Leitungen. Aufgrund des wired-and der Clock paßt sich diese dem langsamsten Möchtegern-Master an (jeder Möchtegern-Master muß (bei jedem Bit) SCL überwachen, und auf ggf langsamere Master warten.) <-- Clock-Stretching
Aufgrund des wired-and von SDA ist ein low dominant ggü einem high, jeder Möchtegern-Master muß (bei jedem Bit) überwachen, ob ein von ihm gesendetes high auf dem Bus landet, oder ob er die Arbitrierung verliert, und sich als Master ausklinken muß.

Nur ein (Möchtegern-)Master darf eine fallende Flanke an SCL erzeugen, aber jeder Teilnehmer darf einen low-Pegel weiter auf low halten (streching).

Dein Tiny kann in Hardware nur Slave-TWI, Master und Arbitrierungskram interessiert ihn nicht.

Laut Datenblatt gibt es drei Ursachen, durch die er selbst die Clock strecht:
  • Wenn die Busgeschwindigkeit für ihn zu hoch ist, bremst er bei jedem Bit.
  • Wenn er durch die Startcondition aus dem Sleep geweckt wird, bremst er, bis er das MSB lesen konnte.
  • Wenn ein Interrupt-Flag (TWDIF oder TWASIF) zugeschlagen hat.
Die ersten beiden sind quasi dasselbe - SCL wird automatisch solange low-gehalten bis das jeweilige Bit gelesen oder geschrieben werden konnte.

Der letzte Fall erfordert üblicherweise eine Entscheidung. SCL wird automatisch solange low gehalten, bis diese Entscheidung getroffen wurde, indem die entsprechende Aktion über TWCMD gestartet wurde.

Eigentlich erledigt die Hardware selbst also das streching, in welchem Fall hast du konkret Probleme?

Wer ist denn der Master in Deinem Projekt, kommt er mit dem Streching klar?
 
Der Slave arbeitet mit dem Attiny 841. Er hat die Aufgabe von einem Poti die DC zu messen und den Wert entweder mit 8 Bit oder 10 Bit an den Master zu übertragen. Habe als Beispiel die übertragung vin 4 Werten mit 8 Bit drin. Als Master verwende ich einen Atmega 128. Das Programm vom Master läuft stabil und ohne Probleme. Leider dauert die ADC Messung länger als das wiederholte auslesen durch den Bus. Mach ich nur 1 Messung fällt es nicht auf. Nehme ich mehrere Messungen und Mittelwertbildung dazu gibt es Probleme.


CodeBox C
#define F_CPU 16000000UL                // Angabe der Frequenz, wichtig für die Zeit
#include <util/delay.h>                    // Einbindung Datei Pause
#include <avr/io.h>                        // Einbindung Datei Ausgänge
#include <stdio.h>
#include <avr/interrupt.h>

#define I2C_SLAVE_ADDRESS       0x52
#define I2C_DATA_INTERRUPT      0x80
#define I2C_BUS_COLLISION       0x08
#define I2C_ADDRESS_STOP_MATCH  0x40

volatile uint8_t command;
volatile uint8_t tx_buf[4];
volatile uint8_t tx_buf_index = 0;
volatile uint8_t wert1;
volatile uint8_t wert2;

//volatile uint16_t sum;

void init_I2C()
  {
    TWSA = I2C_SLAVE_ADDRESS;    // Slave Adresse
    TWSD = 0xFF;
    TWSCRA = (1 << TWEN)           // Zwei-Draht-Schnittstelle aktivieren
    | (1 << TWSHE)              // TWI SDA Haltezeit aktivieren
    | (1 << TWASIE)             // TWI-Adresse/Stop-Unterbrechung aktivieren
    | (1 << TWSIE)              // TWI Stop Interrupt Enable aktivieren
    | (1 << TWDIE);             // TWI Datenunterbrechung aktivieren
    sei();
  }

ISR( TWI_SLAVE_vect )
  {
    uint8_t status = TWSSRA & 0xC0;
    if (status & I2C_DATA_INTERRUPT)    // Daten wurden vom Master empfangen oder werden angefordert
      {
        if (TWSSRA & (1 << TWDIR))        // Master fordert Daten vom Slave an
          {
            if(tx_buf_index >= sizeof(tx_buf))
              {
                tx_buf_index=0;
              }
            TWSD = tx_buf[tx_buf_index];// Daten in Sendepuffer
            tx_buf_index++;
            TWSCRB = ((1<<TWCMD1)|(1<<TWCMD0));   // Das ist die Zeile wo das ACK sendet !!!!!!!!
          }
        else             // Master sendet Daten zum Slave
          {
            TWSCRB |= ((1<<TWCMD1)|(1<<TWCMD0));
            command = TWSD;
          }
      }
    else if (status & I2C_ADDRESS_STOP_MATCH)
      {
        if (TWSSRA & I2C_BUS_COLLISION)
          {
            TWSCRB = (1<<TWCMD1);
          }
        else
          {
            if (TWSSRA & (1<<TWAS))
              {            // ACK
                TWSCRB =  ((1<<TWCMD1)|(1<<TWCMD0));
              }
            else
              {            // Stop Condition
                TWSCRB = (1<<TWCMD1);
              }
          }
      }
  }

void init_ADC()
  {
    ADMUXB &=~((1<<REFS2)|(1<<REFS1)|(1<<REFS0));    // Register ADMUXB
    // REF-Auswahl Referenzspannung, auf Vcc(5V)
    ADCSRA |=((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0));    // Register ADCSRA
    // ADPS - Teilungsfaktor 16MHz/128=125kHz
    ADCSRA |= (1<<ADEN);                            // ADC aktivieren
    ADCSRA |= (1<<ADSC);                            // eine ADC-Wandlung "single conversion"
    while (ADCSRA & (1<<ADSC))
      {                                                // auf Abschluss der Konvertierung warten
      }
  }

uint16_t ADC_lesen1()                        // ADC Einzelmessung
  {
    ADMUXA |=(1<<MUX1);                    // Register ADMUXA
    // MUX - Auswahl welcher Eingang - 000010 - ADC2
    ADCSRA |= (1<<ADSC);                // Register ADCSRA
    // ADSC - Start Konvertierung "single conversion"
    ADCSRB |=(1<<ADLAR);                // bei 8 Bit links
    while (ADCSRA & (1<<ADSC))
    {                                    // auf Abschluss der Konvertierung warten
    }
    //return ADCW;                        // ADC auslesen und zurückgeben bei 10 Bit ohne Adlar
    return ADCH;                        // ADC auslesen und mit 8 Bit zurück mit ADLAR=1
  }
  
  uint16_t ADC_lesen2()                    // ADC Einzelmessung
    {
      ADMUXA |=(1<<MUX1);                // Register ADMUXA
      ADCSRA |= (1<<ADSC);                // Register ADCSRA
      ADCSRB |=(1<<ADLAR);                // bei 8 Bit links
      while (ADCSRA & (1<<ADSC))
        {                                // auf Abschluss der Konvertierung warten
        }
      return ADCH;                        // ADC auslesen und mit 8 Bit zurück mit ADLAR=1
    }
  
uint8_t ADC_lesen_avg(uint8_t anzahlmessung)
  {    
    uint8_t summemessung=0;
    for (uint8_t i = 0; i < anzahlmessung; ++i )    
      {
          summemessung += ADC_lesen1();    
      }
    return ( summemessung/anzahlmessung );        // Durchschnitt errechnen und zurück
  }
  
int main(void)
  {
    init_I2C();  
    init_ADC();
    while(1)
      { 
        wert1= ADC_lesen1();    
        //wert2= ADC_lesen2();    
        //wert1= ADC_lesen_avg(5);
        if(command != 0x00)
          {
            switch(command)
              {
                case 0x01:
                // Test Daten
                tx_buf[0] = wert1;
                tx_buf[1] =    6            //wert2;
                tx_buf[2] = 3;
                tx_buf[3] = 14;
                tx_buf_index = 0;
                break;
              }
            command = 0x00;
          }
      }
  }

Da der Attiny 841 leider etwas anders ist als die anderen Ati, ist der Code auch anders. Ein Vergleich mit den anderen ist nicht möglich. Alles was ich aus dem Datenblatt lesen konnte habe ich als Kommentar eingefügt. Es muss eine Freigane erfolgen wenn das ADC fertig ist. Bis dahin muss SCL auf GND bleiben. Das ist der gesamte Code vom Slave.
Hatte ja gehoft was dazu im Netz zu finden, leider ohne Erfolg.
Vielleicht kennst du was dazu.
achim
 
Hallo achim S.

Die Lösung Deines Problems wurde Dir schon vor Tagen genannt: siehe Re: ADC Messung mit dem Attiny 841

mfg

Hero_123

NB - warum
- uint16_t ADC_lesen1() UND uint16_t ADC_lesen2() - sind beide die gleiche Funktion
- uint8_t ADC_lesen_avg(uint8_t anzahlmessung) und uint16_t ADC_lesen1() - das solltest Du Dir nochmal GENAU ansehen - siehe auch den o.a. link - da wurdest Du auch schon auf die Problematik hingewiesen
 
Zuletzt bearbeitet:
Ja das stimmt. Bleibt allerdings die Frage was dabei eine Lösung ist. Es wurde wiederholt gesagt das alles notwendige Daten dazu im Datenblatt steht, was ich bezweifle. Der Hersteller hat darauf verwiesen und der Rest dazu steht in den Registern drin. Leider ist mir die Antwort im DB dazu nicht klar. Wenn ich im Netz dazu suche finde ich hunderte Fragen mit gleichem Inhalt und keinerlei Antwort. Es wird darauf verwiesen das der Attiny 20, 40 und 1632(???) gleich bzw. ähnlich sein soll. Es gibt vom Hersteller verschiedene Veröffentlichungen dazu. Das hilft aber kaum weiter. In den Beschreibungen oder bei WIKI steht dann was zu SCL verzögern, aber alles ohne nähere Erklärung. Leider bin ich Anfänger und erarbeite mir alles dazu. Das Datenbltt habe ich mir die Stellen übersetzt in der Hoffnung es in deutsch besser zu verstehen. Leider halten sich selbst die Hersteller und Entwickler des Busses relativ zurück mit Infos.
Du meinst diese Stelle von Stefan:

Zur Erinnerung: Das ist die Zeile wo du das ACK sendest:
> TWSCRB = (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0));

Dabei ist
TWSCRB - TWI-Slave-Steuerregister B
und zu TWCMD1 und TWCMD0 steht

Bits 1:0 - TWCMD[1:0]: TWI-Befehl
Das Schreiben dieser Bits löst die Slave-Operation gemäß Tabelle 20-2 aus. Die Art der Operation ist abhängig von den TWI-Slave-Interrupt-Flags, TWDIF und TWASIF. Die Acknowledge-Aktion wird nur ausgeführt, wenn der Slave Datenbytes oder Adressbytes vom Master empfängt.

Leider bekomme ich die Anwendung dazu nicht hin.
achim
 
Da der Attiny 841 leider etwas anders ist als die anderen
Wenn alle gleich ausgestattet wären, gäbe es ja nur einen, oder?!

Meine Tiny-Übersicht kennst Du?

Die ganz klassischen alten Tinies hatten gar kein TWI (natürlich kann man das mit entsprechendem Aufwand (Programmieraufwand einerseits, aber zu bedenken ist je nach anderen Aufgaben des Controllers auch der Laufzeit-Aufwand) auch in Software).

Der große Vorteil einiger/vieler modernerer Tinies (wobei die inzwischen auch schon steinalt sind) war eine Hardware-Schnittstelle, die eine Implementierung von TWI oder SPI unterstützt hat. Das USI. Salopp gesagt einfach ein mehr oder weniger automatisierbares getaktetes Schieberegister, welches an externe Pins gekoppelt werden kann. Bei der reinen Softwarelösung mußte man ja jedes Bit einzeln einlesen/schreiben, durch das Schieberegister braucht man sich nur noch um ganze Bytes kümmern (abgesehen vom Adressierungs-Mechanismus).

Dann gibt es Tinies, die eine eigene TWI-Hardware mitbringen, aber nur auf den slave-Betrieb ausgerichtet sind. Slave-TWI. Vorteil gegenüber USI ist z.B., daß der Adress-Match durch die Hardware miterledigt wird. Wie der Name vermuten läßt, läßt sich dieses Interface nicht als Master nutzen (was beim USI mit entsprechendem Aufwand möglich ist).

Mir ist ein(!) Tiny mit echtem Master/Slave-TWI bekannt, dieses sollte dem der klassischen ATmega entsprechen.

Und zum Schluß gibt es noch die Gruppe der X-Core-ATtinies, die wiederum ein eigenes Master/Slave-TWI-Modul besitzen (welches gewisse Ähnlichkeiten zu den ATXmega haben sollte). Quasi ein X-Core-Full-TWI.



Natürlich ist meine Übersicht bei weitem nicht fertig, aber zumindest was USI betrifft, sind alle bereits eingetragen.
Da ich auf lange Sicht eher nicht damit weiterkommen werde, hier trotzdem ein paar Spoiler für Dich (was die noch offenen Tinies betrifft):
  • ATtiny28 - besitzt gar keine derartige Schnittstelle. (Bei seiner Ausstattung denkt man automatisch an IR-Fernbedienungen.)
  • ATtiny87/167 (LIN-Spezialist) und ATtiny43u (der mit dem eingebauten Boost-Converter, kann quasi mit einer Batterie betrieben werden) - besitzen ein USI.
  • ATtiny48/88 - besitzt (als einziger non-X-core-Tiny) ein echtes Full-Master/Slave-TWI.
  • ATtiny20, ATtiny40, ATtiny441/841, ATtiny1634 und ATtiny828 - besitzen das Slave-TWI, welches Dir Probleme bereitet.
Wie gesagt, die X-Cores ganz außen vor gelassen.
 
Zuletzt bearbeitet:
Habe weiter das Datenblattgelesen und dabei die Tabelle 20-2 gefunden mit den Angaben zu TWCMD1 und 0.

TWCMD[1:0] TWDIR Betrieb/Operation

00 X Keine Aktion

01 X Reserviert

10 Wird verwendet, um die Transaktion abzuschließen

0 Acknowledge-Aktion ausführen, dann auf eine START (S/Sr)-Bedingung warten

  • Warten auf eine beliebige START (S/Sr)-Bedingung
11 Wird als Antwort auf ein Adressbyte verwendet (TWASIF ist gesetzt)

0 Acknowledge-Aktion ausführen, dann nächstes Byte empfangen

1 Acknowledge-Aktion ausführen, dann TWDIF setzenVerwendet als Antwort auf ein Datenbyte (TWDIF ist gesetzt)

0 Acknowledge-Aktion ausführen, dann auf nächstes Byte warten

1 Keine Aktion

So wie es aussieht erfolgt keine korrekte Darstellung. Ist schade.
Dort sind die Einstellungen zu TWCMD angegeben. Es sind noch mehr Register da angegeben. Leider komme ich mit der Einstellung nicht klar.
Das mit den 16 But werde ich kontrollieren und ändern.
achim
 
Im ori wird der ADC mit 10 Bit gelesen. Da ich aber nur 8 Bit übertragen kann verwende nur das untere Byt mit 8 Bit. Daher steht auch 16 Bit drin
 
Im ori wird der ADC mit 10 Bit gelesen. Da ich aber nur 8 Bit übertragen kann verwende nur das untere Byt mit 8 Bit. Daher steht auch 16 Bit drin
Na, wenn das nicht logisch klingt :pleasantry:

Grundgütiger ...
 
Im ori wird der ADC mit 10 Bit gelesen. Da ich aber nur 8 Bit übertragen kann verwende nur das untere Byt mit 8 Bit. Daher steht auch 16 Bit drin

Du kannst auch die 10Bit des ADC auswerten - sende zuerst das HIGH Byte, dann das LOW Byte zum Master und da setzt Du die beiden Bytes wieder zusammen - mache ich auch so bei der Übertragung per SPI oder TWI .
 
Wenn ich das richtig verstanden habe, kann er zwar ein Byte korrekt übertragen, zweie hingegen (aus welchem Grund auch immer) nicht. Allerdings hab ich die anderen Themen im anderen Forum auch nicht komplett gelesen...

Hier gings ja ursprünglich um clock stretching (was die Slaves ja automatisch machen und ein Master laut Spezifikation unterstützen muß).

Meiner Meinung nach sollte Achim erstmal, ne Testroutine schreiben, die sicher längere Telegramm überträgt, und sich dann auf ein Protokoll festlegen, daß für seine Aufgabenstellung geeignet ist (was ihm ja freisteht).

Denkbar ist zB, daß er sowas ähnliches wie ein Scratchpad implementiert, welches der Master ausliest, oder sowas wie ein adressierbares Feld, wobei der Master die Adresse vorgibt.
Kann auch mit nem Statusbyte ergänzt werden - Master adressiert den Slave, dieser liefert über den Status was es neues gibt, Master fordert, was er konkret wissen will blablub...

Aber zuerst muß die StateMachine im Slave konform arbeiten...
 
Mal was zum Code:
Ist in Zeile 137 nicht n C-typischer Semikolon-Syntaxerror?

Teilweise werden bei Vergleichen und Zuweisungen Compilerkonstanten verwendet (was die Lesbarkeit erleichtert), aber nicht immer. Das ist inkonsequent.

Speziell beim Test auf Daten oder Adresse definierst Du extra ne Status Variable (wo nur die obersten Bits sichtbar sind), die anderen Bits desselben Register prüfst Du hingegen direkt.
Wären die Bits direkt adressierbar (bis Adresse 0x1F), hätte das Performance-Vorteile. Da die TWI-Register nicht mal im konventionellen I/O-Space liegen, werden sie eh wie SRAM behandelt. Unter ASM würde ich das TWSSRA also einmal einlesen, und für alle Abfragen im Rechenregister bereithalten. Ob C das auch so optimiert, weiß ich nicht. BASCOM ist auch bei wiederholten Zugriffen/Manipulationen auf dieselbe Speicherzelle stur. Jeder Zwischenschritt wird aus dem Speicher geladen und wieder zurückgeschrieben.

Ist TWSHE nicht eigentlich nur für den SMBUS relevant?

Achso, Du sendest nie ein NACK?
 
Das in Zeile 137 stimmt. Hatte da erst den Wert2 drin und um zu sehen wie die Werte sich ändern wieder einen festen wert eingegeben. Beim nächsten Lauf hat der Kompiler gemeckert und habe es korregiert. Den Rest muss ich erst lesen.
achim
 
Das in Zeile 137 stimmt. Hatte da erst den Wert2 drin und um zu sehen wie die Werte sich ändern wieder einen festen wert eingegeben. Beim nächsten Lauf hat der Kompiler gemeckert und habe es korregiert. Den Rest muss ich erst lesen.
achim
Oh Mann! Versuche mal, das, was Du HIER (!!) reingestellt hast, zu kompilieren :eek:
 
Zuletzt bearbeitet:
Sorry, wo drin besteht das Problem? Der einzigste angezeigte Fehler war das fehlende Semikolon. Alles andere funktioniert und lässt sich ohne jeden Fehler kompilieren.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)