Amateurfunk verbindet die Welt

3 Master mit TWI an gesprächigen Slave

Erstellt: DL6GL, 27.01.2020 , letzte Änderung

« 2 Master mit TWI an stummen Slave TOP » 4 Downloads

Nun das eigentliche Anliegen, Daten vom Slave abzuholen. Der Slave bleibt dabei immer noch unter der Fuchtel des Masters, er reagiert also nur auf eine Leseanforderung des Masters. Eine Multi-Master-Konfiguration, bei der ein oder mehrere Slaves auch frei über den Bus verfügen und sich als Master aufspielen können, ist das nicht. Um das Gerangel um die Bus-Hoheit aller Teilnehmer zu regeln, müsste man noch eine Schippe nachlegen.

Dieses Programm beherrscht beide Optionen: Nur Daten an den Slave wie in Abschnitt 2 senden als auch bei Bedarf Daten vom Slave anfordern.

Nicht ausprobiert, aber die hier vorgestellte Master-Software sollte auch mit passiven I2C-Bausteinen wie EEPROM, ADC und Port-Expander umgehen können.

Nicht verschwiegen sei, dass das in [1] beschriebene Verhalten beim Lesen von Daten aus dem Slave nicht ganz reproduziert werden konnte. Der vom Slave an den Master zu schickende Datensatz musste um das erste Byte, die Slave-Adresse, verkürzt werden, da der Master das erste Byte selbst schreibt. Einzelheiten in Abschnitt 3.2.

3.1       Das Master-Programm

Die Erweiterung des Master-Programms aus Abschnitt 2.2 beschränkt sich auf eine zusätzliche Routine TWI_MasterRead und entsprechende Funktionen in der TWI_isr.

Hier das Hauptprogramm:

Do
For bytRun = 1 To 5
'Set up user defined message buffer for test ----------------------------
'bytMsg_Buf(1) = slave address, set in TWI_MasterWrite
bytMsg_Buf(2) = bytRun
bytMsg_Buf(3) = bytRun + 1
bytMsg_Buf(4) = bytRun + 2
Call TWI_MasterWrite(4 , bytSLV_Addr) 'Send data to slave
Locate 1 , 1
LCD "Out " ; Hex(bytMsg_Buf(1)) ; " " ; bytMsg_Buf(2) ; " " ; bytMsg_Buf(3) ; " " ; bytMsg_Buf(4)

'Read data from slave ---------------------------------------------------
Call TWI_MasterRead(4 , bytSLV_Addr) 'Receive data from slave
Locate 2 , 1
LCD "In " ; Hex(bytMsg_Buf(1)) ; " " ; bytMsg_Buf(2) ; " " ; bytMsg_Buf(3) ; " " ; bytMsg_Buf(4)
Wait 3
Next bytRun
Loop

Neu gegenüber Abschnitt 2.2 ist nur der untere Teil mit dem TWI_MasterRead. Das sieht so aus:

Sub TWI_MasterRead(byVal bytMsg_Size As Byte , byVal bytSLA As Byte)
'Read a message aray bytMsg_Buf from slave, address bytSLA, via TWI_isr.
'First the slave read address is sent to slave.
'Then the slave transmits the requested data to master via TWI.
'TWI_isr puts these data into buffer bytTWI_Buf.
'bytTWI_Buf buffer data are sorted in this sub into output array bytMsg_Buf.
'Input: bytTWI_Buf Data buffer from TWI_isr
' bytMsg_Size Number of message bytes incl. slave address (bytMsg_Buf(1))
' bytSLA Slave Address
'Output: bytMsg_Buf Message array. bytMsg_Buf(1) is slave address.
' bytTWI_Success =FALSE before beeing transmitted via TWI_isr.

Call TWI_Ready 'Wait while TWI is busy
bytSLV_AddrR = bytSLA OR &B0000_0001 'slave address + Read bit 0=1
bytTWI_Buf(1) = bytSLV_AddrR 'Slave read address
bytTWI_BufLen = bytMsg_Size 'Handled in TWI_isr
bytTWI_Success = False 'To be set True in TWI_isr
'Enable TWI & TWI interrupt, set START condition, clear TWINT
TWCR = &B1010_0101 'TWINT, TWSTA, TWEN, TWIE are set

