I²C mit USI realisieren

Dieses Thema im Forum "Hardware" wurde erstellt von LotadaC, 4. Dezember 2018.

Schlagworte:
  1. LotadaC

    LotadaC Sehr aktives Mitglied

    Registriert seit:
    22. Januar 2009
    Beiträge:
    3.009
    Zustimmungen:
    51
    Punkte für Erfolge:
    48
    Sprachen:
    BascomAVR, Assembler
    In diesem Thread soll es um die Realisierung eines I²C-Busses durch das USI, welches es in einigen ATtinies (und wohl auch wenigen ATmegas) gibt, gehen.

    Vorweg erstmal nötige Fakten über I²C selbst. (Wobei ich nur an der Oberfläche bleiben werde - für den tieferen Einstieg wird eine Suche im Internet sicher Resultate erbringen. Hervorheben möchte ich zumindest die I²C-Spezifikation von NXP).

    • zwei Signale (Leitungen) - SCL=Serial Clock, SDA=Serial Data
    • beide Signale sind dominant-rezessiv (im freigegebenen (idle) Zustand ziehen externe Pull-Widerstände die Signale auf high (rezessiv)
    • jeder Teilnehmer kann(!) sie auf Gnd legen (dominant)
    • den freien Bus darf(!) ein Teilnehmer belegen, indem er eine Startcondition generiert. Dadurch wird er zum Master (bis er selbst den Bus durch eine Stop Condition wieder freigibt, oder die Arbitrierung an einen anderen Master verliert).
    • Nur der (ein) Master darf die SCL low ziehen - jeder Teilnehmer darf SCL dann weiter low halten (Clock-Stretching).
    Code:
        _                 _
    SDA  \               /
          ¯¯¯¯¯¯'''¯¯¯¯¯¯
        _____         _____
    SCL      \       /  
              ¯¯'''¯¯  
         ^   ^       ^   ^
         |   |       |   |
        (1) (2)     (3) (4)
    
    • (1) Teilnehmer zieht SDA low, während SCL (und SDA) high sind, belegt dadurch den Bus, wird zum Master (->Start Condition)
    • (2) Master zieht SCL low.
    • (3) Master gibt SCL frei (geht über Pullup high).
    • (4) Master gibt SDA frei, während SCL high ist (-> Stop Condition)


    • Ein leeres Telegramm (nur Start und Stop) ist nicht erlaubt.
    • Nach einer (Repeated) Start Condition ist immer der Master Transmitter, alle anderen Teilnehmer Receiver.
    • Der Master generiert neun Taktimpulse auf SCL, während der ersten acht wird ein Byte (beginnend mit dem MSB) vom Transmitter an den Receiver übertragen. (D.h. der Transmitter legt vor der steigenden SCL-Flanke (bzw direkt nach der vorherigen fallenden Flanke) den entsprechenden "Pegel" auf SDA, nach der steigenden SCL-Flanke liest der Receiver das Bit ein.
      Beim neunten Takt überträgt der Receiver ein ACK(=low) / NACK(=high) zum Transmitter. (D.h. nach der fallenden SCL-Flanke des achten Bits gibt der Transmitter SDA frei, und der Receiver legt den entsprechenden Pegel auf SDA.) Nach der fallenden Flanke des neunten Bits gibt der Receiver SDA dann wieder frei.
    • Ggf. werden weitere Bytes (genauso) übertragen.
    • Der Master beendet das Telegramm mit einer Stop Condition
    Code:
        _       _____ _____ _____ _____ _____ _____ _____ _____ _____      _
    SDA  \     / MSB X  7  X  6  X  5  X  4  X  3  X  2  X LSB X ACK \    /
          ¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯
        ____     __    __    __    __    __    __    __    __    __     ____
    SCL     \   /  \  /  \  /  \  /  \  /  \  /  \  /  \  /  \  /  \   /       
             ¯¯¯    ¯¯    ¯¯    ¯¯    ¯¯    ¯¯    ¯¯    ¯¯    ¯¯    ¯¯¯
       (Start)^  ^                                       ^  ^  ^  ^  ^  (Stop)         
              |  |                                       |  |  |  |  |           
             (1)(2)                    . . .            (3)(4)(5)(6)(7) 
    
    • (1) Fallende SCL-Flanke -> Transmitter (derzeit der Master) legt sein MSB auf SDA.
    • (2) Steigende SCL-Flanke -> alle Receiver lesen SDA ein.
    . . .
    • (3) Fallende SCL-Flanke -> Transmitter legt sein LSB auf SDA.
    • (4) Steigende SCL-Flanke -> Receiver lesen SDA ein.
    • (5) Fallende SCL-Flanke -> Transmitter gibt SDA frei, (adressierter) Receiver legt ACK/NACK auf SDA.
    • (6) Steigende SCL-Flanke -> Transmitter liest SDA (ACK/NACK) ein.
    • (7) Fallende SCL-Flanke -> Receiver gibt SDA frei.
    Im dargestellten Schema zieht der Master nach (7) SDA low, um die folgende Stop-Condition vorzubereiten (steigende SDA während SCL high ist, also muß erstmal SDA low gezogen werden,während SCL low ist). (*) Stattdessen könnten aber auch weitere Bytes übertragen, oder eine Repeated-Start-Condition erzeugt werden.

    Grundsätzlich ist nach einer (Repeated-) Start-Condition der Master Transmitter. Das erste übertragene Byte besteht aus der Receiver-Adresse (7bit, auf 10bit-Adressierung gehe ich hier nicht ein), das LSB bestimmt die Datenrichtung für den Rest des Telegrammes (also bis zum nächsten Stop oder Start). Low=Master bleibt Transmitter, High=Slave wird Transmitter.
    Code:
        _       _____ _____ _____ _____ _____ _____ _____       _____      _
    SDA  \     /ADDR7XADDR6XADDR5XADDR4XADDR3XADDR2XADDR1\ R/W / ACK \    /
          ¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        ____     __    __    __    __    __    __    __    __    __     ____
    SCL     \   /  \  /  \  /  \  /  \  /  \  /  \  /  \  /  \  /  \   /       
             ¯¯¯    ¯¯    ¯¯    ¯¯    ¯¯    ¯¯    ¯¯    ¯¯    ¯¯    ¯¯¯
    
    (*) Wann kann der Master überhaupt ein Repeated Start oder ein Stop generieren? Theoretisch immer dann, wenn der Slave SDA vor der steigenden SCL-Flanke nicht Low hält.
    Insbesondere also als Transmitter (ausserhalb des ACK/NACK-Slots).
    Als Receiver, wenn der Slave 'n High vorbereitet hat, oder innerhalb seines ACK/NACK-Slots.

    Aber wann darf er ein Repeated Start/Stop generieren?

    Ok, soweit erstmal der Überblick über den eigentlichen I²C-Bus.
    Im nächsten Beitrag würde ich dann erstmal nur auf die USI-Hardware eingehen wollen.

    Also bitte hier vorerst keine Kommentare anhängen - etwaige Hinweise und Korrekturen etc... bitte als persönliche Nachricht;)
     
    #1 LotadaC, 4. Dezember 2018
    Zuletzt bearbeitet: 4. Dezember 2018
    TommyB, Hero_123 und Dirk gefällt das.
  2. LotadaC

    LotadaC Sehr aktives Mitglied

    Registriert seit:
    22. Januar 2009
    Beiträge:
    3.009
    Zustimmungen:
    51
    Punkte für Erfolge:
    48
    Sprachen:
    BascomAVR, Assembler
    Wie bereits angedeutet, soll's in diesem Beitrag um die Hardware, das eigentliche USI-Modul gehen. Repräsentativ hier mal das entsprechende Diagramm aus dem Datenblatt des ATtiny261A/462A/861A (die mir bekannten Tinies sind weitgehend identisch).
    USI.png
    Wie immer kann die Software mit der Hardware über so genannte I/O-Register kommunizieren - hier sind das vier:
    • USIDR - das USI Data Register, also das eigentliche Schieberegister.
    • USIBR - das USI Buffer Register, ein komplett ins USIDR eingetaktetes Byte (stimmt so nicht, geh ich noch genauer drauf ein) wird in diesen Puffer kopiert, und kann "später" ausgelesen, während das USIDR bereits weitergeschoben wurde.
    • USISR - das USI Status Register, es enthält einen 4-bit Counter (unteres Nibble) sowie vier Status-Flags (oberes Nibble)
    • USICR - das USI Control Register, hier wird die Hardware quasi eingestellt/kontrolliert.
    Das USIDR ist also ein Schieberegister - sein Eingang (LSB) ist immer mit dem DI/SDA-Pin verbunden. Der Ausgang (MSB) hingegen kann nach einem transparenten Latch wahlweise auf DO (entspräche dem 3-wire-Mode) oder DI/SDA (=2-wire-Mode) geschaltet werden.( Der "Schalter" wird übrigens über das USIWM1-Bit (USI Wire Mode) im USICR angesteuert - ist das Bit gesetzt wird auf SDA geschaltet).
    Hier geht's um den 2-wire-Mode...

    Am Takt-Eingang des Schieberegisters befindet sich ein Clock-Source-Multiplexer (der die Quelle der Schiebe-Impulse festlegt). Dieser Multiplexer wird über die beiden USICS-Bits (USI Clock Source Select Bits) im USICR eingestellt.
    Die vier möglichen Quellen wären:
    • das USICLK-Bit in USICR (das Bit wirkt als Clock-Strobe, schreibt man 'ne "1" rein, wird einmal geschoben)
    • ein Compare-Match von Timer0 (wobei unklar ist, welches bzw beide)
    • eine steigende Flanke am USCK/SCL-Pin
    • eine fallende Flanke am USCK/SCL-Pin
    Ein weitgehend identischer Multiplexer speist den 4-bit-Counter, der verfügt zusätzlich über einen Flankendetektor - bei USICS[1..0]=1x bin (also gesetztem USICS1) inkrementiert also jede USCK-Flanke den Counter.

    Auf den USCK/SCL wirkt sich außerdem das Clock-Hold-Unit aus, und zwar nur im 2-wire-Mode. Also wenn USIWM1 in USICR gesetzt wird. Nach jeder, erkannten Startcondition (fallende SDA-Flanke während SCL high) wird nach der nächsten fallenden SCL-Flanke SCL weiterhin auf Low gehalten -> Start Condition SCL Hold. Ist außerdem noch USIWM0 gesetzt, wird auch nach einem Überlauf des 4-bit-Counters gestrecht (bzw nach der darauf folgenden fallenden SCL-Flanke) ->Counter Overflow SCL Hold.

    Ok, was fehlt noch?
    Im USISR gab es (bereits angedeutet, im oberen Nibble) vier Flags:
    • USISIF - USI Start Condition Interrupt Flag - im 2-wire-Mode setzt eine erkannte Start Detection dieses Flag. Dabei wird gleichzeitig die oben erwähnte Start Condition SCL Hold ausgelöst, und zwar bis das Bit manuell wieder gelöscht wird. USISIF kann einen IRQ triggern.
    • USIOIF - USI Counter Overflow Interrupt Flag - wird bei einem Überlauf des 4-bit-Counters gesetzt. Löst im 2-wire-Mode das oben erwähnte Counter Overflow SCL Hold aus bis USIOIF gelöscht wird. Kann einen IRQ triggern.
    • USIPF - USI Stop Condition Flag - wird (im 2-wire-mode) bei einer erkannten Stop-Condition gesetzt (hier gibt's keinen IRQ)
    • USIDC - USI Data Output Collision - ist (im 2-wire-mode) gesetzt, genau dann wenn MSB des Schieberegisters und tatsächlicher Zustand des SDA-Pins unterschiedlich sind.
    Für USISIF und USIOIF gibt's dann im USISR entsprechende Interrupt Enable Bits (USISIE und USIOIE).

    Zum USIDB schrieb ich anfangs, daß das USIDR "nach einem komplett eingetakteten Byte" ins USIDB kopiert wird, und daß das nicht ganz korrekt wäre. Um das "komplett eingetaktet" muß man sich nämlich selbst kümmern - die Kopie wird erzeugt, wenn der 4-Bit-Counter überläuft. Der muß also so eingestellt werden, daß er nach dem LSB überläuft - dort muß aber eh interagiert werden... Ein Transmitter muß hier SDA freigeben damit der Receiver ACK/NACK drauflegen kann, und der Receiver muß genau das tun...

    Im nächsten Teil werde ich darauf eingehen, wie die normalen Portfunktionen durch USI, genauer den Two Wire Mode, außer Kraft gesetzt werden.

    P.S. auch hier noch:
     
  • Über uns

    Unsere immer weiter wachsende Community beschäftigt sich mit Themenbereichen rund um Mikrocontroller- und Kleinstrechnersysteme. Neben den Themen Design von Schaltungen, Layout und Software, beschäftigen wir uns auch mit der herkömmlichen Elektrotechnik.

    Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  • Coffee Time

    Unser makerconnect-Team arbeitet hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und unser eigener makerconnekt-Server regelmäßig gewartet wird. Wir nehmen das Thema Datensicherung und Datenschutz sehr ernst und sind hier sehr aktiv, auch sorgen wir uns darum, dass alles Drumherum stimmt!

    Dir gefällt das Forum und die Arbeit unseres Teams und du möchtest es unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft, unser Team freut sich auch über eine Spende für die Kaffeekasse :-)
    Vielen Dank!
    Dein makerconnect-Team

    Spende uns! (Paypal)
  1. Diese Seite verwendet Cookies, um Inhalte zu personalisieren und die Seite optimal für dich anzupassen. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies.
    Information ausblenden