PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : TWI (Übertragung mehrer Bytes) Code + Fragen



hdtvfreak
23.12.2008, 23:09
Hallo,

ich bin dabei eine Wandfackel zu konstruieren, die sich über den I2C bzw. TWI steuern lässt. Um die verschiedenen Modi auszuwählen ist es teilweise nötig meherer Bytes zu übertragen.

Beispiel: Lichtfarbe + Blinkfrequenz im Stroboskopmodus.

Zu diesem Zweck habe ich mich in die RN Artikel zu den Thema eingelesen und auch einen funktionsfähigen Code geschrieben bzw. umgeschrieben:

TWI Slave:

' Wandfackel Bacom

$regfile = "m16def.dat" ' the used chip
$crystal = 8000000 ' frequency used
' $baud = 9600 ' keine baud rate angeben !

Config Timer2 = Timer , Prescale = 1024
Config Timer1 = Pwm , Pwm = 8 , Compare A Pwm = Clear Down , Compare B Pwm = Clear Down , Prescale = 64
'Die Timer starten

On Timer2 Timer_ir

Enable Interrupts
Enable Timer1
Enable Timer2

Start Timer1
Start Timer2

Timer2 = 247

Dim Temp As Byte

Dim Twi_control As Byte ' Controlregister lokale kopie
Dim Twi_status As Byte
Dim Twi_data As Byte

Dim Count As Byte ' Testwert, jedes mal +1
Dim Timer_preload As Byte

Dim Control As Byte ' Flag, bestimmt die aktuelle Aktion
Dim Color_management As Byte

Red Alias Pwm1a
Green Alias Pwm1a
Blue Alias Pwm1a

'################################################# ##############################

'Abschnitt linear Fader
Dim Linfade_count As Byte
Dim Linfade_waitms As Byte
Dim Linfade_counter As Byte

'Default Settings
Linfade_count = 255 ' VCC/255 Helligkeitsstufen
Linfade_waitms = 5 'Wartet nach jeder Helligkeitsstufe 5 ms


'Abschnitt Zufallsfader

Declare Sub Cycle(farbe_start As Byte , Farbe_end As Byte)
Dim I As Byte 'Zaehlvariable
Dim Indfade_area As Byte 'Zaehlvariable
Dim Indfade_waitms As Byte 'Warteschleife
Dim Farbe_out As Byte
Dim Indfade_begin As Byte 'Anfangsfarbe
Dim Indfade_end As Byte 'endfarbe

'Default Settings
Indfade_area = 255 ' Anzahl der PWM Abstufungen da 8 bit nur 255 Abstufungen mglich
Indfade_waitms = 5 'Pause zwischen den einzelnen Schritten (in mS)


'Abschnitt Stroboskop

Dim Strobo_ontime As Byte
Dim Strobo_offtime As Byte
Dim Strobo_helligkeit As Byte

'Default Settings

Strobo_helligkeit = 255
Strobo_ontime = 50
Strobo_offtime = 100

'Dimmen

Dim Dim_helligkeit As Byte

'Default Settings

Dim_helligkeit = 126

'################################################# ##############################


Declare Sub Twi_init_slave

' Werte zurcksetzen
Count = 0
Twi_data = 0
Call Twi_init_slave ' TWI aktivieren
Timer_preload = 247

Color_management = 1
' Hauptschleife
Do
Select Case Control 'Fader aktivieren: aus-->an-->aus-->an usw.

' Linearfader
Case 1: '255: 100 %
For Linfade_counter = 0 To Linfade_count Step 1 '0 : 0 %
Select Case Color_management
Case 1 : Red = Linfade_counter
Case 2 : Green = Linfade_counter
Case 3 : Blue = Linfade_counter
End Select
Waitms Linfade_waitms
Next

For Linfade_counter = Linfade_count To 0 Step -1
Select Case Color_management
Case 1 : Red = Linfade_counter
Case 2 : Green = Linfade_counter
Case 3 : Blue = Linfade_counter
End Select
Waitms Linfade_waitms
Next

' Zufallsfader
Case 2:
Indfade_begin = Indfade_end 'Startfarbe ist immer die alte Zielfarbe
Indfade_end = Rnd(indfade_area) 'Zielfarbe ist immer Zufall

For I = 0 To Indfade_area
Call Cycle(indfade_begin , Indfade_end)
Select Case Color_management
Case 1 : Red = Farbe_out
Case 2 : Green = Farbe_out
Case 3 : Blue = Farbe_out
End Select
Waitms Indfade_waitms
Next

