Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
microcontrollertechnik:11_i2c_schnittstelle [2021/06/23 11:05]
tfischer
microcontrollertechnik:11_i2c_schnittstelle [2024/03/11 00:03] (aktuell)
mexleadmin
Zeile 1: Zeile 1:
-====== 10. I2C Schnittstelle ======+====== 11 I2C Schnittstelle ======
 <WRAP group>  <WRAP group> 
 <WRAP column 30%> <WRAP column 30%>
Zeile 9: Zeile 9:
   - wissen wie die Kommunikation zwischen I2C Master und Slave funktioniert   - wissen wie die Kommunikation zwischen I2C Master und Slave funktioniert
  
 +Im Video wird eine Library für die Kommunikation verwendet. Wir werden in untenstehenden Beispiel die Register selbst schreiben.
  
 </WRAP> <WRAP column 65%> </WRAP> <WRAP column 65%>
 ==== Video ==== ==== Video ====
    
 +{{youtube>evTYfGX8cgQ?size=700x400}}
 +
 +<WRAP hide>
 {{youtube>7aZ43cl7o_w?size=700x400}} {{youtube>7aZ43cl7o_w?size=700x400}}
 +</WRAP>
  
 </WRAP> </WRAP> </WRAP> </WRAP>
- 
- 
-==== Dokumentation von Atmel ==== 
- 
-  * [[https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en591794|Application Note: TWI Module as I2C Master]] 
-  * [[https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en591792|Application Note: TWI Module as I2C Slave]] 
-  * alternative Implementierung von [[https://github.com/knightshrub/I2C-master-lib|Elia Ritterbusch]] 
- 
-\\ 
- 
-==== Übersicht über die am häufigsten verwendeten, seriellen Schnittstellen ==== 
- 
-<WRAP group><WRAP column third> 
-=== USART === 
-<WRAP> 
-<imgcaption BildNr0 | Konzeptbild U(S)ART> 
-</imgcaption> 
-{{drawio>KonzeptbildUsart}} 
-</WRAP> 
-  * Keine gibt Takt vor. \\ Es sind gleichberechtigte Kommunikationspartner (siehe <imgref BildNr0>). 
-  * Jeder darf zu jederzeit senden. 
-  * Senden und Empfangen geschieht über zwei separate Leitungen. 
-  * Kommunikation ist nur zwischen zwei Geräten möglich. \\ Ein weiterer Slave würde eine weiteren U(S)ART-Bus benötigen. 
- 
-</WRAP><WRAP column third> 
-=== I2C === 
-<WRAP> 
-<imgcaption BildNr1 | Konzeptbild I2C> 
-</imgcaption> 
-{{drawio>KonzeptbildI2c}} 
-</WRAP> 
-  * Master gibt Takt vor (siehe <imgref BildNr1>). 
-  * Slave darf nur zu bestimmten Zeiten senden und nur, wenn der Master dies anfordert. 
-  * Senden und Empfangen geschieht über die gleiche Leitung. 
-  * Alle Slaves hören am gleichen Bus mit und schreiben auf die gleiche Leitung.  
-  * Jeder Slave muss anhand der Signale überprüfen, ob die Daten für ihn gemeint sind. 
- 
-</WRAP><WRAP column third> 
-=== SPI === 
-<WRAP> 
-<imgcaption BildNr2 | Konzeptbild SPI> 
-</imgcaption> 
-{{drawio>KonzeptbildSpi}} 
-</WRAP> 
-  * Master gibt Takt vor (siehe <imgref BildNr2>). 
-  * Slave darf nur zu bestimmten Zeiten senden und nur, wenn der Master dies anfordert. 
-  * Senden und Empfangen geschieht über zwei separate Leitungen. 
-  * Alle Slaves hören auf der gleichen Leitung mit und schreiben auf die gleiche Leitung.  
-  * Der gewünschte Slave wird über die __S__lave __S__elect Leitung ausgewählt. 
- 
-</WRAP></WRAP> 
- 
- 
-<WRAP> 
-<imgcaption BildNr09 | Zusammenspiel der TWI-Register> 
-</imgcaption> 
-{{drawio>TWIRegister}} 
-</WRAP> 
  
  
Zeile 83: Zeile 30:
 <imgcaption BildNr3 | Statemachine der I2C Kommunikation > <imgcaption BildNr3 | Statemachine der I2C Kommunikation >
 </imgcaption> </imgcaption>
-{{drawio>StatemachinederI2CKommunikation}}+{{drawio>StatemachinederI2CKommunikation.svg}}
 </WRAP> </WRAP>
  
Zeile 90: Zeile 37:
 ==== I2C in Kürze und Zeitverlaufsdiagramm ==== ==== I2C in Kürze und Zeitverlaufsdiagramm ====
  
-**Übertragung**\\+<WRAP> 
 +<imgcaption BildNr09 | Zusammenspiel der TWI-Register> 
 +</imgcaption> 
 +{{drawio>TWIRegister.svg}} 
 +</WRAP> 
 + 
 +=== Übertragung === 
 Für die I2C Übertragung "trommelt" der Master-IC auf der Taktleitung (SCL). Bei jedem "Trommelschlag" (SCL=High), darf der Slave die Datenleitung (SDA) lesen. \\ Für die I2C Übertragung "trommelt" der Master-IC auf der Taktleitung (SCL). Bei jedem "Trommelschlag" (SCL=High), darf der Slave die Datenleitung (SDA) lesen. \\
 D.h. während der Datenübertragung bleibt die Datenleitung bei SCL=High konstant. \\ D.h. während der Datenübertragung bleibt die Datenleitung bei SCL=High konstant. \\
Zeile 100: Zeile 53:
 <imgcaption BildNr4 | Zeitverlaufsdiagramm der I2C Kommunikation > <imgcaption BildNr4 | Zeitverlaufsdiagramm der I2C Kommunikation >
 </imgcaption> </imgcaption>
-{{drawio>ZeitverlaufsdiagrammderI2CKommunikation}}+{{drawio>ZeitverlaufsdiagrammderI2CKommunikation.svg}}
 </WRAP> </WRAP>
  
Zeile 112: Zeile 65:
 <imgcaption BildNr5 | Startbedingung> <imgcaption BildNr5 | Startbedingung>
 </imgcaption> </imgcaption>
-{{drawio>Startbedingung}}+{{drawio>Startbedingung.svg}}
 </WRAP> </WRAP>
  
Zeile 125: Zeile 78:
 TWCR = (1<<TWINT)|(1<<TWEN);       // Setting TWINT clears interupt flag TWCR = (1<<TWINT)|(1<<TWEN);       // Setting TWINT clears interupt flag
                                    // to set the following state:                                    // to set the following state:
-     | (1<<TWIE )                  // Enable TWI Interrupt.+     | (1<<TWIE )                  // Enable TWI Interrupt. --> nur wichtig, falls der Interrupt geprüft wird!
      | (1<<TWSTA)|(0<<TWSTO);      // Initiate a START condition.           | (1<<TWSTA)|(0<<TWSTO);      // Initiate a START condition.     
  
Zeile 142: Zeile 95:
 <imgcaption BildNr6 | Übertragung> <imgcaption BildNr6 | Übertragung>
 </imgcaption> </imgcaption>
-{{drawio>Uebertragung}}+{{drawio>Uebertragung.svg}}
 </WRAP> </WRAP>
  
Zeile 169: Zeile 122:
 </sxh> </sxh>
 </WRAP> </WRAP>
- 
- 
  
 </WRAP></WRAP> </WRAP></WRAP>
Zeile 181: Zeile 132:
 <imgcaption BildNr7 | Stoppbedingung > <imgcaption BildNr7 | Stoppbedingung >
 </imgcaption> </imgcaption>
-{{drawio>Stoppbedingung }}+{{drawio>Stoppbedingung.svg}}
 </WRAP> </WRAP>
  
Zeile 194: Zeile 145:
 TWCR = (1<<TWINT)|(1<<TWEN);       // Setting TWINT clears interupt flag TWCR = (1<<TWINT)|(1<<TWEN);       // Setting TWINT clears interupt flag
                                    // to set the following state:                                    // to set the following state:
-     | (1<<TWIE )                  // Enable TWI Interrupt.+     | (1<<TWIE )                  // Enable TWI Interrupt. --> nur wichtig, falls der Interrupt geprüft wird!
      | (0<<TWSTA)|(1<<TWSTO);      // Initiate a STOP condition.           | (0<<TWSTA)|(1<<TWSTO);      // Initiate a STOP condition.     
 </sxh> </sxh>
Zeile 204: Zeile 155:
 ===== Software ===== ===== Software =====
  
-<WRAP+==== einfache Anwendung - nur Polling ==== 
-<sxh c; first-line: 1>+ 
 +Im ersten Schritt ist im folgenden eine einfache Anwendung dargestellt. \\ 
 +Bei dieser werden Schalterstellungen vom Master an den Slave übertragen.  
 + 
 +--> I. Vorarbeiten 
 + 
 +Laden Sie die Datei {{microcontrollertechnik:simple_i2c.zip}} herunter und entpacken Sie diese. \\ 
 +In ihr finden Sie die Simulation als ''simple_I2C.sim1'' und das Microchip Solution File ''simple_I2C.atsln''
 + 
 +<-- 
 + 
 +--II. Analyse des fertigen Programms # 
 +  - Initialisieren des Programms  
 +    - Öffnen Sie SimulIDE und öffnen Sie dort mittels {{microcontrollertechnik:simulide_open.jpg?25}} die Datei ''simple_I2C.sim1''  
 +    - In der Simulation finden Sie unten links 8 Dip-Schalter, welche durch Klick auf eine der acht kleinen Quadrate aktiviert (Wechsel auf grün)bzw deaktiviert werden kann. 
 +    - Die hex Files sollten bereits in der simulation verlinkt sein, sodass diese nach dem Start auch lauffähig sei soll. \\ Falls nicht finden Sie die hex Files unter ''..\simple_I2C_Master\Debug'' bzw ''..\simple_I2C_Slave\Debug''
 +    - Wenn Sie die Simulation starten, sehen Sie oben rechts im Logic Analyzer den Verlauf der Signale auf SDA (Daten) und SCL (Clock, also Takt). 
 +    - Sobald die Stopp-Bedingung erfüllt ist (positive Flanke auf SDA, wenn SCL bereits High ist), wird die Simulation automatisch gestoppt. \\ Dies geschieht, da im Logic Analyzer als Trigger ''Ch1R & Ch2H''eingetragen wurde. 
 +  - Sie können hier einige Dinge analysieren: 
 +    - Was passiert in den I2C Registern (''TWAR'', ''TWBR'', ''TWCR'', ''TWDR'', ''TWSR'') auf beiden Seiten?  
 +    - Was wird übertragen? 
 +  - Das Programm zu diesem Hexfile soll nun erstellt werden 
 + 
 +<-- 
 + 
 +--> III. Code in Microchip Studio # 
 + 
 +<fs x-large>**I2C Master**</fs> 
 + 
 +<WRAP group><WRAP column 40%><sxh c; first-line: 1>
 /* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
- +  
  Experiment 10:   I2C Kommunikation  Experiment 10:   I2C Kommunikation
  =============    ===============================  =============    ===============================
-  
- Dateiname : I2C_SimpleMaster.c 
      
- Autoren    : Tim Fischer       (Hochschule Heilbronn, Fakultaet T1+ Dateiname : I2C_SimpleMaster.c 
-                +    
- Datum      23.06.2021+ Autoren : Tim Fischer       (Hochschule Heilbronn, Fakultaet TE
 +                 
 + Datum : 18.11.2023 
 +    
 + Version : 1.1 
 +    
 + Hardware Simulide 1.0.0 >R810
      
- Version    1.0+ Software Entwicklungsumgebung: Microchip Studio 7.0 
 +   C-Compiler: AVR/GNU C Compiler 5.4.0
      
- Hardware:  Simulide 0.5.16-RC5 + Funktion einfacher I2C Master, der Schalterwerte überträgt
-  +
- Software:  Entwicklungsumgebung: AtmelStudio 7.0 +
-            C-Compiler: AVR/GNU C Compiler 5.4.0 +
-  +
- Funktion : TBD +
-  +
- Displayanzeige:    TBD +
-  +
- Tastenfunktion:    keine +
-  +
- Jumperstellung:    keine +
-  +
- Fuses im uC:       CKDIV8: Aus (keine generelle Vorteilung des Takts) +
-  +
- Header-Files:  lcd_lib_de.h (Library zur Ansteuerung LCD-Display Ver.1.3) +
- +
      
 + Displayanzeige : keine
 +  
 + Tastenfunktion : Die Tastenstellung der Dip-Schalter an Port B werden als Wert über I2C weitergegeben.
 +   Dabei zählt ein gedrückter Schalter (= hellgrün) als logisches High Signal
 +  
 + Jumperstellung : keine
 +  
 + Fuses im uC : keine
 +   
 // ----------------------------------------------------------------------------*/ // ----------------------------------------------------------------------------*/
- +  
 // Deklarationen ============================================================== // Deklarationen ==============================================================
- +  
 // Festlegung der Quarzfrequenz // Festlegung der Quarzfrequenz
-#define F_CPU 8000000UL // CPU Frequenz von 8MHz +#define F_CPU 8000000UL // CPU Frequenz von 8MHz 
-#define F_SCL 40000L // Baudrate von 100 kHz +#define F_SCL   100000L // Baudrate von 100 kHz 
 + 
 // Include von Header-Dateien // Include von Header-Dateien
-#include <stdio.h> 
 #include <avr/interrupt.h> #include <avr/interrupt.h>
-#include <math.h>  
 #include <util/delay.h> #include <util/delay.h>
 + 
 // Konstanten // Konstanten
-#define SET_BIT(PORT, BIT)  ((PORT) |=  (1 << (BIT))) // Port-Bit Zustand setzen +#define SET_BIT(BYTE, BIT)  ((BYTE) |=  (1 << (BIT))) // Bit Zustand in Byte setzen 
-#define CLR_BIT(PORT, BIT)  ((PORT) &= ~(1 << (BIT))) // Port-Bit Zustand loeschen +#define CLR_BIT(BYTE, BIT)  ((BYTE) &= ~(1 << (BIT))) // Bit Zustand in Byte loeschen 
-#define TGL_BIT(PORT, BIT)  ((PORT) ^=  (1 << (BIT))) // Port-Bit Zustand wechseln (toggle)+#define TGL_BIT(BYTE, BIT)  ((BYTE) ^=  (1 << (BIT))) // Bit Zustand in Byte wechseln (toggle) 
 +  
 +#define SET_ALL_PULLUPS (0xFF) // Konstante für die Aktivierung der Pullup 
 +#define INVERING_MASK (0xFF) // Konstante für die invertierung der Schalterstellungen 
 +#define TWI_ADRESS (0b0001010) // Konstante für die I2C Adresse 
  
 //Funktionsprototypen //Funktionsprototypen
Zeile 261: Zeile 241:
 void I2C_transmitDataOrAddress(char Data); void I2C_transmitDataOrAddress(char Data);
 void I2C_transmitStop(); void I2C_transmitStop();
-void I2C_Data(); +  
- +uint8_t TWI_Address =  TWI_ADRESS; // Variable der I2C Adresse  
-uint8_t TWI_Address = 0b0001010+uint8_t TWI_Data    0b00000000; // Variable für die I2C Daten 
-uint8_t TWI_Data = 0b00110111+  
- + 
 int main(void) int main(void)
 { {
- cli (); // Interrupt aktivieren + PORTB = SET_ALL_PULLUPS; // Pull-up Widerstände an Port B aktivieren 
- SET_BIT(DDRD, PORTD1); // Debug Ausgang ansprechen -> Wechsel auf low +    while (1)  
- TGL_BIT(PORTD, PORTD1); // Debug Ausgang ansprechen -> Wechsel auf High +    
- I2C_Init(); // Initialisierung von TWI anstoßen +        I2C_Init(); // Initialisierung von TWI anstoßen 
- +        I2C_transmitStart(); // Startbit schreiben 
- +        I2C_transmitDataOrAddress((TWI_Address<<1) + 0);// Adresse senden: LSB = 0, zeigt Slave an, dass dieser nur empfangen soll 
- while (1) + TWI_Data = PINB^INVERING_MASK; // Lese Tasterstellungen ein. Invertiere jedes Bit 
-+        I2C_transmitDataOrAddress(TWI_Data); // Daten senden 
- TGL_BIT(PORTD, PORTD1); // Debug Ausgang ansprechen -> Wechsel auf High +        I2C_transmitStop(); // Stoppbit schreiben 
- I2C_Init(); // Initialisierung von TWI anstoßen + _delay_us(1); // erst durch den Delay ist ein Triggern im Simulide möglich  
- TGL_BIT(PORTD, PORTD1); // Debug Ausgang ansprechen -> Wechsel auf Low +    }
- I2C_transmitStart(); // Startbit schreiben +
- TGL_BIT(PORTD, PORTD1); // Debug Ausgang ansprechen -> Wechsel auf High +
- I2C_transmitDataOrAddress((TWI_Address<<1) + 0);// Adresse senden +
- TGL_BIT(PORTD, PORTD1); // Debug Ausgang ansprechen -> Wechsel auf Low +
- I2C_transmitDataOrAddress(TWI_Data);// Daten senden +
- TGL_BIT(PORTD, PORTD1); // Debug Ausgang ansprechen -> Wechsel auf High +
- I2C_transmitStop(); // Stoppbit schreiben +
- TGL_BIT(PORTD, PORTD1); // Debug Ausgang ansprechen -> Wechsel auf Low +
- _delay_us(1); +
-  +
- }+
 } }
 + 
 ///////////////////////////////////////// /////////////////////////////////////////
 // I2C Initialisierung // I2C Initialisierung
Zeile 298: Zeile 266:
 void I2C_Init() void I2C_Init()
 { {
- TWSR = CLR_BIT(TWSR, TWPS0);// Es wird kein Prescaler verwendet +    CLR_BIT(TWSR, TWPS0); // Es wird kein Prescaler verwendet 
- TWSR = CLR_BIT(TWSR, TWPS1);// +    CLR_BIT(TWSR, TWPS1); // Deshalb TWPS0 = 0 und TWPS1 = 0 
- TWCR = 0; // +    TWCR = 0;                   // Control Register zurücksetzen 
- TWBR = ((F_CPU/F_SCL)-16)/2;// die Bitrate wird mittels CPU Frequenz und Serial Clock Frequenz ermittelt+    TWBR = ((F_CPU/F_SCL)-16)/2; // die Bitrate wird mittels CPU Frequenz und Serial Clock Frequenz ermittelt
 } }
 + 
 ///////////////////////////////////////// /////////////////////////////////////////
 // I2C Startbit senden // I2C Startbit senden
Zeile 309: Zeile 277:
 void I2C_transmitStart() void I2C_transmitStart()
 { {
- TWCR = (1<<TWSTA)|(1<<TWEN)|(1<<TWINT); // TWSTA = Startbit aktivieren, TWEN = TWI starten (ENable), TWINT = Interrupt bit löschen (durch setzen+    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTA); // TWSTA = Startbit aktivieren, TWEN = TWI starten (ENable), TWINT = Interrupt bit löschen (durch beschreiben
- while (!(TWCR & (1<<TWINT))){}; // warten bis Übertragung erfolgreich, Trigger ist hier das Setzen von TWINT+    while (!(TWCR & (1<<TWINT))); // warten bis Übertragung erfolgreich, Trigger ist hier das Setzen von TWINT
 } }
- +  
 + 
 ///////////////////////////////////////// /////////////////////////////////////////
 // I2C Adressbyte/Daten senden // I2C Adressbyte/Daten senden
 ///////////////////////////////////////// /////////////////////////////////////////
-void I2C_transmitDataOrAddress(char Data) +void I2C_transmitDataOrAddress(char Data)                       
 { {
- TWDR = Data; +    TWDR = Data; // Data Variabel in Daten Register schreiben 
- TWCR = (1<<TWINT)|(1<<TWEN); // TWEN = TWI starten (ENable), TWINT = Interrupt bit löschen (durch setzen) +    TWCR = (1<<TWINT)|(1<<TWEN); // TWEN = TWI starten (ENable), TWINT = Interrupt bit löschen (durch setzen) 
- while (!(TWCR & (1<<TWINT))); // warten bis Übertragung erfolgreich, Trigger ist hier das Setzen von TWINT+    while (!(TWCR & (1<<TWINT))); // warten bis Übertragung erfolgreich, Trigger ist hier das Setzen von TWINT
 } }
 + 
 ///////////////////////////////////////// /////////////////////////////////////////
 // I2C Stoppbit senden // I2C Stoppbit senden
Zeile 329: Zeile 297:
 void I2C_transmitStop() void I2C_transmitStop()
 { {
- TWCR=(1<<TWSTO)|(1<<TWINT)|(1<<TWEN); // TWSTO = Stopptbit aktivieren, TWEN = TWI starten (ENable), TWINT = Interrupt bit löschen (durch setzen) +    TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO); // TWSTO = Stopptbit aktivieren, TWEN = TWI starten (ENable), TWINT = Interrupt bit löschen (durch setzen)
- while (!(TWCR & (1<<TWINT))); // warten bis Übertragung erfolgreich, Trigger ist hier das Setzen von TWINT +
- // while(TWCR & (1<<TWSTO)); // warten bis Übertragung erfolgreich, Trigger ist hier das setzen von TWINT+
 } }
 +</sxh>
 +</WRAP><WRAP column 55%>
 + \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\
 +''Deklarationen ===================================''
 +\\ \\
 +  - Hier wird die Frequenz des Quarz direkt eingestellt. 
 +  - Weiterhin wird eine Konstante für die I2C Frequenz definiert \\ \\ 
 +  - Die Header-Dateien und die Bit-ändernden Makros entsprechen denen der letzten Programme. \\ \\ \\ \\ \\ \\
 +  - Die weiteren Konstanten sind:
 +    - Konstante für die Pullups
 +    - Konstante für die Invertierung der Schalterstellungen
 +    - Konstante für die I2C Adresse  \\ \\ \\ \\ \\
 +  - Zwei globale Variablen beinhalten die I2C Adresse und die Daten \\ \\
  
 +''Hauptprogramm =========================''
 +
 +  - es werden zunächst die Pull-up Widerstände aller Pins an Port B aktiviert, um die Schalter einlesen zu können
 +  - in der Haupschleife läuft:
 +    - Zu Beginn eine Iitialisierung der I2C Schnittstelle. Die wiederholte Initialisierung vermeidet Probleme der I2C Zustandsmaschine.
 +    - Als nächstes wird die startende Flanke gesendet.
 +    - Das erste Byte auf dem I2C Bus ist die Adressebyte. Dieses setzt sich aus der Adresse und einem Bit zusammen, welches angibt, ob der Slave nur zuhören (''0'') oder antworten (''1'') soll.
 +    - Nun wird der Zustand das Port B als I2C Daten eingelesen. 
 +    - Diese werden per I2C übertragen.
 +    - Und zum Schluss wird das Stopp Bit gesendet \\ \\
 +
 +'' I2C Initialisierung =========================''
 +  - Durch das Zurücksetzen der Bits ''TWPS0'' und ''TWPS1'' wird kein Prescaler (bzw. einer von 1) gewählt
 +  - Auch das Kontrollregister wird zurückgesetzt
 +  - im Bitraten Register wird die I2C Frequenz eingestellt \\ \\ \\ \\
 +'' I2C Startbit senden ==============''
 +  - Es soll die I2C Schnittstelle aktiviert (''TWEN'' setzen) und das Startbit gesendet (''TWSTA'' setzen) werden. Das Schreiben einer ''1'' in ''TWINT'' löscht dieses Interruptflag
 +  - Nach dem Ändern des Konrollregisters muss die Abarbeitung abgewartet werden. Dies ist daran zu erkennen, das ''TWINT'' gleich ''1'' wird \\ \\ \\ 
 +'' I2C Adressbyte/Daten senden =========================''
 +  - Bevor Daten übertragen werden sollen, müssen diese erst in das I2C Datenregister geschrieben werden.
 +  - Wieder die I2C Schnittstelle aktivieren (''TWEN'' setzen) und das Inrerruptflag löschen (''TWINT'' setzen)
 +  - auch hier muss wieder die Abarbeitung abgewartet werden \\ \\ \\
 +'' I2C Stoppbit senden =========================''
 +  - Das Stoppbit wird wieder im Kontrollregister aktiviert
 +  - Hier ist kein Warten notwendig 
 + 
 +</WRAP></WRAP>
 +
 +<fs x-large>**I2C Slave**</fs>
 +
 +<WRAP group><WRAP column 40%><sxh c; first-line: 1>
 +/* ----------------------------------------------------------------------------
 +  
 + Experiment 10:   I2C Kommunikation
 + =============    ===============================
 +  
 + Dateiname : I2C_SimpleSlave.c
 +  
 + Autoren : Tim Fischer       (Hochschule Heilbronn, Fakultaet TE)
 +  
 + Datum : 18.11.2023
 +  
 + Version : 1.0
 +   
 + Hardware :  Simulide 0.5.16-RC5
 +  
 + Software :  Entwicklungsumgebung: Microchip Studio 7.0
 +    C-Compiler: AVR/GNU C Compiler 5.4.0
 +   
 + Funktion : TBD
 + 
 + Displayanzeige : keine
 + 
 + Tastenfunktion : Die Tastenstellung der Dip-Schalter an Port B werden als Wert über I2C weitergegeben.
 +   Dabei zählt ein gedrückter Schalter (= hellgrün) als logisches High Signal
 + 
 + Jumperstellung :  keine
 + 
 + Fuses im uC : keine
 +  
 +   
 +// ----------------------------------------------------------------------------*/
 +  
 +// Deklarationen ==============================================================
 +  
 +// Festlegung der Quarzfrequenz
 +#define F_CPU 8000000UL // CPU Frequenz von 8MHz
 +#define F_SCL   100000L // Baudrate von 100 kHz
 + 
 +// Include von Header-Dateien
 +#include <avr/interrupt.h>
 + 
 +// Konstanten
 +#define SET_BIT(BYTE, BIT)  ((BYTE) |=  (1 << (BIT))) // Bit Zustand in Byte setzen
 +#define CLR_BIT(BYTE, BIT)  ((BYTE) &= ~(1 << (BIT))) // Bit Zustand in Byte loeschen
 +#define TGL_BIT(BYTE, BIT)  ((BYTE) ^=  (1 << (BIT))) // Bit Zustand in Byte wechseln (toggle)
 +
 +#define SET_ALL_TO_OUT (0xFF) // Konstante für die Aktivierung der Port Ausgänge
 +#define TWI_ADRESS (0b0001010) // Konstante für die I2C Adresse
 +#define TWI_ADRESS_MASK (0b0001010) // Konstante für die I2C Adress-Maske
 + 
 +//Funktionsprototypen
 +void I2C_Init();
 +void I2C_setAddress(char Address);
 +char I2C_readData();
 + 
 +uint8_t TWI_Address         = TWI_ADRESS; // Variable der I2C Adresse 
 +uint8_t TWI_AddressMask     = TWI_ADRESS_MASK; // Variable Zum maskieren der eingehenden Adresse
 + 
 +int main(void)
 +{
 +    DDRB= SET_ALL_TO_OUT; // Auf DDRC die Daten ausgeben
 +    I2C_setAddress(TWI_Address); // eigene Adresse setzen
 +     
 +    while (1) 
 +    {
 +        PORTB = I2C_readData(); // Daten an PortC ausgeben
 +    } 
 +}
 + 
 +//////////////////////////////////////////////////
 +// Setzen der I2C Adresse auf die der Slave hört
 +//////////////////////////////////////////////////
 +void I2C_setAddress(char Address)                       
 +{
 +    TWAR = (Address<<1); // Adresse in das Pseudoregister schreiben
 +    TWAMR= TWI_AddressMask;                             // Adressmaske in das Pseudoregister schreiben
 +    TWCR = (1<<TWEA)|(1<<TWEN);                         // Enable Ack, Enable Interupt und Enable TIW
 +}
 + 
 +//////////////////////////////////////////////////
 +// Auslesen der übermittelten Daten 
 +//////////////////////////////////////////////////
 +char I2C_readData()                     
 +{
 +    while (!(TWCR & (1<<TWINT))); // warte solange bis TWINT gesetzt ist
 +    return TWDR;                                        // übermittle Daten
 +}
 </sxh> </sxh>
-</WRAP>+</WRAP><WRAP column 55%> 
 + \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ 
 +''Deklarationen ==================================='' 
 +\\ \\ 
 +  - Hier wird die Frequenz des Quarz direkt eingestellt.  
 +  - Weiterhin wird eine Konstante für die I2C Frequenz definiert \\ \\  
 +  - Die Header-Dateien und die Bit-ändernden Makros entsprechen denen der letzten Programme. \\ \\ \\ \\  
 +  - Die weiteren Konstanten sind: 
 +    - Konstante für die das Setzen der Pins als Ausgang 
 +    - Konstante für die I2C Adresse   
 +    - Konstante für die Maskierung der Adresse \\ \\ \\ \\ \\ 
 +  - Zwei globale Variablen beinhalten die I2C Adresse und die Maske für die eingelesene Adresse \\ \\  
 + 
 +''Hauptprogramm ========================='' 
 + 
 +  - Es werden alle Pins an Port B als Ausgang geschalten 
 +  - Die I2C Adresse, auf welche der Slave hört, wird gesetzt \\ \\ 
 +  - In der Haupschleife läuft nur das Setzen des Port B anhand der eingelesenen I2C Daten \\ \\ \\ \\ \\ 
 + 
 +'' Setzen der I2C Adresse auf die der Slave hört ========================='' 
 +  - Die Adresse wird in das Adressbyte geschrieben.  
 +  - Die Adressmaske wird in das Maskenbyte geschrieben. Durch die Maske lässt sich ein Slave auch mit verschiedenen Adressen ansprechen, da die Maske angibt, welche Bits nicht berücksichtigt werden sollen (mit ''0''
 +  - Im Kontrollregister wird die "Rückbestätigung" and den Master (Acknowledge) aktiviert (''TWEA'' setzen) und das I2C Modul aktiviert (''TWEN'' setzen). Hier darf ''TWINT'' nicht geändert werden! 
 +'' Auslesen der übermittelten Daten =============='' 
 +  - Hier wird solange gewartet, bis I2C Daten vorliegen 
 +  - Nach dem Ändern des Konrollregisters muss die Abarbeitung abgewartet werden. Dies ist daran zu erkennen, das ''TWINT'' gleich ''1'' wird 
 +  
 +</WRAP></WRAP> 
 + 
 +<-- 
 + 
 + 
 +==== komplexere Anwendung - mit Interrupt ==== 
 + 
 +Als Beispiel wurde hier die Temperaturmessung aus Lektion 8 gewählt. \\ 
 +Das Projekt und die Simulation ist hier zu finden {{microcontrollertechnik:ad_wandler_i2c.zip}} 
 + 
 +Bitte nutzen Sie diese als Vorlage, wen Sie eine I2C Schnittstelle implementieren wollen. Da in diesem Programmstand alles über Interrupts läuft, können auch weitere Funktionen abgearbeitet werden. 
 + 
 +===== weiterführende Unterlagen ===== 
 + 
 +==== Bibliotheken ==== 
 + 
 +  * TWI Module as I2C Master (AVR315): 
 +    * Application Note: {{microcontrollertechnik:doc2564.pdf}} 
 +    * Library und Beispielcode: {{microcontrollertechnik:avr315.zip}} 
 +  * TWI Module as I2C Slave (AVR311): 
 +    * Application Note: {{microcontrollertechnik:doc2565.pdf}} 
 +    * Library und Beispielcode: {{microcontrollertechnik:avr311.zip}} 
 +  * alternative und schlanke Implementierung des Slaves von [[https://github.com/thegouger/avr-i2c-slave|The Gouger (GitHub)]] \\ (Kopie vom 16.01.22: {{microcontrollertechnik:avr-i2c-slave-master.zip}}) 
 + 
 +==== Beispiele ====
  
-===== Beispiele =====+  * In Simulide ist eine Umsetzung von Software I2C zu finden: ''...\share\simulide\examples\Arduino\sofware_i2c_lcd\i2c_lcd-arduino''  
 +  * Eine vollständige Implementierung des Codes für den I2C Master ist in der Library von Peter Fleury zu finden: [[http://www.peterfleury.epizy.com/avr-software.html|library]], [[http://www.peterfleury.epizy.com/doxygen/avr-gcc-libraries/group__pfleury__ic2master.html|Dokumentation]] 
 +  * Eine Implementierung eines [[Software I2C Slave]], also eines I2C an einem beliebigen Pin durch Bitmaipulation, habe ich bereitgestellt. Es soll jedoch stets bevorzugt werden die vorhandenen Hardware I2C zu nutzen.
  
-  * Simulide: ''...\share\simulide\examples\Arduino\sofware_i2c_lcd\i2c_lcd-arduino'' (hierbei wird Software I2C eingesetzt) +==== Beschreibung ====
-  * Software I2C: +
-    * Library von Peter Fleury: [[http://www.peterfleury.epizy.com/avr-software.html|library]], [[http://www.peterfleury.epizy.com/doxygen/avr-gcc-libraries/group__pfleury__ic2master.html|Dokumentation]] +
-    * [[Software I2C Slave]]+
  
 +Die "[[https://mu.microchip.com/practical-i2c-introduction-implementation-and-troubleshooting|Microchip University]]" hat auch eine schöne Einführung in I2C. Hier wird aber nicht die Implementierung in Microchip Studio auf einem AVR-Chip gezeigt, sondern in MPLAB X auf einem PIC. D.h. der Code ist nicht direkt übertragbar.