'Now TWI_isr starts runing, receive data from slave...
'When receive operation is finished, TWIE is cleared, then TWSTO is set.
'So TWI Stop is generated. If done, TWSTO is cleared as well.
Waitms 10
Call TWI_Ready 'Wait while TWI is busy
If bytTWI_Success = True Then 'Job finished, get data
'Copy data from transceiver buffer bytTWI_Buf to message buffer bytMsg_Buf
For bytMsg_Cnt = 1 To bytMsg_Size 'Loop data bytes...
bytMsg_Buf(bytMsg_Cnt) = bytTWI_Buf(bytMsg_Cnt) '...from Transmit buffer
Next bytMsg_Cnt
End If

End Sub

Gesetzt werden die Slave-Read-Adresse mit Bit 0 = 1 und die Anzahl bytTWI_BufLen der vom Slave zu sendenden Daten-Bytes. Bei sinnvolleren Programmen als dieses Testprogramm wird der Master die gewünschte Anzahl Daten und ggf. eine Auswahl von Daten, die der Slave bereitstellen kann, im Anforderungsprotokoll schicken.

Dann wird mit gesetzten Bits für START, TWEN, TWIE und wie immer TWINT die TWI_isr beauftragt, den Auftrag zu erledigen.

Mit "Call TWI_Ready" wird auf die Erledigung gewartet.

Sub TWI_Ready
'Wait until TWI is ready for next transmission
'TWI is ready (not busy) if TWIE (bit 0) and TWSTO (bit 4) are 0
'i.e. TWI has been stopped and no TWI interrupt is active
'Input: TWCR TWI control register
'Output: bytTWI_Busy

bytTWI_Busy = TWCR AND &B0001_0001 'Check TWIE & TWSTO in TWCR
'Gives 0 if TWIE & TWSTO = 0
'>0 if TWIE or TWSTO = 1
While bytTWI_Busy > 0 'Wait while TWI is busy
Wend

End Sub

Der Bus wird beobachtet. Er ist aktiv, solange TWIE oder TWSTO oder beide auf 1 gesetzt sind, aber wieder frei, wenn TWIE und TWSTO Null geworden sind, also der TWI Interrupt nicht mehr aktiv ist und ein STOP gesetzt worden ist (dann hat die Hardware TWSTO wieder auf Null gesetzt).

Wenn dann auch die TWI_isr den vollständigen Empfang eines Datenpakets vom Slave mit "bytTWI_Success = True" vermeldet, werden die im Empfangspuffer bytTWI_Buf gesammelten Bytes in den Puffer bytMsg_Buf umgespeichert. Genauso wie in Abschnitt 2.2.

In der Do…Loop des Masterprogramms wird dann bytMsg_Buf auf dem LCD angezeigt, im Aufmacherbild oben in der zweiten Zeile. Der Slave hat zu den vom Master erhaltenen Bytes jeweils eine 1 addiert, bevor er sie zurück geschickt hat.

Hier noch der Ausschnitt aus der TWI_isr, der den Datenempfang von Slave regelt.

   bytTWI_Status = TWSR And &B1111_1000           'Status, TWSR status=bits 2...0
Select Case bytTWI_Status
. . .
'Master receiver mode ---------------------------------------------------
'Master receives specified number of bytes bytTWI_BufLen.
'Last byte is returned with NACK, then STOP.
'TWINT is cleared in any case (TWINT=1) to continue operation.

'Data byte has been received, ACK has been returned (0x50)
Case &H50
Incr bytTWI_BufCnt 'Next byte to receive
bytTWI_Buf(bytTWI_BufCnt) = TWDR 'Read it from TWDR
GoTo ISR4

'SLA+R has been transmitted; ACK has been received (0x40)
Case &H40
ISR4: 'From here &H40 Or &H50
If bytTWI_BufCnt < bytTWI_BufLen Then 'Is not the last byte, next one
'Enable TWI and TWI interrupt, clear TWINT to continue
TWCR = &B1100_0101 'TWINT, TWEA, TWEN, TWIE are set
Else 'Last byte, send NACK after reception
'Enable TWI and TWI interrupt, disable TWEA (Not ACK), clear TWINT
TWCR = &B1000_0101 'TWINT, TWEN, TWIE are set
End If