'Stroboskop
Case 3:
Select Case Color_management
Case 1 : Red = 0
Case 2 : Green = 0
Case 3 : Blue = 0
End Select
Waitms Strobo_ontime
Select Case Color_management
Case 1 : Red = Strobo_helligkeit
Case 2 : Green = Strobo_helligkeit
Case 3 : Blue = Strobo_helligkeit
End Select
Waitms Strobo_offtime

'Anschalten
Case 4:
Select Case Color_management
Case 1 : Red = 0
Case 2 : Green = 0
Case 3 : Blue = 0
End Select

'Ausschalten
Case 5:
Select Case Color_management
Case 1 : Red = 255
Case 2 : Green = 255
Case 3 : Blue = 255
End Select

'Helligkeit Dimmen
Case 6:
Select Case Color_management
Case 1 : Red = Dim_helligkeit
Case 2 : Green = Dim_helligkeit
Case 3 : Blue = Dim_helligkeit
End Select

End Select

Loop

End


' Unterprogramme


' TWI als slave aktivieren
Sub Twi_init_slave
Twsr = 0 ' status und Prescaler auf 0
Twdr = &HFF ' default
Twar = 11 ' Slaveadresse setzen
Twcr = &B01000100 ' TWI aktivieren, ACK einschalten

End Sub


Timer_ir:
Timer1 = Timer_preload

' schauen ob TWINT gesetzt ist
Twi_control = Twcr And &H80 ' Bit7 von Controlregister 10000000

If Twi_control = &H80 Then '10000000
Twi_status = Twsr And &HF8 ' Status /11111000

' wurde ein Byte geschickt
If Twi_status = &H80 Or Twi_status = &H88 Then '10000000 // 10001000
Twi_data = Twdr ' neue Daten merken

If Twi_data = 1 Then
Control = 2
Twi_data = 0
End If

If Twi_data = 4 Then
Control = 5
Twi_data = 0
End If

End If

' TWINT muss immer gelscht werden, damit es auf dem Bus weiter geht
Twcr = &B11000100 ' TWINT lschen, mit ACK
End If

Return


'Farbverlauf
Sub Cycle(farbe_start As Byte , Farbe_end As Bit Byte)

Local Temp1 As Single
Local Temp2 As Single

Temp1 = I / Indfade_area
Temp2 = Temp1
Temp1 = 1 - Temp1

Temp1 = Temp1 * Farbe_start
Temp2 = Temp2 * Farbe_end
Farbe_out = Temp1 + Temp2

End Sub

TWI Master:


$regfile = "M32def.dat" ' the used chip
$crystal = 8000000 ' frequency used
$baud = 9600 ' baud rate


'I2C Read and Write

Declare Function Twi_read_byte(byval Slave As Byte) As Byte
Declare Sub Twi_send_byte(byval Slave As Byte , Zeichen As Byte)

'Twi Variablen

Dim Twi_control As Byte ' Controlregister lokale kopie
Dim Twi_status As Byte
Dim Twi_data As Byte


'Variablen fr Main loop

Dim B As Byte ' Zeichen von UART
Dim X As Byte
Dim Error As Byte ' Fehlermerker
Dim F As Byte
Dim Id As Byte
Dim Action As Byte
Dim I2csent As Byte

Dim Flag As Byte


' TWI Init
Twcr = &B00000100 ' erstmal nur TWI aktivieren
Twsr = 0 ' Status und Prescaler Register
Twbr = 32 ' Bit Rate Register, 100kHz


'################################################# #############################
'##################### Main Loop ##############################################
'################################################# #############################



Do


' Eingang Rs232

B = Inkey()


If B = 49 Then
Print 99
End If

If B = 50 Then
Action = 1
Call Twi_send_byte(11 , Action)
End If


If B = 51 Then
Action = 11
Call Twi_send_byte(11 , Action)
End If


If B = 52 Then
Action = 12
Call Twi_send_byte(11 , Action)
End If


If B = 53 Then
Action = 13
Call Twi_send_byte(11 , Action)
End If

Loop


End



'################################################# #############################
'##################### Twi send_byte ##########################################
'################################################# #############################

' sendet ein Byte und schliesst die bertragung ab
Sub Twi_send_byte(byval Slave As Byte , Zeichen As Byte)
Error = 0 ' Fehler zurcksetzen

' Startbedingung
Twcr = &B10100100 ' TWINT

' warten bis TWINT gesetzt ist
Gosub Twi_wait_int

'################# Slave Adresse Senden ########################################

