Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


Grundsätzlich ist ja im Artikel TWI Praxis alles dargestellt, was der Mensch braucht, um die Hardware TWI eines AVR zu verwenden.

  • Wenn "Multimaster" lediglich darin besteht, daß sich mehrere Master die I2C-Peripherie gewissermaßen teilen, reicht das auch völlig, da das TWI-Modul des Avr immer automatisch wartet, bis der Bus frei ist.
  • Im Multimasterbetrieb mit gegenseitigem Zugriff treten allerdings Situationen auf, die die Sache dann leicht ins Stocken bringen

Konflikte

Bus Lost

Die TWI-Hardware verfolgt ja immer den Bus, und wenn ein Master bei einem gerade besetzten Bus ein START absetzen will, wird einfach gewartet. Innerhalb eines kurzen Zeit-Fensters wird das allerdings zu spät bemerkt, da tritt dann die "Arbitrierung" in Kraft (ganz genau steht das im Datenblatt). Irgendwann merkt ein Master, daß ein anderer auch gerade Daten sendet und bricht dann seine eigene Übertragung ab. Status: Irgendeine Variante mit "Bus Lost".

Das einzig Sinnvolle ist es dann zu warten, bis der andere seine Übertragung beendet hat, und es dann ganz einfach wieder zu versuchen. Genauso ist es, wenn andere Störungen aufgetreten sind (Es reicht völlig, wenn ein anderer µC mit den Bascom-I2C Soft-Funktionen einfach irgendwann zu senden beginnt).

Deadlock

Wenn so ein Multimaster gleichzeitig auch als Slave fungiert, kann aber auch ein Deadlock entstehen. Wenn wir einfach eine Sendeschleife definieren, bis wir mit unserer (Master) Sendung durchgekommen sind, und zur selben Zeit will ausgerechnet der Master, den wir gerade adressieren, mit der gleichen Strategie auf UNS zugreifen, finden beide Prozessoren kein Ende in ihrer Schleife. Jeder wartet, bis der andere endlich zuhört.

In diesem Fall müssen wir den Sendeversuch eigentlich abbrechen und erstmal schauen, ob wir nicht selbst adressiert worden sind. Wenn ja, müssen wir erstmal das verarbeiten, was wir gekriegt haben und erst dann können wir nochmal einen Sendeversuch starten.

MyTWI.LIB

Diese Bascom-Library versucht nun, diese Konflikte soweit abzuhandeln, daß der User nicht allzuviel damit konfrontiert ist. Zu dieser Library gehört eine Include-Datei MyTWI.BAS, die die Library, die Externals und einige Felder und Konstanten definiert. Die vollständigen Originale können runtergeladen werden

MyTWI.BAS

$lib "MyTWI.LIB"

$external Twi_sense
$external Mst_start
$external Mst_stop
$external Twi_mst_slv_isr

Const Twi_m_sent = 1
Const Twi_m_recv = 2
Const Twi_timeout = 3
Const Twi_m_busy = 4
Const Twi_buserr = 7

Felder für Master Transmitter/Receiver

'-------------------------------------
'  twi-STRUCTURE MASTER
'-------------------------------------
Dim Twi_mst_flag As Byte                          '0     Kontroll-flags
Dim Twi_mst_addr As Byte                          '1     SLA + R/W

Dim Twi_mt_curr As Byte                           '0     resv
Dim Twi_mt_data As Word                           '1/2   Data address
Dim Twi_mt_cntr As Byte                           '3     anzahl bytes

Dim Twi_mr_curr As Byte                           '0     resv
Dim Twi_mr_data As Word                           '1/2   Data address
Dim Twi_mr_cntr As Byte                           '3     anzahl bytes

Felder für Slave Receiver/Transmitter

'-------------------------------------
'  twi-STRUCTURE SLAVE RECV / TRANS
'-------------------------------------
Dim Twi_slv_flag As Byte                          '0     Kontroll-flags
Dim Twi_slv_addr As Byte                          '1     slave adresse
Dim Twi_slv_stat As Byte                          '2     twi-state (SLA)