'Data byte has been received, NACK has been returned (0x58)
Case &H58
Incr bytTWI_BufCnt 'Last byte to receive
bytTWI_Buf(bytTWI_BufCnt) = TWDR
bytTWI_Success = True 'Message completed
'Enable TWI, disable TWI interrupt, set STOP condition, clear TWINT
TWCR = &B1001_0100 'TWINT, TWSTO, TWIE are set

'All other cases: disable TWI interrupt, send Stop condition
Case Else
'Enable TWI, disable TWI interrupt, set STOP condition, clear TWINT
TWCR = &B1001_0100 'TWINT, TWSTO, TWEN are set

End Select


  • Die Prozedur zum Lesen von Bytes ist ähnlich wie beim Slave in Abschnitt 2.3 bis auf die jeweils anderen Status-Codes für den Master Receive-Mode.
     
  • In "Case &H50" werden unentwegt Daten-Bytes eingelesen, wenn sie mit ACK vom Slave gekommen sind.
     
  • In "Case &H40" wird geprüft, ob es das letzte erwartete Byte war. Wenn noch nicht, wird mit ACK geantwortet. Wenn es das letzte Byte ist, signalisiert der Master das Ende der Übertragung des Datenpakets mit einem NACK zum letzten Datenbyte.
     
  • "Case &H58" setzt der Master darauf mit dem letzten Byte mit NACK vom Slave ein STOP, womit dieser Vorgang abgeschlossen ist.
     
  • Zusammengefasst: Der Master liest solange Daten vom Slave rein, bis er mit dem letzten erwarteten Byte dem Slave mit einem NACK zu verstehen gibt, Schluss zu machen. Nach dessen Bestätigung von Slave beendet er die Sache. Mit einem STOP gibt es den Bus wieder frei.


3.2       Das Slave-Programm

Das Hauptprogramm ist gegenüber Abschnitt 2.3 etwas fülliger:

Do

bytTWI_DataRecv = TWI_BytesRecv() 'Bytes received
If bytTWI_DataRecv > 0 Then
'Save buffer bytTWI_Buf_RX received from Master to buffer bytMsg_Buf
Call TWI_SlaveRead
Select Case bytTWI_DataRecv
Case 1 'Only SLA+R received

Case Else 'SLA+R and data received
'Sending back data with SLA at the first position (0 in Ref. 1, 1 here)
'and data at the subsequent positions could not be reproduced here.
'Master received record is SLA+R from Master Read START condition @ 1,
'SLA+R from Slave transmit @ 2 (which is bullshit)
'and subsequent data from Slave transmit @ 3...
'So data to be transmitted by Slave are shifted one position back
'eliminating above SLA+R from Slave transmit @ 2
'Data sent back start from address 1 (not 2!)
'Change received data (add 1 for test)...
For bytMsg_Cnt = 2 To bytTWI_DataRecv
bytTmp0 = bytMsg_Cnt - 1
bytMsg_Buf(bytTmp0) = bytMsg_Buf(bytMsg_Cnt) + 1
Next bytMsg_Cnt

'This version taken from Ref. 1 does not work
'For bytMsg_Cnt = 1 To bytTWI_DataRecv
' bytMsg_Buf(bytMsg_Cnt) = bytMsg_Buf(bytMsg_Cnt) + 1
'Next bytMsg_Cnt

'... and send them back to master
'Put changed data back into TWI TX buffer
Call TWI_SlaveWrite(bytTWI_DataRecv)
End Select

End If

Loop