' wenn Zugriff auf den Bus erlaubt, Slaveadresse ausgeben
If Twi_status = &H08 Or Twi_status = &H10 Then
Twdr = Slave And &HFE ' slave adresse + Write
Twcr = &B10000100 ' TWINT lschen, Byte senden

' warten bis TWINT gesetzt ist
Gosub Twi_wait_int


'##################### Daten Senden ############################################

' Slave hat sich gemeldet
If Twi_status = &H18 Or Twi_status = &H20 Then
Twdr = 1 ' Daten
Twcr = &B10000100 ' TWINT lschen, Byte senden

' warten bis TWINT gesetzt ist
Gosub Twi_wait_int

' Zeichen wurden gesendet
If Twi_status = &H28 Or Twi_status = &H30 Then
Error = 0
Flag = 1 ' kein Fehler
Else
Error = Twi_status ' Fehler
End If

Else
' kein slave
Error = Twi_status ' Fehler
End If

If Flag = 1 Then 'falls daten erfolgreich gesendet

Twdr = 4 ' Daten
Twcr = &B10000100 ' TWINT lschen, Byte senden ' Wurde das erste Byte erfolgreich bertragen

Gosub Twi_wait_int

' Zeichen wurden gesendet
If Twi_status = &H28 Or Twi_status = &H30 Then
Error = 0 ' kein Fehler
Else
Error = Twi_status ' Fehler
End If

End If

'################################################# ##############################
' STOPbedingung kommt hier immer im Ablauf, egal welcher Status
Twcr = &B10010100 ' TWINT lschen, STOP senden
' nach einem STOP wird TWINT nicht mehr gesetzt,
' man darf/kann also nicht darauf warten !

'################################################# ##############################

Else
' Bus belegt, wird er wieder freigegeben
Twcr = &B10000100 ' TWINT lschen, Bus freigeben
Error = Twi_status ' Fehler
End If

End Sub


'################################################# #############################
'##################### Twi init ###############################################
'################################################# #############################

Twi_wait_int:
Do
Twi_control = Twcr And &H80
Loop Until Twi_control = &H80

Twi_status = Twsr And &HF8 ' status
' status nur zu Debugzwecken ausgeben, weil Bus sehr langsam wird !
' Print "Err " ; Hex(twi_status)
Return

Dies funktioniert bereits wunderbar, dennoch ein paar Fragen.

1. In den RN-Artikel wird der Inhalt des Status Registers eingelesen (TWSR) beziehungsweise die Bits 3-7. Mich würde interessieren, ob es irgendwo eine Aufstellung gibt, welche Bedeutung die einzelne Konstellation hat.
like: &H28 oder &H30 --> Zeichen wurde erfolgreich gesendet

Im Datenblatt des Atmega 32 habe ich leider keine Erklärung gefunden
Hat da evtl. jemand einen Tipp?

2. Bis beide Bytes bei meinem Beispiel gesendet wurden, vergehen etwa 1 Sekunde.
Mir ist bewusst, dass die Waitroutinen in der Main Schleife und der Aufruf der TWI Abfrage über den Timer (im ms-Takt) den Code ein wenig ausbremsen. Aber 1 Sekunde? Dauert es wirklich so lange bis der Slave sein Ack sendet?

3. Das TWINT Bit im Control Register (TWCR) wird ja gesetzt, wenn die Aktion "z.B. ein Byte senden" erfolgreich ausgeführt wurde. Setzt dies auch voraus, dass der Slave sein Ack gegeben hat. Ja oder???

Falls mir einer bei einen meiner Fragestellungen helfen könnte wäre ich ihm sehr dankbar.

Ich möchte hiermit ebenfalls den Autoren der RN-Artikel meinen herzlichen Dank aussprechen: Super Arbeit =D>

Grüße + fröhliche Weihnachten

hdtvfreak

mare_crisium
24.12.2008, 06:11
hdtvfreak,

zu 1.: Doch, die Liste der Zustände findet sich im Datenblatt; Du musst aber das ausführliche nehmen (" ATmega32A (352 pages, revision A, updated 6/08 )" oder "doc8155.pdf"). Z.B. auf Seite 188. Dazu gibt's auch noch sehr hilfreiche Übersichten, z.B. auf Seite 190.
zu 2.: Nee, das geht auch viel fixer! Guck' nochmal Deine SCL-Frequenzeinstellung nach (S. 182). Bei mir kommt das ACK praktisch verzögerungsfrei.
zu 3.: Das TWINT "is set by hardware when the TWI has finished its current job and expects application software response" (Seite 202), unabhängig, ob der Sklave "NACK" oder "ACK" gesagt hat.

