Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
Balkonkraftwerk Speicher und Wechselrichter Tests und Tutorials

(Neu)
 
 
(8 dazwischenliegende Versionen von 4 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
In diesem Artikel folgen einige Programmbeispiele um das [[USI (Avr)|USI]]-Hardwaremodul der [[AVR]]s zu verwenden. Mit dem [[USI (Avr)|USI]]-Modul können die Schnittstellen [[I2C]] ([[TWI]]) und [[SPI]] nachgebildet werden. Näheres zu [[USI (Avr)|USI]], [[I2C]] und [[SPI]] finden sich in den entsprechenden Artikeln. Die Beispiele sind zwar in [[Bascom]] Basic verfasst, aber so ausgeführt, das es möglich sein sollte das Prinzip mit jeder anderen Sprache nachvollziehen zu können.
+
In diesem Artikel folgen einige Programmbeispiele, um das [[USI (Avr)|USI]]-Hardwaremodul der [[AVR]]s zu verwenden. Mit dem [[USI (Avr)|USI]]-Modul können die Schnittstellen [[I2C]] ([[TWI]]) und [[SPI]] nachgebildet werden. Näheres zu [[USI (Avr)|USI]], [[I2C]] und [[SPI]] finden sich in den entsprechenden Artikeln. Die Beispiele sind zwar in [[Bascom]] Basic verfasst, aber so ausgeführt, dass es möglich sein sollte das Prinzip mit jeder anderen Sprache nachvollziehen zu können.
  
  
 
== USI-I2C-Master ==
 
== USI-I2C-Master ==
[[Bild:USI-I2C_PCF8574_RN-Control_Tiny2313.JPG|thumb|Beispielumgebung]]
+
[[Bild:USI-I2C_PCF8574_RN-Control_Tiny2313.JPG|thumb|Beispielumgebung mit PCF8574]]
 
Ist der AVR Master, bestimmt er was und wie schnell es auf dem [[I2C|I<sup>2</sup>C]]-Bus zugeht. (Ausnahme: [[Clock_Stretching]] )
 
Ist der AVR Master, bestimmt er was und wie schnell es auf dem [[I2C|I<sup>2</sup>C]]-Bus zugeht. (Ausnahme: [[Clock_Stretching]] )
  
Zur Generierung der gewünschten Busgeschwindigkeit ist die Software zuständig, die länge der Pausen zwischen den Takten muss erechnet werden, im Beispielprogramm für ca. 100kHz bei 16MHz CPU-Frequenz.
+
Zur Generierung der gewünschten Busgeschwindigkeit ist die Software zuständig. Die Länge der Pausen zwischen den Takten muss errechnet werden. Im Beispielprogramm für ca. 100 kHz bei 16 MHz CPU-Frequenz.
 
Der Takt kann auch mithilfe eines [[Timer/Counter (Avr)|Timer]]s erzeugt werden, dies wird erst zu einem späteren Zeitpunkt mit einem Beispiel gezeigt.
 
Der Takt kann auch mithilfe eines [[Timer/Counter (Avr)|Timer]]s erzeugt werden, dies wird erst zu einem späteren Zeitpunkt mit einem Beispiel gezeigt.
  
In den Beispielen wird als Master das Board [[RN-Control]] mit einem [[ATtiny2313|Tiny2313]], und als Slave ein [[I2C_Chip-Übersicht#I.2FO_expanders:|PCF8574]], da sich dieser leicht ansteuern lässt. <!--  und jeden Scheiss mitmacht ;-)) -->
+
In den Beispielen wird als Master das Board [[RN-Control]] mit einem [[ATtiny2313|Tiny2313]], und als Slave ein [[I2C_Chip-Übersicht#I.2FO_expanders:|PCF8574]] verwendet, da sich dieser leicht ansteuern lässt. <!--  und jeden Scheiss mitmacht ;-)) -->
  
 
----
 
----
Zeile 18: Zeile 18:
 
;Nur Senden
 
;Nur Senden
  
Der Master sendet einem Slave ein (oder mehrere) Byte, und schliesst die Übertragung anschliessend ab.
+
Der Master sendet einem Slave ein (oder mehrere) Byte, und schließt die Übertragung anschließend ab.
  
 
Das Gegenstück wäre ''Slave Receiver''.
 
Das Gegenstück wäre ''Slave Receiver''.
Zeile 24: Zeile 24:
  
 
Beispielprogramm sendet ein Byte zum Slave mit Adresse 64 (0x40 bzw. &H40):
 
Beispielprogramm sendet ein Byte zum Slave mit Adresse 64 (0x40 bzw. &H40):
 
  
 
  {{vbcomment|USI-I2C Testprogramm}}
 
  {{vbcomment|USI-I2C Testprogramm}}
Zeile 49: Zeile 48:
 
  Ddr_usi_sda Alias Ddrb.5
 
  Ddr_usi_sda Alias Ddrb.5
 
   
 
   
  Dim Usi_twi_errorstate As Byte                           {{vbcomment|eigener Fehlerstatus}}
+
  Dim Usi_twi_errorstate As Byte                         {{vbcomment|eigener Fehlerstatus}}
 
  {{vbcomment|Array der Daten die übertragen werden}}
 
  {{vbcomment|Array der Daten die übertragen werden}}
 
  Dim Messagebuf(4) As Byte
 
  Dim Messagebuf(4) As Byte
  Dim Temp_usisr As Byte                                   {{vbcomment|Tempvariable für Unterprogramm}}
+
  Dim Temp_usisr As Byte                                 {{vbcomment|Tempvariable für Unterprogramm}}
  Dim Anzahlbuf As Byte                                     {{vbcomment|Anzahl Zeichen die gesendet werden sollen }}
+
  Dim Anzahlbuf As Byte                                   {{vbcomment|Anzahl Zeichen die gesendet werden sollen }}
  Dim Cnt As Byte                                           {{vbcomment|Zähler}}
+
  Dim Cnt As Byte                                         {{vbcomment|Zähler}}
  Dim B As Byte                                             {{vbcomment|Zeichen von UART oder Testzeichen zum senden über USI}}
+
  Dim B As Byte                                           {{vbcomment|Zeichen von UART oder Testzeichen zum senden über USI}}
 
   
 
   
 
  {{vbcomment|Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges.}}
 
  {{vbcomment|Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges.}}
Zeile 63: Zeile 62:
 
   
 
   
 
  B = 0
 
  B = 0
  Anzahlbuf = 2                                             {{vbcomment|in diesem Beispiel immer nur 2 Zeichen}}
+
  Anzahlbuf = 2                                           {{vbcomment|in diesem Beispiel immer nur 2 Zeichen}}
 
   
 
   
  Waitms 300                                               {{vbcomment|Sicherheitspause nach Reset}}
+
  Waitms 300                                             {{vbcomment|Sicherheitspause nach Reset}}
 
   
 
   
 
  Call Usi_twi_master_initialise
 
  Call Usi_twi_master_initialise
Zeile 72: Zeile 71:
 
  Print "Tiny2313 USI-TWI-Test"
 
  Print "Tiny2313 USI-TWI-Test"
 
   
 
   
  Messagebuf(1) = &H40                                     {{vbcomment|Adresse von 8574}}
+
  Messagebuf(1) = &H40                                   {{vbcomment|Adresse von 8574}}
 
   
 
   
 
  Do
 
  Do
 
 
     {{vbcomment|warten bis etwas über UART kommt}}
 
     {{vbcomment|warten bis etwas über UART kommt}}
 
     Input B
 
     Input B
Zeile 82: Zeile 80:
 
   
 
   
 
     {{vbcomment|Den Wert zum Slave senden}}
 
     {{vbcomment|Den Wert zum Slave senden}}
     Messagebuf(2) = Not B                                 {{vbcomment|not, weil andersrum}}
+
     Messagebuf(2) = Not B                               {{vbcomment|Not, weil LEDs gegen GND schalten}}
 
     Call Usi_twi_start_transceiver
 
     Call Usi_twi_start_transceiver
 +
    Call Usi_twi_master_stop
 
   
 
   
 
     {{vbcomment|Ausgabe, damit wir sehen was geschehen ist}}
 
     {{vbcomment|Ausgabe, damit wir sehen was geschehen ist}}
Zeile 92: Zeile 91:
 
     Print Hex(temp_usisr)
 
     Print Hex(temp_usisr)
 
   
 
   
     Call Usi_twi_master_initialise                       {{vbcomment|nochmal initialisieren falls ein Fehler aufgetreten ist}}
+
     Call Usi_twi_master_initialise                     {{vbcomment|nochmal initialisieren falls ein Fehler aufgetreten ist}}
 
   
 
   
 
     {{vbcomment|Waitms 700  ' Wenn automatisch gezählt werden soll, eine kleine Pause, damit man auch was sehen kann}}
 
     {{vbcomment|Waitms 700  ' Wenn automatisch gezählt werden soll, eine kleine Pause, damit man auch was sehen kann}}
Zeile 98: Zeile 97:
 
   
 
   
 
  End
 
  End
 +
 +
==== Unterprogramm nur Senden ====
 +
 +
Die restlichen Subroutinen sind im nächsten Abschnitt zu finden.
 +
 +
Diese Funktion ist nur zum Versenden geeignet !
 +
 +
Statt dieser kann auch die Routine von unten genommen werden, die aber etwas mehr Speicherplatz benötigt.
 +
 +
{{vbcomment|USI Transmit Funktion.}}
 +
Sub Usi_twi_start_transceiver()
 
   
 
   
 +
    Usi_twi_errorstate = 0
 +
 +
    {{vbcomment|Test if any unexpected conditions have arrived prior to this execution.}}
 +
    {{vbcomment|Ist eine Startbedingung aufgetreten ?}}
 +
    If Usisr.7 = 1 Then                                {{vbcomment|USISIF}}
 +
        Usi_twi_errorstate = &H02                      {{vbcomment|Usi_twi_ue_start_con}}
 +
        Exit Sub
 +
    End If
 +
 +
    {{vbcomment|Ist eine Stopbedingung aufgetreten ?}}
 +
    If Usisr.5 = 1 Then                                {{vbcomment|USIPF}}
 +
        Usi_twi_errorstate = &H03                      {{vbcomment|Usi_twi_ue_stop_con}}
 +
        Exit Sub
 +
    End If
 +
 +
    {{vbcomment|Ist eine Datenkollision aufgetreten ?}}
 +
    If Usisr.4 = 1 Then                                {{vbcomment|USIDC}}
 +
        Usi_twi_errorstate = &H04                      {{vbcomment|Usi_twi_ue_data_col}}
 +
        Exit Sub
 +
    End If
 +
 +
    {{vbcomment|Release SCL to ensure that (repeated) Start can be performed}}
 +
    Pout_usi_scl = 1
 +
    Waitus 5
 +
 +
    {{vbcomment|Startbedingung ausgeben}}
 +
    Pout_usi_sda = 0                                    {{vbcomment|Force SDA LOW.}}
 +
    Waitus 4
 +
    Pout_usi_scl = 0                                    {{vbcomment|Pull SCL LOW.}}
 +
    Pout_usi_sda = 1                                    {{vbcomment|Release SDA.}}
 +
 +
    {{vbcomment|prüfen ob die Startbedingung vom eigenen USI erkannt wurde}}
 +
    If Usisr.7 = 0 Then                                {{vbcomment|USISIF}}
 +
        Usi_twi_errorstate = &H07
 +
        Exit Sub
 +
    End If
 +
 +
    {{vbcomment|Slaveadresse und Daten ausgeben}}
 +
    For Cnt = 1 To Anzahlbuf
 +
        {{vbcomment|Ein Byte ausgeben}}
 +
        Pout_usi_scl = 0                                {{vbcomment|Pull SCL LOW.}}
 +
        Usidr = Messagebuf(cnt)
 +
        Temp_usisr = Temp_usisr_8bit                    {{vbcomment|8Bit ausgeben}}
 +
        Call Usi_twi_master_transfer
 +
 +
        {{vbcomment|(N)ACK vom Slave lesen}}
 +
        Ddr_usi_sda = 0                                {{vbcomment|Enable SDA as input.}}
 +
        Temp_usisr = Temp_usisr_1bit                    {{vbcomment|1Bit einlesen}}
 +
        Call Usi_twi_master_transfer
 +
 +
        {{vbcomment|kein ACK gekommen, Slave meldet sich nicht}}
 +
        If Temp_usisr.0 = 1 Then
 +
            Usi_twi_errorstate = &H05                  {{vbcomment|Slave hat mit NACK quittiert}}
 +
            If Cnt = 1 Then
 +
                Usi_twi_errorstate = &H06              {{vbcomment|Es hat sich kein Slave gemeldet}}
 +
            End If
 +
            Exit For                                    {{vbcomment|hier wird die Schleife bei einem NACK verlasssen}}
 +
        End If
 +
 +
    Next Cnt
 +
 +
End Sub
 +
 +
----
 +
 +
=== Transmitter und Receiver mit DS1621 als Slave ===
 +
[[Bild:USI-I2C_DS1621_RN-Control_Tiny2313.JPG|thumb|Beispielumgebung mit DS1621]]
 +
 +
;Senden und Empfangen (Repeated Start)
 +
Im Beispiel wird ein DS1621 Temperatursensor mit I2C-Schnittstelle verwendet.<br>
 +
Der Master sendet dem Slave ein Kommando, stellt um auf Empfang und holt den Temperaturwert, schließt die Übertragung anschließend ab.
 +
 +
Der Temperaturwert wird über [[UART]] ([[RS232]]) im Klartext ausgegeben.
 +
 +
Die Subroutinen sind die gleichen wie für ''Master Transmitter''. Da der DS1621 aber mit 400 kHz kommunizieren kann, wurden die ''Waitus'' zwischen den Pegelwechseln weggelassen, da die Befehle hier sowieso nicht schnell genug sind und ein Wait unnötig machen. Sie sind aber als Kommentar eingefügt, damit zu sehen ist an welchen Stellen ansonsten gewartet werden sollte (zB bei 100kHz).
 +
 +
Beispielprogramm holt Temperaturwert vom Slave mit Adresse 144 (0x90 bzw. &H90):<br>
 +
(Das Beispiel belegt 1418 Byte im Flash, das sind ca. 69% eines Tiny2313 !)
 +
 +
{{vbcomment|USI-I2C Testprogramm}}
 +
{{vbcomment|mit DS1621 @ &H90}}
 +
{{vbcomment|}}
 +
{{vbcomment|ohne Interrupt und ohne Timer}}
 +
{{vbcomment|Temperatur von DS1621 lesen, und über UART ausgeben}}
 +
$regfile = "attiny2313.dat"
 +
$crystal = 16000000
 +
$baud = 9600
 +
 +
{{vbcomment|Unterprogramme für die USI-Kommunikation}}
 +
Declare Sub Usi_twi_master_initialise()
 +
Declare Sub Usi_twi_start_transceiver()
 +
Declare Sub Usi_twi_master_stop()
 +
Declare Sub Usi_twi_master_transfer()
 +
 +
{{vbcomment|einige Aliases anlegen}}
 +
Pout_usi_scl Alias Portb.7
 +
Pin_usi_scl Alias Pinb.7
 +
Ddr_usi_scl Alias Ddrb.7
 +
Pout_usi_sda Alias Portb.5
 +
Pin_usi_sda Alias Pinb.5
 +
Ddr_usi_sda Alias Ddrb.5
 +
 +
Dim Usi_twi_errorstate As Byte                          {{vbcomment|eigener Fehlerstatus}}
 +
{{vbcomment|Array der Daten die übertragen werden}}
 +
Dim Messagebuf(4) As Byte
 +
Dim Temp_usisr As Byte                                  {{vbcomment|Tempvariable für Unterprogramm}}
 +
Dim Anzahlbuf As Byte                                  {{vbcomment|Anzahl Zeichen die gesendet werden sollen}}
 +
Dim Cnt As Byte                                        {{vbcomment|Zähler}}
 +
Dim Rw As Bit                                          {{vbcomment|Read/Write Flag}}
 +
 +
{{vbcomment|Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges.}}
 +
Const Temp_usisr_8bit = &HF0
 +
{{vbcomment|Prepare register value to: Clear flags, and set USI to shift 1 bit i.e. count 2 clock edges.}}
 +
Const Temp_usisr_1bit = &HFE
 +
 +
Waitms 500                                              {{vbcomment|Sicherheitspause nach Reset}}
 +
 +
Call Usi_twi_master_initialise
 +
 +
Dim Device As Byte
 +
Dim Deviceread As Byte
 +
Dim Lowtemp As Byte
 +
Dim Hightemp As Byte
 +
 +
Device = &H90
 +
Deviceread = &H91
 +
 +
Sound Portd.5 , 300 , 450                              {{vbcomment|BEEP}}
 +
 +
Print
 +
Print "DS1621-USI Temperatur"
 +
 +
 +
{{vbcomment|Hauptprogramm}}
 +
Do
 +
 +
    Messagebuf(1) = Device                              {{vbcomment|Adresse von DS1621}}
 +
    Messagebuf(2) = &HEE                                {{vbcomment|Temperaturmessung anstoßen}}
 +
    Anzahlbuf = 2                                      {{vbcomment|2 Byte ausgeben}}
 +
    Call Usi_twi_start_transceiver
 +
    {{vbcomment|Print Hex(usi_twi_errorstate)}}
 +
 +
    If Usi_twi_errorstate = 0 Then                      {{vbcomment|kein Fehler aufgetreten}}
 +
 +
        Call Usi_twi_master_stop
 +
        {{vbcomment|Print Hex(usi_twi_errorstate)}}
 +
 +
        {{vbcomment|Waitms 1}}
 +
        Messagebuf(1) = Device                          {{vbcomment|Adresse von DS1621}}
 +
        Messagebuf(2) = &HAA                            {{vbcomment|Temperaturmessung Lesekommando}}
 +
        Anzahlbuf = 2                                  {{vbcomment|2 Byte ausgeben}}
 +
        Call Usi_twi_start_transceiver
 +
        {{vbcomment|Print Hex(usi_twi_errorstate)}}
 +
        {{vbcomment|kein STOP an dieser Stelle !}}
 +
        {{vbcomment|es folgt ein Repeated START !}}
 +
 +
        If Usi_twi_errorstate = 0 Then                  {{vbcomment|kein Fehler aufgetreten}}
 +
            {{vbcomment|Temperatur lesen}}
 +
            Messagebuf(1) = Deviceread                  {{vbcomment|Adresse von DS1621}}
 +
            Messagebuf(2) = 0
 +
            Messagebuf(3) = 0
 +
            Anzahlbuf = 3                              {{vbcomment|3 Byte ausgeben, bzw. einlesen}}
 +
            Call Usi_twi_start_transceiver
 +
            {{vbcomment|Print Hex(usi_twi_errorstate)}}
 +
 +
            If Usi_twi_errorstate = 0 Then              {{vbcomment|kein Fehler aufgetreten}}
 +
 +
                Call Usi_twi_master_stop
 +
                {{vbcomment|Print Hex(usi_twi_errorstate)}}
 +
 +
                {{vbcomment|Gelesenen Bytes stehen ab Position 2 im Array}}
 +
                Lowtemp = Messagebuf(2)
 +
                Hightemp = Messagebuf(3)
 +
 +
                {{vbcomment|Negativer Wert ?}}
 +
                If Lowtemp.7 = 1 Then
 +
                    Lowtemp = Not Lowtemp
 +
                    {{vbcomment|.5 ?}}
 +
                    If &H80 > Hightemp Then
 +
                        Incr Lowtemp
 +
                    End If
 +
                    Print "-" ;
 +
                Else
 +
                    Print " " ;
 +
                End If
 +
 +
                Print Lowtemp ; "," ;
 +
 +
                If Hightemp = &H80 Then
 +
                    Print "5"
 +
                Else
 +
                    Print "0"
 +
                End If
 +
 +
            End If
 +
        End If
 +
    End If
 +
 +
    If Usi_twi_errorstate <> 0 Then                    {{vbcomment|bei einem Fehler den Fehlercode ausgeben}}
 +
        Print "Error " ; Hex(usi_twi_errorstate)
 +
    End If
 +
 +
    {{vbcomment|nochmal initialisieren falls ein Fehler aufgetreten ist}}
 +
    Call Usi_twi_master_initialise
 +
 +
    Waitms 2000
 +
Loop
 +
 +
End
 +
 +
 +
=== Unterprogramme für USI-Kommunikation ===
 +
 +
Hier folgen die Unterprogramme für die USI-I2C-Master Kommunikation
 +
 +
==== Initialisierung ====
 +
 +
USI-I2C Pins Initialisieren, und USI-Register setzen für I2C-Master.
 +
 
  {{vbcomment|USI TWI single master initialization function}}
 
  {{vbcomment|USI TWI single master initialization function}}
 
  Sub Usi_twi_master_initialise()
 
  Sub Usi_twi_master_initialise()
Zeile 111: Zeile 340:
 
     Usidr = &HFF
 
     Usidr = &HFF
 
   
 
   
     {{vbcomment|Disable Interrupts.}}
+
     {{vbcomment|Disable USI Interrupts.}}
 
     {{vbcomment|Set USI in Two-wire mode.}}
 
     {{vbcomment|Set USI in Two-wire mode.}}
 
     {{vbcomment|Software stobe as counter clock source}}
 
     {{vbcomment|Software stobe as counter clock source}}
 
     Usicr = &B00101010
 
     Usicr = &B00101010
 
   
 
   
     {{vbcomment|Clear flags, and reset counter.}}
+
     {{vbcomment|Statusflags löschen, und Counter auf 0 zurücksetzen.}}
 
     Usisr = &B11110000
 
     Usisr = &B11110000
 
   
 
   
 
  End Sub
 
  End Sub
+
 
  {{vbcomment|USI Transmit and receive function.}}
+
==== Senden/Empfangen ====
 +
 
 +
Startbedingung ausgeben,<br>
 +
versenden der Anzahl der angegebenen Bytes,<br>
 +
und einen Status zurückgeben, ob die Daten angenommen (ACK) wurden.
 +
 
 +
  {{vbcomment|USI Transmit und Receive Funktion.}}
 
  Sub Usi_twi_start_transceiver()
 
  Sub Usi_twi_start_transceiver()
 
   
 
   
Zeile 128: Zeile 363:
 
     {{vbcomment|Test if any unexpected conditions have arrived prior to this execution.}}
 
     {{vbcomment|Test if any unexpected conditions have arrived prior to this execution.}}
 
     {{vbcomment|Ist eine Startbedingung aufgetreten ?}}
 
     {{vbcomment|Ist eine Startbedingung aufgetreten ?}}
     If Usisr.7 = 1 Then                                   {{vbcomment|USISIF}}
+
     If Usisr.7 = 1 Then                                 {{vbcomment|USISIF}}
         Usi_twi_errorstate = &H02                         {{vbcomment|Usi_twi_ue_start_con}}
+
         Usi_twi_errorstate = &H02                       {{vbcomment|Usi_twi_ue_start_con}}
 
         Exit Sub
 
         Exit Sub
 
     End If
 
     End If
 
   
 
   
 
     {{vbcomment|Ist eine Stopbedingung aufgetreten ?}}
 
     {{vbcomment|Ist eine Stopbedingung aufgetreten ?}}
     If Usisr.5 = 1 Then                                   {{vbcomment|USIPF}}
+
     If Usisr.5 = 1 Then                                 {{vbcomment|USIPF}}
         Usi_twi_errorstate = &H03                         {{vbcomment|Usi_twi_ue_stop_con}}
+
         Usi_twi_errorstate = &H03                       {{vbcomment|Usi_twi_ue_stop_con}}
 
         Exit Sub
 
         Exit Sub
 
     End If
 
     End If
 
   
 
   
 
     {{vbcomment|Ist eine Datenkollision aufgetreten ?}}
 
     {{vbcomment|Ist eine Datenkollision aufgetreten ?}}
     If Usisr.4 = 1 Then                                   {{vbcomment|USIDC}}
+
     If Usisr.4 = 1 Then                                 {{vbcomment|USIDC}}
         Usi_twi_errorstate = &H04                         {{vbcomment|Usi_twi_ue_data_col}}
+
         Usi_twi_errorstate = &H04                       {{vbcomment|Usi_twi_ue_data_col}}
 
         Exit Sub
 
         Exit Sub
 
     End If
 
     End If
Zeile 147: Zeile 382:
 
     {{vbcomment|Release SCL to ensure that (repeated) Start can be performed}}
 
     {{vbcomment|Release SCL to ensure that (repeated) Start can be performed}}
 
     Pout_usi_scl = 1
 
     Pout_usi_scl = 1
     Waitus 5
+
     Waitus 1                                            {{vbcomment|5}}
 
   
 
   
 
     {{vbcomment|Generate Start Condition}}
 
     {{vbcomment|Generate Start Condition}}
     Pout_usi_sda = 0                                     {{vbcomment|Force SDA LOW.}}
+
     Pout_usi_sda = 0                                   {{vbcomment|Force SDA LOW.}}
     Waitus 4
+
     {{vbcomment|Waitus 4}}
     Pout_usi_scl = 0                                     {{vbcomment|Pull SCL LOW.}}
+
     Pout_usi_scl = 0                                   {{vbcomment|Pull SCL LOW.}}
     Pout_usi_sda = 1                                     {{vbcomment|Release SDA.}}
+
     Pout_usi_sda = 1                                   {{vbcomment|Release SDA.}}
 
   
 
   
     {{vbcomment|checken ob die Startsequenz vom eigenen USI erkannt wurde}}
+
     {{vbcomment|prüfen ob die Startsequenz vom eigenen USI erkannt wurde}}
     If Usisr.7 = 0 Then                                   {{vbcomment|USISIF}}
+
     If Usisr.7 = 0 Then                                 {{vbcomment|USISIF}}
 
         Usi_twi_errorstate = &H07
 
         Usi_twi_errorstate = &H07
 
         Exit Sub
 
         Exit Sub
 
     End If
 
     End If
 +
 +
    {{vbcomment|RW-Bit : Messagebuf(1).0}}
 +
    Rw = Messagebuf(1).0
 
   
 
   
 
     {{vbcomment|Write address and Read/Write data}}
 
     {{vbcomment|Write address and Read/Write data}}
 
     For Cnt = 1 To Anzahlbuf
 
     For Cnt = 1 To Anzahlbuf
         {{vbcomment|Write a byte}}
+
         {{vbcomment|SlaveAdresse immer Write, sonst auf R/W prüfen}}
        Pout_usi_scl = 0                                 {{vbcomment|Pull SCL LOW.}}
+
        If Cnt = 1 Or Rw = 0 Then
        Usidr = Messagebuf(cnt)
+
            {{vbcomment|Write a byte}}
        Temp_usisr = Temp_usisr_8bit
+
            Pout_usi_scl = 0                           {{vbcomment|Pull SCL LOW.}}
        Call Usi_twi_master_transfer
+
            Usidr = Messagebuf(cnt)
 +
            Temp_usisr = Temp_usisr_8bit
 +
            Call Usi_twi_master_transfer
 
   
 
   
        {{vbcomment|Clock and verify (N)ACK from slave}}
+
            {{vbcomment|Clock and verify (N)ACK from slave}}
        Ddr_usi_sda = 0                                   {{vbcomment|Enable SDA as input.}}
+
            Ddr_usi_sda = 0                             {{vbcomment|Enable SDA as input.}}
        Temp_usisr = Temp_usisr_1bit
+
            Temp_usisr = Temp_usisr_1bit
        Call Usi_twi_master_transfer
+
            Call Usi_twi_master_transfer
 
   
 
   
        {{vbcomment|kein ACK gekommen, Slave meldet sich nicht}}
+
            {{vbcomment|kein ACK gekommen}}
        If Temp_usisr.0 = 1 Then
+
            If Temp_usisr.0 = 1 Then
            Usi_twi_errorstate = &H05
+
                Usi_twi_errorstate = &H05               {{vbcomment|Der Slave hat die Daten mit NACK quittiert}}
            Exit For                                     {{vbcomment|hier wird die Schleife bei einem NACK verlasssen}}
+
                If Cnt = 1 Then
        End If
+
                    Usi_twi_errorstate = &H06          {{vbcomment|Es hat sich kein Slave gemeldet}}
 +
                End If
 +
                Exit For                               {{vbcomment|die Schleife bei einem Fehler verlasssen}}
 +
            End If
 +
        Else
 +
            {{vbcomment|Read a Byte}}
 +
            Ddr_usi_sda = 0                            {{vbcomment|Enable SDA as input.}}
 +
            Temp_usisr = Temp_usisr_8bit
 +
            Call Usi_twi_master_transfer
 
   
 
   
    Next Cnt
+
            Messagebuf(cnt) = Temp_usisr                {{vbcomment|Empfangenes Byte ins Array}}
 
   
 
   
    Call Usi_twi_master_stop
+
            {{vbcomment|Prepare to generate ACK (or NACK in case of End Of Transmission)}}
End Sub
+
            If Cnt = Anzahlbuf Then
 +
                Usidr = &HFF                            {{vbcomment|Load NACK to confirm End Of Transmission.}}
 +
            Else
 +
                Usidr = &H00                            {{vbcomment|Load ACK. Set data register bit 7 (output for SDA) low.}}
 +
            End If
 
   
 
   
{{vbcomment|Function for generating a TWI Stop Condition. Used to release the TWI bus.}}
+
            Temp_usisr = Temp_usisr_1bit
Sub Usi_twi_master_stop()
+
            Call Usi_twi_master_transfer                {{vbcomment|ACK oder NACK senden}}
    Pout_usi_sda = 0                                      {{vbcomment|Pull SDA LOW.}}
+
    Pout_usi_scl = 1                                      {{vbcomment|Release SCL.}}
+
 
   
 
   
    {{vbcomment|Wait for SCL to go high.}}
+
        End If
    While Pin_usi_scl = 0
+
     Next Cnt
    Wend
+
    Waitus 4
+
    Pout_usi_sda = 1                                      {{vbcomment|Release SDA.}}
+
    Waitus 5
+
+
    ' checken ob die Stopsequenz vom eigenen USI erkannt wurde}}
+
    If Usisr.5 = 0 Then                                  {{vbcomment|USIPF}}
+
        Usi_twi_errorstate = &H08
+
{{vbcomment|      Exit Sub                ' Exit nicht nötig da schon das Ende erreicht}}
+
     End If
+
 
   
 
   
 
  End Sub
 
  End Sub
+
 
{{vbcomment|Core function for shifting data in and out from the USI.}}
+
==== Ein Byte ausgeben/einlesen ====
{{vbcomment|Data to be sent has to be placed into the USIDR prior to calling}}
+
 
{{vbcomment|this function. Data read, will be return'ed from the function.}}
+
Diese Routine gibt die Anzahl Bit aus, wie anhand der Variablen ''Temp_usisr'' eingestellt wurde, bzw. es wird damit die Anzahl Takte angegeben, die der Counter des USI-Moduls zählt bevor er überläuft, und das Flag ''USIOIF'' im Status-Register ''USISR'' gesetzt wird.
 +
 
 +
Getaktet wird hier per Software, pro Bit zwei Takte. Bei jedem Takt ändert sich der Zustand (High/Low) von SCL.
 +
 
 +
Die Daten, die ausgegeben werden sollen, müssen vor dem Aufruf dieser Routine in das Datenregister ''USIDR'' geschrieben werden.
 +
 
 +
Ob ein Bit ausgegeben oder gelesen wird, wird vor dem Aufruf im Datenrichtungsregister der Datenleitung SDA eingestellt. Im Beispiel mit dem Alias ''Ddr_usi_sda''.
 +
 
 +
Das gelesene Byte von ''USIDR'' wird in die Variable ''Temp_usisr'' geschrieben, und kann vom Hauptprogramm weiterverarbeitet werden.
 +
 
 
  Sub Usi_twi_master_transfer()
 
  Sub Usi_twi_master_transfer()
 
     Usisr = Temp_usisr
 
     Usisr = Temp_usisr
 
   
 
   
     Temp_usisr = &B00101011                               {{vbcomment|for Toggle Clock Port.}}
+
     Temp_usisr = &B00101011                             {{vbcomment|for Toggle Clock Port.}}
 
   
 
   
 
     Do
 
     Do
         Waitus 5
+
         {{vbcomment|Waitus 5}}
         Usicr = Temp_usisr
+
         Usicr = Temp_usisr                             {{vbcomment|SCL takten}}
 
         {{vbcomment|Wait for SCL to go high.}}
 
         {{vbcomment|Wait for SCL to go high.}}
 
         While Pin_usi_scl = 0
 
         While Pin_usi_scl = 0
 
         Wend
 
         Wend
 
   
 
   
         Waitus 4
+
         {{vbcomment|Waitus 4}}
         Usicr = Temp_usisr
+
         Usicr = Temp_usisr                             {{vbcomment|SCL takten}}
 
   
 
   
     Loop Until Usisr.6 = 1                               {{vbcomment|USIOIF, Check for transfer complete.}}
+
     Loop Until Usisr.6 = 1                             {{vbcomment|USIOIF, Wenn der Zähler überläuft, sind alle Bits versendet.}}
 
   
 
   
     Waitus 5
+
     {{vbcomment|Waitus 5}}
     Temp_usisr = Usidr                                   {{vbcomment|Read out data.}}
+
     Temp_usisr = Usidr                                 {{vbcomment|Daten aus USI-Datenregister lesen.}}
     Usidr = &HFF                                         {{vbcomment|Release SDA.}}
+
     Usidr = &HFF                                       {{vbcomment|Release SDA.}}
 
   
 
   
     Ddr_usi_sda = 1                                       {{vbcomment|Enable SDA as output.}}
+
     Ddr_usi_sda = 1                                     {{vbcomment|Enable SDA as output.}}
 
   
 
   
 
  End Sub
 
  End Sub
  
----
+
==== STOP-Bedingung ausgeben ====
  
=== Receiver ===
+
STOP-Bedingung ausgeben, und prüfen ob dies vom eigenen Modul erkannt wurde.<br>
;Nur Empfangen
+
Status zurückgeben, ob die STOP-Bedingung erfolgreich ausgegeben wurde.
Der Master holt von einem Slave ein (oder mehrere) Byte, und schliesst die Übertragung anschliessend ab.
+
  
Das Gegenstück wäre ''Slave Transmitter''.
+
  {{vbcomment|Function for generating a TWI Stop Condition. Used to release the TWI bus.}}
 
+
  Sub Usi_twi_master_stop()
 
+
Beispielprogramm holt ein Byte vom Slave mit Adresse 64 (0x40 bzw. &H40):
+
 
+
  {{vbcomment|USI-I2C Testprogramm}}
+
  {{vbcomment|mit PCF8574 @ &H40}}
+
{{vbcomment|}}
+
{{vbcomment|ohne Interrupt und ohne Timer}}
+
{{vbcomment|ein Byte lesen}}
+
 
   
 
   
Do
+
    Pout_usi_sda = 0                                    {{vbcomment|Pull SDA LOW.}}
Loop
+
    Pout_usi_scl = 1                                    {{vbcomment|Release SCL.}}
 
   
 
   
End
+
    {{vbcomment|Wait for SCL to go high.}}
 
+
    While Pin_usi_scl = 0
----
+
    Wend
 
+
    {{vbcomment|Waitus 4}}
== USI-I2C-Slave ==
+
    Pout_usi_sda = 1                                    {{vbcomment|Release SDA.}}
 
+
    Waitus 1                                            {{vbcomment|5}}
Da der I2C-Master den Takt vorgibt, sind die [[TWI#Anmerkung_zur_CPU-Frequenz|Anmerkungen zur CPU-Frequenz]] zu beachten.
+
 
+
In den Beispielen wird als Master das Board [[RN-Mega8]] mit einem [[ATMega8|Mega8]], und als Slave das [[RN-Control]] Board, mit dem Adapter auf dem der [[ATtiny2313|Tiny2313]] sitzt, verwendet. Da nur diese beiden Boards am Bus hängen, wurde ebenfalls als Slaveadresse 64 (0x40 bzw. &H40) verwendet, damit man die vorhergehenden Beispiele gleich weiterverwenden kann.
+
 
+
----
+
 
+
=== Transmitter ===
+
 
+
;auf Abruf Senden
+
 
+
Der Slave wird von einem Master über eine festgelegte Adresse angesprochen, ein (oder mehrere) Byte werden zum Master übertragen, und anschliessend die Übertragung beendet.
+
 
+
Das Gegenstück wäre ''Master Receiver''.
+
 
+
 
+
Das Beispielprogramm gibt Bytewerte zurück die sich bei jeder Abfrage um eins erhöhen. Verwendet man beim Master das Beispiel von ''Master Receiver'', wird der Wert nach jeder Eingabe von Enter  angezeigt.
+
{{vbcomment|USI-I2C-Slave test}}
+
{{vbcomment|zum simulieren eines PCF8574}}
+
 
   
 
   
Do
+
    {{vbcomment|prüfen ob die Stopsequenz vom eigenen USI erkannt wurde}}
  Loop
+
    If Usisr.5 = 0 Then                                {{vbcomment|USIPF}}
 +
        Usi_twi_errorstate = &H08
 +
  {{vbcomment|      Exit Sub                ' Exit nicht nötig, da schon das Ende erreicht}}
 +
    End If
 
   
 
   
  End
+
    Usisr.5 = 1                                        {{vbcomment|USIPF zurücksetzen}}
 +
  End Sub
  
 
----
 
----
  
=== Receiver ===
+
=== Fehlercodes aus den Beispielen ===
  
;Nur Empfangen
+
Wenn ein unerwartetes Ereignis auftreten sollte, wird eine Zahl ausgegeben, hier ist deren Bedeutung dazu.
 +
Die Fehlercodes wurden aus den Beispielen von Atmel übernommen. (siehe Weblinks)
  
Der Slave wird von einem Master über eine festgelegte Adresse angesprochen, ein (oder mehrere) Byte werden zum Slave übertragen, und anschliessend die Übertragung beendet.
+
<div align="center">
 +
{| {{Blauetabelle}}
 +
|- {{Hintergrund1}}
 +
!| Code || Kurzbezeichnung || Fehlerbeschreibung
 +
|-
 +
|align="center"|02
 +
| USI_TWI_UE_START_CON || Unerwartete Start Bedingung aufgetreten
 +
|-
 +
|align="center"|03
 +
| USI_TWI_UE_STOP_CON || Unerwartete Stop Bedingung aufgetreten
 +
|-
 +
|align="center"|04
 +
| USI_TWI_UE_DATA_COL || Unerwartete Data Collision (arbitration)
 +
|-
 +
|align="center"|05
 +
| USI_TWI_NO_ACK_ON_DATA || Der Slave hat die Daten nicht per ACK quittiert
 +
|-
 +
|align="center"|06
 +
| USI_TWI_NO_ACK_ON_ADDRESS || Es hat sich kein Slave per ACK gemeldet
 +
|-
 +
|align="center"|07
 +
| USI_TWI_MISSING_START_CON || Erzeugte Start Bedingung nicht erkannt
 +
|-
 +
|align="center"|08
 +
| USI_TWI_MISSING_STOP_CON || Erzeugte Stop Bedingung nicht erkannt
 +
|}
 +
</div>
  
Das Gegenstück wäre ''Master Transmitter''.
 
  
 +
== USI-I2C-Slave ==
  
Das Beispielprogramm gibt die Bytewerte an PortC aus (aber nur die oberen 6 bit, da die beiden unteren für I2C verwendet werden), an dem beim RN-Control eine LED-Reihe angeschlossen ist. Verwendet man beim Master das Beispiel von ''Master Transmitter'', wird der Wert, den man dort eingibt am Slave Binär angezeigt.
+
Da der I2C-Master den Takt vorgibt, sind die [[TWI#Anmerkung_zur_CPU-Frequenz|Anmerkungen zur CPU-Frequenz]] zu beachten.
{{vbcomment|TWI-slave test}}
+
{{vbcomment|zum simulieren eines PCF8574}}
+
+
Do
+
Loop
+
+
End
+
  
----
+
In den Beispielen wird als Master das Board [[RN-Mega8]] mit einem [[ATMega8|Mega8]], und als Slave das [[RN-Control]] Board, mit dem Adapter auf dem der [[ATtiny2313|Tiny2313]] sitzt, verwendet. Da nur diese beiden Boards am Bus hängen, wurde ebenfalls als Slaveadresse 64 (0x40 bzw. &H40) verwendet, damit man die vorhergehenden Beispiele gleich weiterverwenden kann.
  
 +
 +
Im unten angegebenen WebLink (Nr.1) zum Forum ist ein Beispielprogramm um einen USI-I2C-Slave zu programmieren, es ist noch nicht ganz ausgereift, funktioniert aber ausreichend.
 +
 +
Im gleichen angegebenen WebLink (Nr.1) ist ein Beispiel für Portierung der '''Atmel Application Note AVR312''' "Using the USI module as a I2C slave" (siehe Link) auf Bascom. Das vollständig interruptgesteuerte Programm belegt 476Byte im Speicher eines ATtiny25.
  
 
== Siehe auch ==
 
== Siehe auch ==
Zeile 318: Zeile 568:
 
==WebLinks==
 
==WebLinks==
 
* [http://www.roboternetz.de/phpBB2/viewtopic.php?t=25058] - Thread im Forum der zu diesem hier geführt hat
 
* [http://www.roboternetz.de/phpBB2/viewtopic.php?t=25058] - Thread im Forum der zu diesem hier geführt hat
 +
* [http://www.shop.robotikhardware.de/shop/catalog/product_info.php?cPath=71&products_id=107] - DS1621 bei Robotikhardware
 
* [http://www.atmel.com/dyn/resources/prod_documents/doc2561.pdf Atmel_AVR310] - Using the USI module as a I2C master
 
* [http://www.atmel.com/dyn/resources/prod_documents/doc2561.pdf Atmel_AVR310] - Using the USI module as a I2C master
 
* [http://www.atmel.com/dyn/resources/prod_documents/doc2560.pdf Atmel_AVR312] - Using the USI module as a I2C slave
 
* [http://www.atmel.com/dyn/resources/prod_documents/doc2560.pdf Atmel_AVR312] - Using the USI module as a I2C slave
 +
* [http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=26774] - Forum-Artikel, in dem eine [[Bascom]]-Lib vorgestellt wird, um die Bascom-I2C-Befehle mit dem USI-Modul als Master zu verwenden.
  
 
= Baustelle =
 
{{Baustelle|Linux_80}}
 
  
  
Zeile 332: Zeile 581:
 
[[Kategorie:Kommunikation]]
 
[[Kategorie:Kommunikation]]
 
[[Kategorie:Microcontroller]]
 
[[Kategorie:Microcontroller]]
[[Kategorie:Elektronik]]
 
 
[[Kategorie:Praxis]]
 
[[Kategorie:Praxis]]
 
[[Kategorie:Software]]
 
[[Kategorie:Software]]
 
[[Kategorie:Quellcode Bascom]]
 
[[Kategorie:Quellcode Bascom]]

Aktuelle Version vom 21. Juni 2009, 07:16 Uhr

In diesem Artikel folgen einige Programmbeispiele, um das USI-Hardwaremodul der AVRs zu verwenden. Mit dem USI-Modul können die Schnittstellen I2C (TWI) und SPI nachgebildet werden. Näheres zu USI, I2C und SPI finden sich in den entsprechenden Artikeln. Die Beispiele sind zwar in Bascom Basic verfasst, aber so ausgeführt, dass es möglich sein sollte das Prinzip mit jeder anderen Sprache nachvollziehen zu können.


USI-I2C-Master

Beispielumgebung mit PCF8574

Ist der AVR Master, bestimmt er was und wie schnell es auf dem I2C-Bus zugeht. (Ausnahme: Clock_Stretching )

Zur Generierung der gewünschten Busgeschwindigkeit ist die Software zuständig. Die Länge der Pausen zwischen den Takten muss errechnet werden. Im Beispielprogramm für ca. 100 kHz bei 16 MHz CPU-Frequenz. Der Takt kann auch mithilfe eines Timers erzeugt werden, dies wird erst zu einem späteren Zeitpunkt mit einem Beispiel gezeigt.

In den Beispielen wird als Master das Board RN-Control mit einem Tiny2313, und als Slave ein PCF8574 verwendet, da sich dieser leicht ansteuern lässt.



Transmitter

Nur Senden

Der Master sendet einem Slave ein (oder mehrere) Byte, und schließt die Übertragung anschließend ab.

Das Gegenstück wäre Slave Receiver.


Beispielprogramm sendet ein Byte zum Slave mit Adresse 64 (0x40 bzw. &H40):

' USI-I2C Testprogramm 
' mit PCF8574 @ &H40 
'  
' ohne Interrupt und ohne Timer 
' ein Byte senden 
$regfile = "attiny2313.dat"
$crystal = 16000000
$baud = 9600

' Unterprogramme für die USI-Kommunikation 
Declare Sub Usi_twi_master_initialise()
Declare Sub Usi_twi_start_transceiver()
Declare Sub Usi_twi_master_stop()
Declare Sub Usi_twi_master_transfer()

' einige Aliases anlegen 
Pout_usi_scl Alias Portb.7
Pin_usi_scl Alias Pinb.7
Ddr_usi_scl Alias Ddrb.7
Pout_usi_sda Alias Portb.5
Pin_usi_sda Alias Pinb.5
Ddr_usi_sda Alias Ddrb.5

Dim Usi_twi_errorstate As Byte                          ' eigener Fehlerstatus 
' Array der Daten die übertragen werden 
Dim Messagebuf(4) As Byte
Dim Temp_usisr As Byte                                  ' Tempvariable für Unterprogramm 
Dim Anzahlbuf As Byte                                   ' Anzahl Zeichen die gesendet werden sollen  
Dim Cnt As Byte                                         ' Zähler 
Dim B As Byte                                           ' Zeichen von UART oder Testzeichen zum senden über USI 

' Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges. 
Const Temp_usisr_8bit = &HF0
' Prepare register value to: Clear flags, and set USI to shift 1 bit i.e. count 2 clock edges. 
Const Temp_usisr_1bit = &HFE

B = 0
Anzahlbuf = 2                                           ' in diesem Beispiel immer nur 2 Zeichen 

Waitms 300                                              ' Sicherheitspause nach Reset 

Call Usi_twi_master_initialise

Print
Print "Tiny2313 USI-TWI-Test"

Messagebuf(1) = &H40                                    ' Adresse von 8574 

Do
    ' warten bis etwas über UART kommt 
    Input B
    ' oder automatisch zählen lassen 
    ' Incr B 

    ' Den Wert zum Slave senden 
    Messagebuf(2) = Not B                               ' Not, weil LEDs gegen GND schalten 
    Call Usi_twi_start_transceiver
    Call Usi_twi_master_stop

    ' Ausgabe, damit wir sehen was geschehen ist 
    Print B ;
    Print " "
    Print Hex(usi_twi_errorstate);
    Print " " ;
    Print Hex(temp_usisr)

    Call Usi_twi_master_initialise                      ' nochmal initialisieren falls ein Fehler aufgetreten ist 

    ' Waitms 700  ' Wenn automatisch gezählt werden soll, eine kleine Pause, damit man auch was sehen kann 
Loop

End

Unterprogramm nur Senden

Die restlichen Subroutinen sind im nächsten Abschnitt zu finden.

Diese Funktion ist nur zum Versenden geeignet !

Statt dieser kann auch die Routine von unten genommen werden, die aber etwas mehr Speicherplatz benötigt.

' USI Transmit Funktion. 
Sub Usi_twi_start_transceiver()

    Usi_twi_errorstate = 0

    ' Test if any unexpected conditions have arrived prior to this execution. 
    ' Ist eine Startbedingung aufgetreten ? 
    If Usisr.7 = 1 Then                                 ' USISIF 
        Usi_twi_errorstate = &H02                       ' Usi_twi_ue_start_con 
        Exit Sub
    End If

    ' Ist eine Stopbedingung aufgetreten ? 
    If Usisr.5 = 1 Then                                 ' USIPF 
        Usi_twi_errorstate = &H03                       ' Usi_twi_ue_stop_con 
        Exit Sub
    End If

    ' Ist eine Datenkollision aufgetreten ? 
    If Usisr.4 = 1 Then                                 ' USIDC 
        Usi_twi_errorstate = &H04                       ' Usi_twi_ue_data_col 
        Exit Sub
    End If

    ' Release SCL to ensure that (repeated) Start can be performed 
    Pout_usi_scl = 1
    Waitus 5

    ' Startbedingung ausgeben 
    Pout_usi_sda = 0                                    ' Force SDA LOW. 
    Waitus 4
    Pout_usi_scl = 0                                    ' Pull SCL LOW. 
    Pout_usi_sda = 1                                    ' Release SDA. 

    ' prüfen ob die Startbedingung vom eigenen USI erkannt wurde 
    If Usisr.7 = 0 Then                                 ' USISIF 
        Usi_twi_errorstate = &H07
        Exit Sub
    End If

    ' Slaveadresse und Daten ausgeben 
    For Cnt = 1 To Anzahlbuf
        ' Ein Byte ausgeben 
        Pout_usi_scl = 0                                ' Pull SCL LOW. 
        Usidr = Messagebuf(cnt)
        Temp_usisr = Temp_usisr_8bit                    ' 8Bit ausgeben 
        Call Usi_twi_master_transfer

        ' (N)ACK vom Slave lesen 
        Ddr_usi_sda = 0                                 ' Enable SDA as input. 
        Temp_usisr = Temp_usisr_1bit                    ' 1Bit einlesen 
        Call Usi_twi_master_transfer

        ' kein ACK gekommen, Slave meldet sich nicht 
        If Temp_usisr.0 = 1 Then
            Usi_twi_errorstate = &H05                   ' Slave hat mit NACK quittiert 
            If Cnt = 1 Then
                Usi_twi_errorstate = &H06               ' Es hat sich kein Slave gemeldet 
            End If
            Exit For                                    ' hier wird die Schleife bei einem NACK verlasssen 
        End If

    Next Cnt

End Sub

Transmitter und Receiver mit DS1621 als Slave

Beispielumgebung mit DS1621
Senden und Empfangen (Repeated Start)

Im Beispiel wird ein DS1621 Temperatursensor mit I2C-Schnittstelle verwendet.
Der Master sendet dem Slave ein Kommando, stellt um auf Empfang und holt den Temperaturwert, schließt die Übertragung anschließend ab.

Der Temperaturwert wird über UART (RS232) im Klartext ausgegeben.

Die Subroutinen sind die gleichen wie für Master Transmitter. Da der DS1621 aber mit 400 kHz kommunizieren kann, wurden die Waitus zwischen den Pegelwechseln weggelassen, da die Befehle hier sowieso nicht schnell genug sind und ein Wait unnötig machen. Sie sind aber als Kommentar eingefügt, damit zu sehen ist an welchen Stellen ansonsten gewartet werden sollte (zB bei 100kHz).

Beispielprogramm holt Temperaturwert vom Slave mit Adresse 144 (0x90 bzw. &H90):
(Das Beispiel belegt 1418 Byte im Flash, das sind ca. 69% eines Tiny2313 !)

' USI-I2C Testprogramm 
' mit DS1621 @ &H90 
'  
' ohne Interrupt und ohne Timer 
' Temperatur von DS1621 lesen, und über UART ausgeben 
$regfile = "attiny2313.dat"
$crystal = 16000000
$baud = 9600

' Unterprogramme für die USI-Kommunikation 
Declare Sub Usi_twi_master_initialise()
Declare Sub Usi_twi_start_transceiver()
Declare Sub Usi_twi_master_stop()
Declare Sub Usi_twi_master_transfer()

' einige Aliases anlegen 
Pout_usi_scl Alias Portb.7
Pin_usi_scl Alias Pinb.7
Ddr_usi_scl Alias Ddrb.7
Pout_usi_sda Alias Portb.5
Pin_usi_sda Alias Pinb.5
Ddr_usi_sda Alias Ddrb.5

Dim Usi_twi_errorstate As Byte                          ' eigener Fehlerstatus 
' Array der Daten die übertragen werden 
Dim Messagebuf(4) As Byte
Dim Temp_usisr As Byte                                  ' Tempvariable für Unterprogramm 
Dim Anzahlbuf As Byte                                   ' Anzahl Zeichen die gesendet werden sollen 
Dim Cnt As Byte                                         ' Zähler 
Dim Rw As Bit                                           ' Read/Write Flag 

' Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges. 
Const Temp_usisr_8bit = &HF0
' Prepare register value to: Clear flags, and set USI to shift 1 bit i.e. count 2 clock edges. 
Const Temp_usisr_1bit = &HFE

Waitms 500                                              ' Sicherheitspause nach Reset 

Call Usi_twi_master_initialise

Dim Device As Byte
Dim Deviceread As Byte
Dim Lowtemp As Byte
Dim Hightemp As Byte

Device = &H90
Deviceread = &H91

Sound Portd.5 , 300 , 450                               ' BEEP 

Print
Print "DS1621-USI Temperatur"


' Hauptprogramm 
Do

    Messagebuf(1) = Device                              ' Adresse von DS1621 
    Messagebuf(2) = &HEE                                ' Temperaturmessung anstoßen 
    Anzahlbuf = 2                                       ' 2 Byte ausgeben 
    Call Usi_twi_start_transceiver
    ' Print Hex(usi_twi_errorstate) 

    If Usi_twi_errorstate = 0 Then                      ' kein Fehler aufgetreten 

        Call Usi_twi_master_stop
        ' Print Hex(usi_twi_errorstate) 

        ' Waitms 1 
        Messagebuf(1) = Device                          ' Adresse von DS1621 
        Messagebuf(2) = &HAA                            ' Temperaturmessung Lesekommando 
        Anzahlbuf = 2                                   ' 2 Byte ausgeben 
        Call Usi_twi_start_transceiver
        ' Print Hex(usi_twi_errorstate) 
        ' kein STOP an dieser Stelle ! 
        ' es folgt ein Repeated START ! 

        If Usi_twi_errorstate = 0 Then                  ' kein Fehler aufgetreten 
            ' Temperatur lesen 
            Messagebuf(1) = Deviceread                  ' Adresse von DS1621 
            Messagebuf(2) = 0
            Messagebuf(3) = 0
            Anzahlbuf = 3                               ' 3 Byte ausgeben, bzw. einlesen 
            Call Usi_twi_start_transceiver
            ' Print Hex(usi_twi_errorstate) 

            If Usi_twi_errorstate = 0 Then              ' kein Fehler aufgetreten 

                Call Usi_twi_master_stop
                ' Print Hex(usi_twi_errorstate) 

                ' Gelesenen Bytes stehen ab Position 2 im Array 
                Lowtemp = Messagebuf(2)
                Hightemp = Messagebuf(3)

                ' Negativer Wert ? 
                If Lowtemp.7 = 1 Then
                    Lowtemp = Not Lowtemp
                    ' .5 ? 
                    If &H80 > Hightemp Then
                        Incr Lowtemp
                    End If
                    Print "-" ;
                Else
                    Print " " ;
                End If

                Print Lowtemp ; "," ;

                If Hightemp = &H80 Then
                    Print "5"
                Else
                    Print "0"
                End If

            End If
        End If
    End If

    If Usi_twi_errorstate <> 0 Then                     ' bei einem Fehler den Fehlercode ausgeben 
        Print "Error " ; Hex(usi_twi_errorstate)
    End If

    ' nochmal initialisieren falls ein Fehler aufgetreten ist 
    Call Usi_twi_master_initialise

    Waitms 2000
Loop

End


Unterprogramme für USI-Kommunikation

Hier folgen die Unterprogramme für die USI-I2C-Master Kommunikation

Initialisierung

USI-I2C Pins Initialisieren, und USI-Register setzen für I2C-Master.

' USI TWI single master initialization function 
Sub Usi_twi_master_initialise()
    ' Direction Out 
    Ddr_usi_scl = 1
    Ddr_usi_sda = 1
    ' Release SCL & SDA 
    Pout_usi_scl = 1
    Pout_usi_sda = 1

    ' Preload dataregister with "released level" data. 
    Usidr = &HFF

    ' Disable USI Interrupts. 
    ' Set USI in Two-wire mode. 
    ' Software stobe as counter clock source 
    Usicr = &B00101010

    ' Statusflags löschen, und Counter auf 0 zurücksetzen. 
    Usisr = &B11110000

End Sub

Senden/Empfangen

Startbedingung ausgeben,
versenden der Anzahl der angegebenen Bytes,
und einen Status zurückgeben, ob die Daten angenommen (ACK) wurden.

' USI Transmit und Receive Funktion. 
Sub Usi_twi_start_transceiver()

    Usi_twi_errorstate = 0

    ' Test if any unexpected conditions have arrived prior to this execution. 
    ' Ist eine Startbedingung aufgetreten ? 
    If Usisr.7 = 1 Then                                 ' USISIF 
        Usi_twi_errorstate = &H02                       ' Usi_twi_ue_start_con 
        Exit Sub
    End If

    ' Ist eine Stopbedingung aufgetreten ? 
    If Usisr.5 = 1 Then                                 ' USIPF 
        Usi_twi_errorstate = &H03                       ' Usi_twi_ue_stop_con 
        Exit Sub
    End If

    ' Ist eine Datenkollision aufgetreten ? 
    If Usisr.4 = 1 Then                                 ' USIDC 
        Usi_twi_errorstate = &H04                       ' Usi_twi_ue_data_col 
        Exit Sub
    End If

    ' Release SCL to ensure that (repeated) Start can be performed 
    Pout_usi_scl = 1
    Waitus 1                                            ' 5 

    ' Generate Start Condition 
    Pout_usi_sda = 0                                    ' Force SDA LOW. 
    ' Waitus 4 
    Pout_usi_scl = 0                                    ' Pull SCL LOW. 
    Pout_usi_sda = 1                                    ' Release SDA. 

    ' prüfen ob die Startsequenz vom eigenen USI erkannt wurde 
    If Usisr.7 = 0 Then                                 ' USISIF 
        Usi_twi_errorstate = &H07
        Exit Sub
    End If

    ' RW-Bit : Messagebuf(1).0 
    Rw = Messagebuf(1).0

    ' Write address and Read/Write data 
    For Cnt = 1 To Anzahlbuf
        ' SlaveAdresse immer Write, sonst auf R/W prüfen 
        If Cnt = 1 Or Rw = 0 Then
            ' Write a byte 
            Pout_usi_scl = 0                            ' Pull SCL LOW. 
            Usidr = Messagebuf(cnt)
            Temp_usisr = Temp_usisr_8bit
            Call Usi_twi_master_transfer

            ' Clock and verify (N)ACK from slave 
            Ddr_usi_sda = 0                             ' Enable SDA as input. 
            Temp_usisr = Temp_usisr_1bit
            Call Usi_twi_master_transfer

            ' kein ACK gekommen 
            If Temp_usisr.0 = 1 Then
                Usi_twi_errorstate = &H05               ' Der Slave hat die Daten mit NACK quittiert 
                If Cnt = 1 Then
                    Usi_twi_errorstate = &H06           ' Es hat sich kein Slave gemeldet 
                End If
                Exit For                                ' die Schleife bei einem Fehler verlasssen 
            End If
        Else
            ' Read a Byte 
            Ddr_usi_sda = 0                             ' Enable SDA as input. 
            Temp_usisr = Temp_usisr_8bit
            Call Usi_twi_master_transfer

            Messagebuf(cnt) = Temp_usisr                ' Empfangenes Byte ins Array 

            ' Prepare to generate ACK (or NACK in case of End Of Transmission) 
            If Cnt = Anzahlbuf Then
                Usidr = &HFF                            ' Load NACK to confirm End Of Transmission. 
            Else
                Usidr = &H00                            ' Load ACK. Set data register bit 7 (output for SDA) low. 
            End If

            Temp_usisr = Temp_usisr_1bit
            Call Usi_twi_master_transfer                ' ACK oder NACK senden 

        End If
    Next Cnt

End Sub

Ein Byte ausgeben/einlesen

Diese Routine gibt die Anzahl Bit aus, wie anhand der Variablen Temp_usisr eingestellt wurde, bzw. es wird damit die Anzahl Takte angegeben, die der Counter des USI-Moduls zählt bevor er überläuft, und das Flag USIOIF im Status-Register USISR gesetzt wird.

Getaktet wird hier per Software, pro Bit zwei Takte. Bei jedem Takt ändert sich der Zustand (High/Low) von SCL.

Die Daten, die ausgegeben werden sollen, müssen vor dem Aufruf dieser Routine in das Datenregister USIDR geschrieben werden.

Ob ein Bit ausgegeben oder gelesen wird, wird vor dem Aufruf im Datenrichtungsregister der Datenleitung SDA eingestellt. Im Beispiel mit dem Alias Ddr_usi_sda.

Das gelesene Byte von USIDR wird in die Variable Temp_usisr geschrieben, und kann vom Hauptprogramm weiterverarbeitet werden.

Sub Usi_twi_master_transfer()
    Usisr = Temp_usisr

    Temp_usisr = &B00101011                             ' for Toggle Clock Port. 

    Do
        ' Waitus 5 
        Usicr = Temp_usisr                              ' SCL takten 
        ' Wait for SCL to go high. 
        While Pin_usi_scl = 0
        Wend

        ' Waitus 4 
        Usicr = Temp_usisr                              ' SCL takten 

    Loop Until Usisr.6 = 1                              ' USIOIF, Wenn der Zähler überläuft, sind alle Bits versendet. 

    ' Waitus 5 
    Temp_usisr = Usidr                                  ' Daten aus USI-Datenregister lesen. 
    Usidr = &HFF                                        ' Release SDA. 

    Ddr_usi_sda = 1                                     ' Enable SDA as output. 

End Sub

STOP-Bedingung ausgeben

STOP-Bedingung ausgeben, und prüfen ob dies vom eigenen Modul erkannt wurde.
Status zurückgeben, ob die STOP-Bedingung erfolgreich ausgegeben wurde.

' Function for generating a TWI Stop Condition. Used to release the TWI bus. 
Sub Usi_twi_master_stop()

    Pout_usi_sda = 0                                    ' Pull SDA LOW. 
    Pout_usi_scl = 1                                    ' Release SCL. 

    ' Wait for SCL to go high. 
    While Pin_usi_scl = 0
    Wend
    ' Waitus 4 
    Pout_usi_sda = 1                                    ' Release SDA. 
    Waitus 1                                            ' 5 

    ' prüfen ob die Stopsequenz vom eigenen USI erkannt wurde 
    If Usisr.5 = 0 Then                                 ' USIPF 
        Usi_twi_errorstate = &H08
'        Exit Sub                 ' Exit nicht nötig, da schon das Ende erreicht 
    End If

    Usisr.5 = 1                                         ' USIPF zurücksetzen 
End Sub

Fehlercodes aus den Beispielen

Wenn ein unerwartetes Ereignis auftreten sollte, wird eine Zahl ausgegeben, hier ist deren Bedeutung dazu. Die Fehlercodes wurden aus den Beispielen von Atmel übernommen. (siehe Weblinks)

Code Kurzbezeichnung Fehlerbeschreibung
02 USI_TWI_UE_START_CON Unerwartete Start Bedingung aufgetreten
03 USI_TWI_UE_STOP_CON Unerwartete Stop Bedingung aufgetreten
04 USI_TWI_UE_DATA_COL Unerwartete Data Collision (arbitration)
05 USI_TWI_NO_ACK_ON_DATA Der Slave hat die Daten nicht per ACK quittiert
06 USI_TWI_NO_ACK_ON_ADDRESS Es hat sich kein Slave per ACK gemeldet
07 USI_TWI_MISSING_START_CON Erzeugte Start Bedingung nicht erkannt
08 USI_TWI_MISSING_STOP_CON Erzeugte Stop Bedingung nicht erkannt


USI-I2C-Slave

Da der I2C-Master den Takt vorgibt, sind die Anmerkungen zur CPU-Frequenz zu beachten.

In den Beispielen wird als Master das Board RN-Mega8 mit einem Mega8, und als Slave das RN-Control Board, mit dem Adapter auf dem der Tiny2313 sitzt, verwendet. Da nur diese beiden Boards am Bus hängen, wurde ebenfalls als Slaveadresse 64 (0x40 bzw. &H40) verwendet, damit man die vorhergehenden Beispiele gleich weiterverwenden kann.


Im unten angegebenen WebLink (Nr.1) zum Forum ist ein Beispielprogramm um einen USI-I2C-Slave zu programmieren, es ist noch nicht ganz ausgereift, funktioniert aber ausreichend.

Im gleichen angegebenen WebLink (Nr.1) ist ein Beispiel für Portierung der Atmel Application Note AVR312 "Using the USI module as a I2C slave" (siehe Link) auf Bascom. Das vollständig interruptgesteuerte Programm belegt 476Byte im Speicher eines ATtiny25.

Siehe auch

  • Avr - Infos zu AVR allgemein
  • I2C - Details zu I2C
  • TWI - Two-wire Serial Interface
  • SPI - Serial Peripheral Interface
  • TWI Praxis - Bascom-Beispiele für Hardware TWI
  • TWI Praxis Multimaster - Bascom-Beispiele für Hardware TWI


WebLinks

  • [1] - Thread im Forum der zu diesem hier geführt hat
  • [2] - DS1621 bei Robotikhardware
  • Atmel_AVR310 - Using the USI module as a I2C master
  • Atmel_AVR312 - Using the USI module as a I2C slave
  • [3] - Forum-Artikel, in dem eine Bascom-Lib vorgestellt wird, um die Bascom-I2C-Befehle mit dem USI-Modul als Master zu verwenden.


--Linux 80 02:11, 26. Nov 2006 (CET)


LiFePO4 Speicher Test