Dim Twi_sr_cntr As Byte                           '0     anzahl bytes
Dim Twi_sr_data As Word                           '1/2   Data-address

Dim Twi_st_cntr As Byte                           '0     anzahl bytes
Dim Twi_st_data As Word                           '1/2   Data-address

Wofür die Felder dienen, ist im Demoprogramm am besten zu verstehen

M32Slave.BAS

Das ist das "Haupt" Programm. Der Timer0 hat mit den TWI-Funktionen nichts zu tun. Er soll nur jede Sekunde eine TWI-Master-Sendung auslösen.

Definitionen

$regfile = "m32def.dat"       'das muß man für sich anpassen, logo
$crystal = 8000000            'detto

$include "MyTWI.bas"          

$baud = 9600                  'natürlich beliebig

$hwstack = 64                 'Wegen der Interrupts + Reserve


Const Tmr_c_prescale = 64
Const Tmr_c_preload = 131
Const Tmr_c_divis = 250

'-------------------------------------------------
Config Timer0 = Timer , Prescale = Tmr_c_prescale           'Timer 1mS
On Timer0 Interrupt_ticker                                  ' Timer for Timer Queue

Dim Timeout As Byte
Dim Timediv As Word
'-------------------------------------------------

Declare Sub Twi_show_state(byref State As Byte)            'siehe weiter unten

Adressen

Die verwendeten I2C-Adressen müß man natürlich auch auswählen

'-------------------------------------------------
'  Die eigene I2C Adresse   (als Slave)
'-------------------------------------------------
Const Mn1_adr = &H6A

'-------------------------------------------------
'  fremde I2C Adresse   (als Master)
'-------------------------------------------------
Const Mn2_adr = &H6E

Setup als Slave

Dim Twi_slv_buff(24) As Byte                                'Buffer für Slavefunktionen

      Twi_slv_addr = Mn1_adr                                'lokale I2C-Adresse
      Twi_sr_data = Varptr(twi_slv_buff(1))                 'datenbuffer empfang
      Twi_st_data = Varptr(twi_slv_buff(1))                 'datenbuffer senden

      Twar = Twi_slv_addr + 1                               'I2C Adress Mega32 + GCA
      Config Twi = 400000                                   'I2C Speed wahlweise

      On Twi Twi_mst_slv_isr , Nosave                       'ISR f. TWI

      Gosub Twi_sense                                       'Aktivieren Slave-Funktion

      Enable Timer0                                         'Timer
      Enable Interrupts                                     'Generell

  • Anmerkung: Wenn man nur
     Twar = Twi_slv_addr     

schreibt, wird ein GCA NICHT erkannt.

Hauptschleife

Ob man als Slave angesprochen wurde, muß man natürlich periodisch abfragen und quittieren. Lange "WAIT" sind also nicht günstig

Hauptschleife Beginn

   Do

Wer hätte das gedacht ?

Hauptschleife Slave-Flag abfragen

      If Twi_slv_flag <> 0 Then
'-----------------------------------------------------------------
'             Vorsicht, der Bus ist solange blockiert
'-----------------------------------------------------------------
         Select Case Twi_slv_flag
         Case &H60 : Print Spc(30) ; "SLAVE recv:" ;        'es ist was empfangen worden
                  Print Hex(twi_slv_addr) ; " ";
                  For Temp = 1 To Twi_sr_cntr
                     Print Hex(twi_slv_buff(temp));         'print der Daten
                  Next
         Case &HA8 : Print Spc(30) ; "SLAVE tran:" ;        'es ist was abgeholt worden
                  Print Hex(twi_slv_addr) ; " ";
                  For Temp = 1 To Twi_st_cntr
                     Print Hex(twi_slv_buff(temp));        'print der Daten
                  Next
         Case &H70 : Print Spc(30) ; "SLAVE GCA :" ;        'ein General Call ist gekommen
                  Print Hex(twi_slv_addr) ; " ";
                  For Temp = 1 To Twi_sr_cntr
                     Print Hex(twi_slv_buff(temp));         'print der Daten
                  Next
         Case Else:
                  Print Chr(7) ;                            'Irgendein Fehler
                  Call Twi_show_state(twi_slv_flag)         'Print status-text
         End Select
         Print
         Twi_slv_flag = 0                                   'quittieren
         Twi_slv_stat = 0                                   '
         Gosub Twi_sense                                    'Slave wieder scharfmachen
                                                            'und Bus freigeben
      End If