Dir auch fröhliche Weihnacht ;-) !

mare_crisium

hdtvfreak
24.12.2008, 13:47
Hallo mare_crisium. Vielen Dank für die ausführliche Antwort.


hdtvfreak,
zu 1.: Doch, die Liste der Zustände findet sich im Datenblatt; Du musst aber das ausführliche nehmen (" ATmega32A (352 pages, revision A, updated 6/08 )" oder "doc8155.pdf"). Z.B. auf Seite 188. Dazu gibt's auch noch sehr hilfreiche Übersichten, z.B. auf Seite 190.
mare_crisium

Habs nun gefunden =P~ .


hdtvfreak,
zu 2.: Nee, das geht auch viel fixer! Guck' nochmal Deine SCL-Frequenzeinstellung nach (S. 182). Bei mir kommt das ACK praktisch verzögerungsfrei.
mare_crisium

Hm, lag wohl an den Waitroutinen in der Main Loop. Ohne diese klappt es perfekt. Die SCL Frequenz stimmt auch 100khz.
Habe gar nicht gewusst, dass Bascom bei Waitms auch keine Timer Interrupts mehr ausführt. Werde mir wohl etwas anderes überlegen müssen.


hdtvfreak,
zu 3.: Das TWINT "is set by hardware when the TWI has finished its current job and expects application software response" (Seite 202), unabhängig, ob der Sklave "NACK" oder "ACK" gesagt hat.
mare_crisium

Jetzt hab ich doch noch eine kurze Frage zum TWI Ablauf. Bisher habe ich es so verstanden:

1. Master beginnt und sendet Startsequenz.
2. Slave erkennt die Startsequenz, sendet Ack, löscht TWINT
3. Master erkennt, dass TWINT gelöscht wurde und liest dann aus dem Statusregister den aktuellen Stand aus und reagiert darauf.
--> sendet die Slave ID + setzt das R/W Bit auf Write
4. Slave löscht TWINT, Master erkennt es, liest Status aus und sendet Byte
5. Slave quittiert den Erhalt des Bytes, indem es TWINT löscht und ein Ack sendet
6. Master erkennt dies, liest Statusregister aus und schaut ob die Übertragung ok war.

So in etwa richtig?

Grüße

hdtvfreak

linux_80
24.12.2008, 14:13
Hallo,

ein Slave reagiert das erstemal mit einem ACK, nachdem seine Slave-Adresse über den Bus kam.

Der Ablauf ist auch im DB zu sehen (Grafiken), und wann welcher Status auftritt, und wie man, je nach Status, weiterverfahren kann/muss.

Der Satz bei 4. ist evtl. etwas misszuverstehen, denn nur das reine Setzen/löschen von TWINT beim Slave löst beim Master noch keine Reaktion aus. Erst beim nächsten Takt, wenn ein ACK/NACK beim Master anliegt, wird beim Master TWINT gesetzt, und die Software muss darauf reagieren.

mare_crisium
25.12.2008, 14:37
hdtvfreak,

es ist so wie linux_80 sagt: Der Sklave hat, wie früher zwischen Sklavenhaltern und Sklaven üblich ;-) , keinen Einfluss auf den Master; d.h. die TWI_INT-Flag im Master kann er nicht verändern. Er darf nur "Yes, Sir!" (=ACK) oder "No, Sir!" (=NACK) sagen. Umgekehrt kann der Master auch nicht die TWI_INT-Flag des Sklaven sehen.

Das Ablaufschema für die Betriebsart Master-Transmitter/Slave-Receiver geht etwas anders als Du's aufgestellt hast (siehe Seite 188 für den Master, Seite 191 für den Sklaven):