Die Funktion TWI_BytesRecv() stellt fest, ob Daten vom Master und wie viele empfangen wurden. Nachdem in TWI_SlaveRead ein Datenpaket vom Master aufgenommen wurde, wird geprüft, ob neben der Slave-Adresse auch noch Nutzdaten empfangen wurden. Wenn ja, werden diese zur Unterscheidung jeweils um 1 erhöht und ab Position 1 (!) zurück in den Datenpuffer bytMsg_Buf geschrieben. Dieser unschöne Symmetriebruch - Arrayposition 1 war der Slave-Adresse vorbehalten - passt nicht zur Beschreibung in [1]. Das Problem liegt allerdings in der Master -TWI_isr. Hier wird mit der Start-Bedingung zum Lesen vom Slave die Slave-Adresse schon auf Position 1 gespeichert. Eine weitere Slave-Adresse im Übertragungsprotokoll vom Slave ist dann eine zu viel. Daher sendet der Slave nur die Nutzdaten ab Arrayposition 1. Eine Möglichkeit, diese Unkonsistenz zu beseitigen, habe ich (noch) nicht gefunden.

Die Routine TWI_SlaveWrite schreibt die Daten zurück in den Puffer bytTWI_Buf, den die TWI_isr nun zurück an den Master schickt. Der Ausschnitt aus der TWI_isr zeigt, wie diese das macht:

   bytTWI_Status = TWSR And &B1111_1000           'Status, TWSR status=bits 7...3
Select Case bytTWI_Status
. . .
'Slave transmit mode ----------------------------------------------------
'Data are transmitted until master answers with a NACK

'I admit, the C language "switch" statement with or without "break" is elegant,
'so you can enter a "case" and continue with some more "case" up to the next "break"
'The BASCOM "Select case" jumps out after any "case", i.e. makes a "break".
'So, though I don't like, some GoTo's must be used to realize the "no break".

'Own address SLA + R has been transmitted, ACK returned (0xA8)
Case &HA8
bytTWI_BufCnt_TX = 0 'Initialize byte counter
GoTo ISR1 'I don't like GoTo's

'Data byte in TWDR has been transmitted, ACK returned (0xB8)
Case &HB8
ISR1:
'Data are sent until master stops with NACK
'Take care in master software not to extend buffer size!
If bytTWI_BufCnt_TX < bytTWI_BufSize Then 'Below max buffer size
Incr bytTWI_BufCnt_TX 'Increment byte counter
TWDR = bytTWI_Buf_TX(bytTWI_BufCnt_TX) 'Send 1 byte from output buffer
End If
'Enable TWI, TWI interrupt and Acknoledge, clear TWINT
TWCR = &B1100_0101 'TWINT, TWEA, TWEN, TWIE are set

'Data byte has been transmitted, NACK returned (0xC0), end of transmission
Case &HC0
'Enable TWI, TWI interrupt and Acknoledge, clear TWINT
TWCR = &B1100_0101 'TWINT, TWEA, TWEN, TWIE are set

'All other cases: disable TWI interrupt and Acknoledge, clear TWINT
Case Else
'Enable TWI, disable TWI interrupt and Acknoledge, clear TWINT
TWCR = &B1000_0100 'TWINT, TWEN are set

End Select
  • Die Funktionen des Slave Transmit Mode sind bis auf unterschiedliche Statuscodes vergleichbar mit denen des Master Transmit Mode in Abschnitt 2.2.
      
  • In "CaseB8" werden unentwegt Daten-Bytes geschrieben, solange der Master mit ACK antwortet.
      
  • Die Übertragung läuft solange, bis in "Case &HC0" das NACK vom Master registriert wird (siehe 3.1). Das wird mit ACK bestätigt.
      
  • Zusammengefasst: der Slave sendet solange Daten, bis der Master mit dem letzten erwarteten Byte ein NACK erhält. Der Master beendet daraufhin mit einem STOP die Sache. Der Bus ist wieder frei.
      
  • Das Ergebnis ist dann in der zweiten Zeile des Aufmacherbildes oben zu sehen.


Dem Autor Michael S., der volle Name wurde in [1] nicht angegeben, danke ich für die nach einigen Mühen auch für mich verständliche Adaption der Microchip/Atmel-Programme in [2] und [3]. Nun auch als C-Jünger bekehrt hat er mich nicht Ich bleibe bei BASCOM. Die C-Programmierer kochen auch nur mit Wasser, obschon elegant so in Zeichensprache verdichtet, dass aufmerksames Lesen unerlässlich ist. Für einen Compiler kein Problem, für meine in die Jahre gekommenen grauen Zellen aber schon ein wenig...

« 2 Master mit TWI an stummen Slave TOP » 4 Downloads