Der Kern: Slave auswerten

  • Twi_slv_flag =
    • 0 Keine Slave Aktion hat stattgefunden
    • &H60 Daten wurden empfangen. Sie stehen im Buffer mit der Länge Twi_sr_cntr
    • &HA8 Daten wurden gesendet. Sie stehen im Buffer mit der Länge Twi_st_cntr
    • &H70 GCA-Daten wurden empfangen.
    • else Irgendein Fehler ist gewesen. Irgendeine Reaktion ist aber nicht notwendig.

Der Kern: Slave quittieren und wieder enablen

Twi_slv_flag = 0            
Twi_slv_stat = 0            
Gosub Twi_sense                                
  • Anmerkung: Die Demo geht natürlich bei "Slave Transmitter" ein wenig an der Realität vorbei. Normalerweiser wird ja erst in "Slave Receiver" festgelegt, welche Daten überhaupt zu senden sind. Daher ist eine nachträgliche Auswertung ja sinnlos. Bestenfalls als Bestätigung, daß sie der fremde Master auch tatsächlich geholt hat.



Hauptschleife wechseln auf Master

Das ist jetzt reine Demo

'-----------------------------------------------------------------
'         Jede Sekunde was senden & empfangen
'-----------------------------------------------------------------
      If Timeout = 1 Then
        Gosub Master_transmit                               'Senden in einer SUB
        If Twi_mst_flag = 0 Then                            'Hats geklappt ?
            Timeout = 0                                     'ja
'       else (sonst probieren wir's gleich nochmal)
         End If
      End If
'-----------------------------------------------------------------

Hauptschleife Ende

   Loop
End

Sende-SUB

'----------------------------------------
'   Beispiel 8 byte senden,
'     dann mit Rep.Start von gleicher adresse 3 Byte empfangen
'----------------------------------------
Master_transmit:
' sende buffer (mit testdaten) füllen
   For Temp = 1 To 24
      Twi_mst_buff(temp) = Slv_byte
   Next

   Twi_mst_addr = Mn2_adr                                   'I2C adresse ZIEL
   Twi_mt_cntr = 8                                          '  8 Byte senden
   Twi_mt_data = Varptr(twi_mst_buff(1))                    ' Daten

   Twi_mr_cntr = 3                                          ' dann 3 Byte empfangen
   Twi_mr_data = Varptr(twi_mst_buff(1))                    ' empfangsbuffer

   Gosub Run_wait_master                                    ' noch eine SUB

   Return
  • Anmerkung:

Nur senden

   Twi_mst_addr = Mn2_adr                                   'I2C adresse ZIEL
   Twi_mt_cntr = 8                                          '  8 Byte senden
   Twi_mt_data = Varptr(twi_mst_buff(1))                    ' Daten
   Twi_mr_cntr = 0                                  

Nur abholen

   Twi_mst_addr = Mn2_adr + 1                               'I2C adresse ZIEL (+R)
   Twi_mr_cntr = nn                                         ' wieviele Byte
   Twi_mr_data = Varptr(twi_mst_buff(1))                    ' empfangsbuffer


Durchführen der Master-Funktion

Run_wait_master:
      Gosub Mst_start                                       'aufruf LIBRARY
      Select Case Twi_mst_flag                              'ergebnis ?
      Case Twi_m_sent:                                      'gesendet
               Print "<<<<<<MASTER sent:" ;
               Print Hex(twi_mst_addr) ; " ";
               For Temp = 1 To Twi_mt_cntr
                     Print Hex(twi_mst_buff(temp));
               Next
               Print
               Twi_mst_flag = 0
      Case Twi_m_recv:                                      'geholt
                Print ">>>>>>MASTER read:" ;
               Print Hex(twi_mst_addr) ; " ";
               For Temp = 1 To Twi_mr_cntr
                     Print Hex(twi_mst_buff(temp));
               Next
               Print
               Twi_mst_flag = 0
      Case Else:                                            'irgendein Problem
                Print Chr(7) ; Hex(twi_mst_addr) ; " ";
                Call Twi_show_state(twi_mst_flag)
      End Select
   Return
  • Anmerkung: Auch das ist nur DEMO. Auswerten sollte man eventuell die Probleme, denn die bedeuten, daß das Ganze fehlgeschlagen ist, entweder, weil man gerade selbst adressiert wurde, oder weil eben irgendwas auf dem Bus passiert ist, was die Library-Funktion nicht abhandeln konnte.