1. Master beginnt und sendet Startsequenz.
2. Alle am Bus angeschlossenen Sklaven gehen in "Hab-Acht"-Stellung und warten auf die Übermittlung der Sklaven-Adresse, die angesprochen werden soll. Bei keinem Sklaven wird der TWI_INT ausgelöst.
2. Beim Master löst der Abschluss der Startsequenz den TWI_INT aus. Das Zustandsbyte ist 0x08. Der Master reagiert auf darauf, indem er die Sklavenadresse mit R/W-Bit=0 sendet.
3. Wenn am Bus ein Sklave mit der gesendeten Adresse vorhanden ist, der sich im aktiven Wartezustand befindet (siehe Punkt 7), dann löst das Eintreffen der Adresse bei ihm den TWI_INT aus. Das Zustandsbyte ist 0x60 (weil das Read-Bit Null ist). Nur dieser Sklave reagiert darauf, indem er ACK sendet, alle anderen gehen weiter ihrer Beschäftigung nach. Wenn sich kein Sklave von der Adresse angesprochen fühlt, oder der angesprochene Sklave sich im passiven Wartezustand (siehe Punkt 7) befindet, dann wird die TWI-Datenleitung nicht auf Null gezogen und im Master wird das als NACK gewertet.
4. Sowohl ACK als auch NACK lösen beim Master den TWI_INT aus. Das Statusbyte ist 0x40 (Sklaven mit R/W-bit = 0 angesprochen, ACK empfangen) oder 0x38 (Sklaven mit R/W-Bit =0 angesprochen, NACK empfangen). Je nach Statusbyte reagiert der Master mit Abbruch (sendet STOP-Sequenz) oder mit dem Senden der Datenbytes.
5. Der angesprochene Sklave empfängt das erste Datenbyte. Nach Empfang des 8.Bits sendet der Sklave automatisch ACK an den Master. Danach wird beim Sklaven der TWI_INT ausgelöst, das Zustandsbyte ist 0x80. Je nachdem, ob er jetzt noch weitere Daten erwartet oder nicht, setzt oder löscht er das TWEA-Bit im Register TWCR.
6. Beim Master lösen ACK oder NACK den TWI_INT aus. Das Zustandsbyte ist 0x28 (wenn der Sklave ACK gesendet hat) oder 0x30 (wenn der Sklave NACK gesendet hat). Der Master reagiert darauf mit Senden weiterer Datenbytes (wenn noch welche zu senden übrig sind) oder mit Abbruch (sendet STOP-Sequenz, wenn keine Datenbytes mehr zu senden sind oder der Sklave durch das NACK angezeigt hat, dass er keine weiteren Datenbytes mehr gebrauchen kann).
7. Wenn der Sklave weitere Datenbytes empfängt, wiederholt sich der Ablauf wie bei 5. Wenn er eine STOP-Sequenz empfängt, dann wird bei ihm der TWI_INT ausgelöst. Das Zustandswort ist 0xA0. Je nach gewünschtem Verhalten kann er jetzt in den aktiven (empfangene Adressen werden auf Übereinstimmung mit eigener Adresse überprüft) oder passiven (empfangene Adressen werden nicht beachtet) Sklaven-Wartezustand übergehen, er kann für Rundrufe (General calls) empfänglich sein oder nicht usw. Ausserdem muss er jetzt anfangen, mit den empfangenen Daten irgendwas anzufangen.

Damit habe ich eigentlich nur ausführlich erzählt, was im Datenblatt in den Tabellen auf Seite 188 und Seite 191 dargestellt ist. Man muss die Tabellen erst zu lesen lernen und dafür wollte ich Dir ein Beispiel geben.

Das Schlechte an den vielen Zusammenhängen und Zustandsbytes ist, dass man leicht die Übersicht verliert. Das viel wichtigere Gute aber ist, dass man für jedes Zustandsbyte ein entsprechendes Dienstprogramm schreiben kann und dann eine TWI-Kommunikation erhält, die auf alles vorbereitet ist und die wie auf Schienen läuft. Es ist ein bisschen mühselig und aufwendig, aber nur einmal. Es lohnt, sich die Mühe zu machen.

Ciao

mare_crisium

hdtvfreak
25.12.2008, 17:56
Guten Abend mare_crisium

vielen Dank für diese ausführliche und detaillierte Antwort. Ich schätze deine Mühe sehr und werde auch nochmal über das Datenblatt herfallen und die entsprechenden Passagen ebenfalls wiederholen =D>

Deinem zweitem Punkt stimme ich auch voll und ganz zu. Die ganzen Zustandbytes sind zwar teilweise verwirrend, blickt man durch, so hat man aber gewonnen und kann auf jedes Problem reagieren.

Ich wünsche dir noch schöne Feiertage, super Beitrag weiter so!!

Grüße
hdtvfreak

\:D/

hdtvfreak
26.12.2008, 00:08
Ok, habe mir in Ruhe nochmal alles durchgelesen. Den Programmablauf verstehe ich nun fast komplett. Den Rest erschließe ich mir selbst.

Habe auch schon erste Testergebnisse zur Übertragungsgeschwindikeit durchgeführt. Klappt wunderbar. 12,5 kbyte in einer Sekunde sind kein Thema und es sind ja auch noch 400kbits möglich.

Vielen Dank nochmals

Grüße

hdtvfreak