TWI_Show_State.BAS

Das ist ein unabhängiger Programmteil, der einfach den TWI Status mit Text über "PRINT" ausgibt. Für den Betrieb ist das nicht notwendig, er verbraucht wegen der Texte doch einigen Platz.

'----------------------------------------
'
'----------------------------------------
Sub Twi_show_state(byref State As Byte)
   Print Hex(state);
   Select Case State
   Case &H08 : Print " Start "
   Case &H10 : Print " Start Rep "
   Case &HA0 : Print " Stop / Rep Start  "
   Case &HA8 : Print " Sla R Received Acked "
   Case &H60 : Print " Sla W Received Acked "
   Case &H70 : Print " Gca Received Acked "
   Case &H80 : Print " SR Sla Data Received Acked "
   Case &H90 : Print " SR Gca Data Received Acked "
   Case &H88 : Print " SR Sla Data Received Not Acked "
   Case &H98 : Print " SR Gca Data Received Not Acked "
   Case &HB8 : Print " ST Data Transmitted Acked "
   Case &HC0 : Print " ST Data Transmitted Not Acked "
   Case &HC8 : Print " ST Last Data Transm Acked "
   Case &H18 : Print " MT Sla W Transmitted Acked "
   Case &H40 : Print " MT Sla R Transmitted Acked "
   Case &H20 : Print " MT Sla W Transmitted Not Acked"
   Case &H48 : Print " MT Sla R Transmitted Not Acked "
   Case &H38 : Print " MT Lost Or Sla R / W Not Acked "
   Case &H68 : Print " MT Lost / Sla W Received Acked "
   Case &H78 : Print " MT Lost / Gca Received Acked "
   Case &HB0 : Print " MT Lost / Sla R Received Acked "
   Case &H28 : Print " MT Data Transmitted Acked "
   Case &H30 : Print " MT Data Transmitted No Acked "
   Case &H50 : Print " MT Data Received Acked "
   Case &H58 : Print " MT Data Received Not Acked "
   Case Twi_timeout : Print " M  Timeout"
   Case Twi_m_busy : Print " TWI Busy"
   Case Twi_buserr : Print " Bus Error"
   Case Else : Print " ??"
   End Select
End Sub

Demo-Programm

  • m32slave.bas demo/testprogramm
  • twi_show_state.bas Twi-Status printen
  • mytwi.bas includefile für die library
  • mytwi.lib Bascom library, gehört natürlich ins Bascom LIB directory

Funktionen der Demo

  • Das Demo definiert sich als Slave und sendet oder empfängt auf diese Weise Daten.
  • Jede Sekunde aber sendet er selbst als Master Daten an einen anderen I2C-Slave und holt dann gleich auch Daten von dort ab

Test

Getestet hab ich das im Kreuz-und Quer-Dauerbetrieb mit 3 Master/Slaves (2 x Atmega32, 1x AT90S2313 und den drei PCFs von der RNBFRA-Karte. Die Busgeschwindigkeit ist mit 400 kHz recht günstig, da dadurch der Bus jweils nicht solange besetzt ist. Definitionsgemäß ist es nicht notwendig, daß alle Teilnehmer die gleiche Speed haben.

Vorsicht beim 2313: Wenn der mit den normalen Bascom I2C Funktionen sendet, benimmt er sich 
wie ein absoluter Terrorist, da er sich um nichts kümmert.

Wenn der mitspielen soll, dann bitte Bascom_Soft-I2c_Library verwenden.

Autor

Benutzer:PicNick

Siehe auch

WebLinks / Download